From 4417f0525c9bf525ff31deda525b042ce036c578 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 7 Jul 2025 09:45:08 +0900 Subject: [PATCH 01/85] isRemoteSuspend --- .../migration/1751848750315-RemoteSuspend.js | 26 +++++++++++++++++++ packages/backend/src/models/User.ts | 8 +++++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 packages/backend/migration/1751848750315-RemoteSuspend.js diff --git a/packages/backend/migration/1751848750315-RemoteSuspend.js b/packages/backend/migration/1751848750315-RemoteSuspend.js new file mode 100644 index 0000000000..d49e14dc21 --- /dev/null +++ b/packages/backend/migration/1751848750315-RemoteSuspend.js @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/** + * @typedef {import('typeorm').MigrationInterface} MigrationInterface + */ + +/** + * @class + * @implements {MigrationInterface} + */ +module.exports = class RemoteSuspend1751848750315 { + name = 'RemoteSuspend1751848750315' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" ADD "isRemoteSuspended" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isRemoteSuspended" IS 'Whether the User is suspended by the remote moderators.'`); + } + + async down(queryRunner) { + await queryRunner.query(`COMMENT ON COLUMN "user"."isRemoteSuspended" IS 'Whether the User is suspended by the remote moderators.'`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isRemoteSuspended"`); + } +} diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index a6e9edcf5f..33cd01dfe3 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -166,10 +166,16 @@ export class MiUser { @Column('boolean', { default: false, - comment: 'Whether the User is suspended.', + comment: 'Whether the User is suspended by the local moderators.', }) public isSuspended: boolean; + @Column('boolean', { + default: false, + comment: 'Whether the User is suspended by the remote moderators.', + }) + public isRemoteSuspended: boolean; + @Column('boolean', { default: false, comment: 'Whether the User is locked.', From 585ff3d262c112a77fcfce6a1df55cfe6edc4587 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 7 Jul 2025 12:35:41 +0900 Subject: [PATCH 02/85] =?UTF-8?q?isSuspended=E3=81=AE=E5=91=A8=E5=9B=B2?= =?UTF-8?q?=E3=81=ABisRemoteSuspended=E3=81=AE=E8=80=83=E6=85=AE=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/FanoutTimelineEndpointService.ts | 1 + packages/backend/src/core/RoleService.ts | 2 +- packages/backend/src/core/UserSearchService.ts | 6 ++++-- .../backend/src/core/entities/NotificationEntityService.ts | 2 +- packages/backend/src/core/entities/UserEntityService.ts | 7 ++++++- packages/backend/src/server/ServerService.ts | 1 + .../backend/src/server/api/endpoints/admin/show-user.ts | 5 +++++ .../backend/src/server/api/endpoints/admin/show-users.ts | 2 +- .../backend/src/server/api/endpoints/hashtags/users.ts | 3 ++- packages/backend/src/server/api/endpoints/users/show.ts | 1 + packages/backend/src/server/web/ClientServerService.ts | 3 +++ packages/frontend/src/pages/admin-user.vue | 3 +++ packages/misskey-js/src/autogen/types.ts | 1 + 13 files changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 97b617096a..1a9c668568 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -151,6 +151,7 @@ export class FanoutTimelineEndpointService { }; if (!ps.ignoreAuthorFromUserSuspension) { if (note.user!.isSuspended) return false; + if (note.user!.isRemoteSuspended) return false; } if (note.userId !== note.renoteUserId && noteJoined.renoteUser?.isSuspended) return false; if (note.userId !== note.replyUserId && noteJoined.replyUser?.isSuspended) return false; diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 314f7e221a..a54e105c80 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -254,7 +254,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { } // サスペンド済みユーザである case 'isSuspended': { - return user.isSuspended; + return this.userEntityService.isSuspendedEither(user); } // 鍵アカウントユーザである case 'isLocked': { diff --git a/packages/backend/src/core/UserSearchService.ts b/packages/backend/src/core/UserSearchService.ts index 4be7bd9bdb..8c243a5368 100644 --- a/packages/backend/src/core/UserSearchService.ts +++ b/packages/backend/src/core/UserSearchService.ts @@ -207,7 +207,7 @@ export class UserSearchService { } } - userQuery.andWhere('user.isSuspended = FALSE'); + userQuery.andWhere('user.isSuspended = FALSE').andWhere('user.isRemoteSuspended = FALSE'); return userQuery; } @@ -243,7 +243,8 @@ export class UserSearchService { .where('user.updatedAt IS NULL') .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) - .andWhere('user.isSuspended = FALSE'); + .andWhere('user.isSuspended = FALSE') + .andWhere('user.isRemoteSuspended = FALSE'); if (mutingQuery) { nameQuery.andWhere(`user.id NOT IN (${mutingQuery.getQuery()})`); @@ -286,6 +287,7 @@ export class UserSearchService { .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) .andWhere('user.isSuspended = FALSE') + .andWhere('user.isRemoteSuspended = FALSE') .setParameters(profQuery.getParameters()); users = users.concat(await userQuery diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index e91fb9eb51..199bb9bf25 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -290,7 +290,7 @@ export class NotificationEntityService implements OnModuleInit { if (notifier == null) return false; if (notifier.host && userMutedInstances.has(notifier.host)) return false; - if (notifier.isSuspended) return false; + if (this.userEntityService.isSuspendedEither(notifier)) return false; return true; } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index d4769d24d4..18fd88d288 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -69,6 +69,10 @@ function isRemoteUser(user: MiUser | { host: MiUser['host'] }): boolean { return !isLocalUser(user); } +function isSuspendedEither(user: MiUser): boolean { + return user.isSuspended || user.isRemoteSuspended; +} + export type UserRelation = { id: MiUser['id'] following: MiFollowing | null, @@ -163,6 +167,7 @@ export class UserEntityService implements OnModuleInit { public isLocalUser = isLocalUser; public isRemoteUser = isRemoteUser; + public isSuspendedEither = isSuspendedEither; @bindThis public async getRelation(me: MiUser['id'], target: MiUser['id']): Promise { @@ -537,7 +542,7 @@ export class UserEntityService implements OnModuleInit { bannerBlurhash: user.bannerId == null ? null : user.bannerBlurhash, isLocked: user.isLocked, isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), - isSuspended: user.isSuspended, + isSuspended: this.isSuspendedEither(user), description: profile!.description, location: profile!.location, birthday: profile!.birthday, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 23c085ee27..8356f1073c 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -215,6 +215,7 @@ export class ServerService implements OnApplicationShutdown { usernameLower: username.toLowerCase(), host: (host == null) || (host === this.config.host) ? IsNull() : host, isSuspended: false, + isRemoteSuspended: false, }, }); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 1ba6853dbe..8364a9613b 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -124,6 +124,10 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + isRemoteSuspended: { + type: 'boolean', + optional: false, nullable: false, + }, isHibernated: { type: 'boolean', optional: false, nullable: false, @@ -246,6 +250,7 @@ export default class extends Endpoint { // eslint- isModerator: isModerator, isSilenced: isSilenced, isSuspended: user.isSuspended, + isRemoteSuspended: user.isRemoteSuspended, isHibernated: user.isHibernated, lastActiveDate: user.lastActiveDate ? user.lastActiveDate.toISOString() : null, moderationNote: profile.moderationNote ?? '', diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 2b2c8c60ab..d8e4a636c4 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -61,7 +61,7 @@ export default class extends Endpoint { // eslint- const query = this.usersRepository.createQueryBuilder('user'); switch (ps.state) { - case 'available': query.where('user.isSuspended = FALSE'); break; + case 'available': query.where('user.isSuspended = FALSE').andWhere('user.isRemoteSuspended = FALSE'); break; case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break; case 'suspended': query.where('user.isSuspended = TRUE'); break; case 'admin': { diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 30f0c1b0c8..d5a1ecdaed 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -51,7 +51,8 @@ export default class extends Endpoint { // eslint- if (!safeForSql(normalizeForSearch(ps.tag))) throw new Error('Injection'); const query = this.usersRepository.createQueryBuilder('user') .where(':tag <@ user.tags', { tag: [normalizeForSearch(ps.tag)] }) - .andWhere('user.isSuspended = FALSE'); + .andWhere('user.isSuspended = FALSE') + .andWhere('user.isRemoteSuspended = FALSE'); const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 5ff3a63d6a..04dd3f7dd4 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -138,6 +138,7 @@ export default class extends Endpoint { // eslint- } : { id: In(ps.userIds), isSuspended: false, + isRemoteSuspended: false, }); // リクエストされた通りに並べ替え diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 8ca61a497d..e261d5be58 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -451,6 +451,7 @@ export class ClientServerService { usernameLower: username.toLowerCase(), host: host ?? IsNull(), isSuspended: false, + isRemoteSuspended: false, requireSigninToViewContents: false, }); @@ -510,6 +511,7 @@ export class ClientServerService { usernameLower: username.toLowerCase(), host: host ?? IsNull(), isSuspended: false, + isRemoteSuspended: false, }); vary(reply.raw, 'Accept'); @@ -559,6 +561,7 @@ export class ClientServerService { id: request.params.user, host: IsNull(), isSuspended: false, + isRemoteSuspended: false, }); if (user == null) { diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index a194b9a94f..e853829782 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only @{{ acct(user) }} Suspended + Suspended in Remote Silenced Moderator @@ -254,6 +255,7 @@ const ap = ref(null); const moderator = ref(false); const silenced = ref(false); const suspended = ref(false); +const remoteSuspended = ref(false); const isSystem = ref(false); const moderationNote = ref(''); const filesPaginator = markRaw(new Paginator('admin/drive/files', { @@ -288,6 +290,7 @@ function createFetcher() { moderator.value = info.value.isModerator; silenced.value = info.value.isSilenced; suspended.value = info.value.isSuspended; + remoteSuspended.value = info.value.isRemoteSuspended; moderationNote.value = info.value.moderationNote; isSystem.value = user.value.host == null && user.value.username.includes('.'); diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index df6a22ec41..40932afee3 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -11586,6 +11586,7 @@ export interface operations { isModerator: boolean; isSilenced: boolean; isSuspended: boolean; + isRemoteSuspended: boolean; isHibernated: boolean; lastActiveDate: string | null; moderationNote: string; From dabdaf14d2b28fb74365709aa8ad33ef4ce48a9a Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 7 Jul 2025 12:37:16 +0900 Subject: [PATCH 03/85] =?UTF-8?q?perform(One)Activity=E3=81=A7=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=82=AB=E3=83=AB=E5=87=8D=E7=B5=90=E3=81=A7=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E5=BC=BE=E3=81=84=E3=81=A6=E3=81=97=E3=81=BE=E3=81=86?= =?UTF-8?q?=E3=81=AE=E3=81=AF=E3=82=84=E3=82=81=E3=82=88=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/activitypub/ApInboxService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index e88f60b806..623fde4d63 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -141,7 +141,7 @@ export class ApInboxService { @bindThis public async performOneActivity(actor: MiRemoteUser, activity: IObject, resolver?: Resolver): Promise { - if (actor.isSuspended) return; + // ここでは凍結されているかどうかはチェックせず、各処理で判断する if (isCreate(activity)) { return await this.create(actor, activity, resolver); From 91fca71a4377e036b3cf8dda45a2e299e60adae7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 7 Jul 2025 12:55:32 +0900 Subject: [PATCH 04/85] define ap person create/update/render --- packages/backend/src/core/activitypub/ApRendererService.ts | 1 + packages/backend/src/core/activitypub/misc/contexts.ts | 1 + packages/backend/src/core/activitypub/models/ApPersonService.ts | 2 ++ packages/backend/src/core/activitypub/type.ts | 1 + 4 files changed, 5 insertions(+) diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 55521d6e3a..2ba0dfc7e9 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -577,6 +577,7 @@ export class ApRendererService { publicKey: this.renderKey(user, keypair, '#main-key'), isCat: user.isCat, attachment: attachment.length ? attachment : undefined, + suspended: user.isSuspended, }; if (user.movedToUri) { diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts index 6611e4b7f9..7b8278b2ba 100644 --- a/packages/backend/src/core/activitypub/misc/contexts.ts +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -543,6 +543,7 @@ const extension_context_definition = { Emoji: 'toot:Emoji', featured: 'toot:featured', discoverable: 'toot:discoverable', + suspended: 'toot:suspended', // schema schema: 'http://schema.org#', PropertyValue: 'schema:PropertyValue', diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index e52078ed0f..4673875e1f 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -393,6 +393,7 @@ export class ApPersonService implements OnModuleInit { makeNotesFollowersOnlyBefore: (person as any).makeNotesFollowersOnlyBefore ?? null, makeNotesHiddenBefore: (person as any).makeNotesHiddenBefore ?? null, emojis, + isRemoteSuspended: person.suspended === true, })) as MiRemoteUser; let _description: string | null = null; @@ -570,6 +571,7 @@ export class ApPersonService implements OnModuleInit { movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, + isRemoteSuspended: person.suspended === true, ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), } as Partial & Pick; diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index 72732b01df..bc6da82bef 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -195,6 +195,7 @@ export interface IActor extends IObject { }; 'vcard:bday'?: string; 'vcard:Address'?: string; + suspended?: boolean; } export const isCollection = (object: IObject): object is ICollection => From 9ce03e5c9c5fd231d9f7579f1ceda84b81d877ef Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 7 Jul 2025 13:01:17 +0900 Subject: [PATCH 05/85] =?UTF-8?q?UserSuspendService=E3=81=AB=E3=81=8A?= =?UTF-8?q?=E3=81=84=E3=81=A6deliver=E3=81=99=E3=82=8B=E3=81=AE=E3=81=AFAc?= =?UTF-8?q?coutUpdate=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/core/UserSuspendService.ts | 50 ++----------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 7920e58e36..ea23cd8ca4 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -10,11 +10,11 @@ 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 { RelationshipJobData } from '@/queue/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { AccountUpdateService } from '@/core/AccountUpdateService.js'; @Injectable() export class UserSuspendService { @@ -31,7 +31,7 @@ export class UserSuspendService { private userEntityService: UserEntityService, private queueService: QueueService, private globalEventService: GlobalEventService, - private apRendererService: ApRendererService, + private accountUpadateService: AccountUpdateService, private moderationLogService: ModerationLogService, ) { } @@ -83,28 +83,7 @@ export class UserSuspendService { }); 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); - } + this.accountUpadateService.publishToFollowers(user.id); } } @@ -113,28 +92,7 @@ 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); - } + this.accountUpadateService.publishToFollowers(user.id); } } From 412f4a988497b09df0d419c688e68545a781ec41 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 7 Jul 2025 13:06:34 +0900 Subject: [PATCH 06/85] split federation test (user-suspension.test.ts) --- .../test/user-suspension.test.ts | 561 ++++++++++++++++++ .../backend/test-federation/test/user.test.ts | 106 ---- 2 files changed, 561 insertions(+), 106 deletions(-) create mode 100644 packages/backend/test-federation/test/user-suspension.test.ts diff --git a/packages/backend/test-federation/test/user-suspension.test.ts b/packages/backend/test-federation/test/user-suspension.test.ts new file mode 100644 index 0000000000..ae7f88a6a7 --- /dev/null +++ b/packages/backend/test-federation/test/user-suspension.test.ts @@ -0,0 +1,561 @@ +import assert, { rejects, strictEqual } from 'node:assert'; +import * as Misskey from 'misskey-js'; +import { createAccount, deepStrictEqualWithExcludedFields, fetchAdmin, type LoginUser, resolveRemoteNote, resolveRemoteUser, sleep } from './utils.js'; + +const [aAdmin, bAdmin] = await Promise.all([ + fetchAdmin('a.test'), + fetchAdmin('b.test'), +]); + +describe('User Suspension', () => { + describe('Suspension', () => { + describe('Check suspend/unsuspend consistency', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + test('Bob follows Alice, and Alice gets suspended, there is no following relation, and Bob fails to follow again', async () => { + await bob.client.request('following/create', { userId: aliceInB.id }); + await sleep(); + + const followers = await alice.client.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // followed by Bob + + await aAdmin.client.request('admin/suspend-user', { userId: alice.id }); + await sleep(); + + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); // no following relation + + await rejects( + async () => await bob.client.request('following/create', { userId: aliceInB.id }), + (err: any) => { + strictEqual(err.code, 'NO_SUCH_USER'); + return true; + }, + ); + }); + + test('Alice gets unsuspended, Bob succeeds in following Alice', async () => { + await aAdmin.client.request('admin/unsuspend-user', { userId: alice.id }); + await sleep(); + + const followers = await alice.client.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // FIXME: followers are not deleted?? + + /** + * FIXME: still rejected! + * seems to can't process Undo Delete activity because it is not implemented + * related @see https://github.com/misskey-dev/misskey/issues/13273 + */ + await rejects( + async () => await bob.client.request('following/create', { userId: aliceInB.id }), + (err: any) => { + strictEqual(err.code, 'NO_SUCH_USER'); + return true; + }, + ); + + // FIXME: resolving also fails + await rejects( + async () => await resolveRemoteUser('a.test', alice.id, bob), + (err: any) => { + strictEqual(err.code, 'INTERNAL_ERROR'); + return true; + }, + ); + }); + + /** + * instead of simple unsuspension, let's tell existence by following from Alice + */ + test('Alice can follow Bob', async () => { + await alice.client.request('following/create', { userId: bobInA.id }); + await sleep(); + + const bobFollowers = await bob.client.request('users/followers', { userId: bob.id }); + strictEqual(bobFollowers.length, 1); // followed by Alice + assert(bobFollowers[0].follower != null); + const renewedaliceInB = bobFollowers[0].follower; + assert(aliceInB.username === renewedaliceInB.username); + assert(aliceInB.host === renewedaliceInB.host); + assert(aliceInB.id !== renewedaliceInB.id); // TODO: Same username and host, but their ids are different! Is it OK? + + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); // following are deleted + + // Bob tries to follow Alice + await bob.client.request('following/create', { userId: renewedaliceInB.id }); + await sleep(); + + const aliceFollowers = await alice.client.request('users/followers', { userId: alice.id }); + strictEqual(aliceFollowers.length, 1); + + // FIXME: but resolving still fails ... + await rejects( + async () => await resolveRemoteUser('a.test', alice.id, bob), + (err: any) => { + strictEqual(err.code, 'INTERNAL_ERROR'); + return true; + }, + ); + }); + }); + }); + + describe('Profile', () => { + describe('Consistency of profile', () => { + let alice: LoginUser; + let aliceWatcher: LoginUser; + let aliceWatcherInB: LoginUser; + + beforeAll(async () => { + alice = await createAccount('a.test'); + [ + aliceWatcher, + aliceWatcherInB, + ] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + }); + + test('Check consistency', async () => { + const aliceInA = await aliceWatcher.client.request('users/show', { userId: alice.id }); + const resolved = await resolveRemoteUser('a.test', aliceInA.id, aliceWatcherInB); + const aliceInB = await aliceWatcherInB.client.request('users/show', { userId: resolved.id }); + + // console.log(`a.test: ${JSON.stringify(aliceInA, null, '\t')}`); + // console.log(`b.test: ${JSON.stringify(aliceInB, null, '\t')}`); + + deepStrictEqualWithExcludedFields(aliceInA, aliceInB, [ + 'id', + 'host', + 'avatarUrl', + 'avatarBlurhash', + 'instance', + 'badgeRoles', + 'url', + 'uri', + 'createdAt', + 'lastFetchedAt', + 'publicReactions', + ]); + }); + }); + + describe('ffVisibility is federated', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + + // NOTE: follow each other + await Promise.all([ + alice.client.request('following/create', { userId: bobInA.id }), + bob.client.request('following/create', { userId: aliceInB.id }), + ]); + await sleep(); + }); + + test('Visibility set public by default', async () => { + for (const user of await Promise.all([ + alice.client.request('users/show', { userId: bobInA.id }), + bob.client.request('users/show', { userId: aliceInB.id }), + ])) { + strictEqual(user.followersVisibility, 'public'); + strictEqual(user.followingVisibility, 'public'); + } + }); + + /** FIXME: not working */ + test.skip('Setting private for followersVisibility is federated', async () => { + await Promise.all([ + alice.client.request('i/update', { followersVisibility: 'private' }), + bob.client.request('i/update', { followersVisibility: 'private' }), + ]); + await sleep(); + + for (const user of await Promise.all([ + alice.client.request('users/show', { userId: bobInA.id }), + bob.client.request('users/show', { userId: aliceInB.id }), + ])) { + strictEqual(user.followersVisibility, 'private'); + strictEqual(user.followingVisibility, 'public'); + } + }); + + test.skip('Setting private for followingVisibility is federated', async () => { + await Promise.all([ + alice.client.request('i/update', { followingVisibility: 'private' }), + bob.client.request('i/update', { followingVisibility: 'private' }), + ]); + await sleep(); + + for (const user of await Promise.all([ + alice.client.request('users/show', { userId: bobInA.id }), + bob.client.request('users/show', { userId: aliceInB.id }), + ])) { + strictEqual(user.followersVisibility, 'private'); + strictEqual(user.followingVisibility, 'private'); + } + }); + }); + + describe('isCat is federated', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + test('Not isCat for default', () => { + strictEqual(aliceInB.isCat, false); + }); + + test('Becoming a cat is sent to their followers', async () => { + await bob.client.request('following/create', { userId: aliceInB.id }); + await sleep(); + + await alice.client.request('i/update', { isCat: true }); + await sleep(); + + const res = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(res.isCat, true); + }); + }); + + describe('Pinning Notes', () => { + let alice: LoginUser, bob: LoginUser; + let aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + aliceInB = await resolveRemoteUser('a.test', alice.id, bob); + + await bob.client.request('following/create', { userId: aliceInB.id }); + }); + + test('Pinning localOnly Note is not delivered', async () => { + const note = (await alice.client.request('notes/create', { text: 'a', localOnly: true })).createdNote; + await alice.client.request('i/pin', { noteId: note.id }); + await sleep(); + + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 0); + }); + + test('Pinning followers-only Note is not delivered', async () => { + const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; + await alice.client.request('i/pin', { noteId: note.id }); + await sleep(); + + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 0); + }); + + let pinnedNote: Misskey.entities.Note; + + test('Pinning normal Note is delivered', async () => { + pinnedNote = (await alice.client.request('notes/create', { text: 'a' })).createdNote; + await alice.client.request('i/pin', { noteId: pinnedNote.id }); + await sleep(); + + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 1); + const pinnedNoteInB = await resolveRemoteNote('a.test', pinnedNote.id, bob); + strictEqual(_aliceInB.pinnedNotes[0].id, pinnedNoteInB.id); + }); + + test('Unpinning normal Note is delivered', async () => { + await alice.client.request('i/unpin', { noteId: pinnedNote.id }); + await sleep(); + + const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(_aliceInB.pinnedNoteIds.length, 0); + }); + }); + }); + + describe('Follow / Unfollow', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + describe('Follow a.test ==> b.test', () => { + beforeAll(async () => { + await alice.client.request('following/create', { userId: bobInA.id }); + + await sleep(); + }); + + test('Check consistency with `users/following` and `users/followers` endpoints', async () => { + await Promise.all([ + strictEqual( + (await alice.client.request('users/following', { userId: alice.id })) + .some(v => v.followeeId === bobInA.id), + true, + ), + strictEqual( + (await bob.client.request('users/followers', { userId: bob.id })) + .some(v => v.followerId === aliceInB.id), + true, + ), + ]); + }); + }); + + describe('Unfollow a.test ==> b.test', () => { + beforeAll(async () => { + await alice.client.request('following/delete', { userId: bobInA.id }); + + await sleep(); + }); + + test('Check consistency with `users/following` and `users/followers` endpoints', async () => { + await Promise.all([ + strictEqual( + (await alice.client.request('users/following', { userId: alice.id })) + .some(v => v.followeeId === bobInA.id), + false, + ), + strictEqual( + (await bob.client.request('users/followers', { userId: bob.id })) + .some(v => v.followerId === aliceInB.id), + false, + ), + ]); + }); + }); + }); + + describe('Follow requests', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + + await alice.client.request('i/update', { isLocked: true }); + }); + + describe('Send follow request from Bob to Alice and cancel', () => { + describe('Bob sends follow request to Alice', () => { + beforeAll(async () => { + await bob.client.request('following/create', { userId: aliceInB.id }); + await sleep(); + }); + + test('Alice should have a request', async () => { + const requests = await alice.client.request('following/requests/list', {}); + strictEqual(requests.length, 1); + strictEqual(requests[0].followee.id, alice.id); + strictEqual(requests[0].follower.id, bobInA.id); + }); + }); + + describe('Alice cancels it', () => { + beforeAll(async () => { + await bob.client.request('following/requests/cancel', { userId: aliceInB.id }); + await sleep(); + }); + + test('Alice should have no requests', async () => { + const requests = await alice.client.request('following/requests/list', {}); + strictEqual(requests.length, 0); + }); + }); + }); + + describe('Send follow request from Bob to Alice and reject', () => { + beforeAll(async () => { + await bob.client.request('following/create', { userId: aliceInB.id }); + await sleep(); + + await alice.client.request('following/requests/reject', { userId: bobInA.id }); + await sleep(); + }); + + test('Bob should have no requests', async () => { + await rejects( + async () => await bob.client.request('following/requests/cancel', { userId: aliceInB.id }), + (err: any) => { + strictEqual(err.code, 'FOLLOW_REQUEST_NOT_FOUND'); + return true; + }, + ); + }); + + test('Bob doesn\'t follow Alice', async () => { + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); + }); + }); + + describe('Send follow request from Bob to Alice and accept', () => { + beforeAll(async () => { + await bob.client.request('following/create', { userId: aliceInB.id }); + await sleep(); + + await alice.client.request('following/requests/accept', { userId: bobInA.id }); + await sleep(); + }); + + test('Bob follows Alice', async () => { + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 1); + strictEqual(following[0].followeeId, aliceInB.id); + strictEqual(following[0].followerId, bob.id); + }); + }); + }); + + describe('Deletion', () => { + describe('Check Delete consistency', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + test('Bob follows Alice, and Alice deleted themself', async () => { + await bob.client.request('following/create', { userId: aliceInB.id }); + await sleep(); + + const followers = await alice.client.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // followed by Bob + + await alice.client.request('i/delete-account', { password: alice.password }); + await sleep(); + + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 0); // no following relation + + await rejects( + async () => await bob.client.request('following/create', { userId: aliceInB.id }), + (err: any) => { + strictEqual(err.code, 'NO_SUCH_USER'); + return true; + }, + ); + }); + }); + + describe('Deletion of remote user for moderation', () => { + let alice: LoginUser, bob: LoginUser; + let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; + + beforeAll(async () => { + [alice, bob] = await Promise.all([ + createAccount('a.test'), + createAccount('b.test'), + ]); + + [bobInA, aliceInB] = await Promise.all([ + resolveRemoteUser('b.test', bob.id, alice), + resolveRemoteUser('a.test', alice.id, bob), + ]); + }); + + test('Bob follows Alice, then Alice gets deleted in B server', async () => { + await bob.client.request('following/create', { userId: aliceInB.id }); + await sleep(); + + const followers = await alice.client.request('users/followers', { userId: alice.id }); + strictEqual(followers.length, 1); // followed by Bob + + await bAdmin.client.request('admin/delete-account', { userId: aliceInB.id }); + await sleep(); + + /** + * FIXME: remote account is not deleted! + * @see https://github.com/misskey-dev/misskey/issues/14728 + */ + const deletedAlice = await bob.client.request('users/show', { userId: aliceInB.id }); + assert(deletedAlice.id, aliceInB.id); + + // TODO: why still following relation? + const following = await bob.client.request('users/following', { userId: bob.id }); + strictEqual(following.length, 1); + await rejects( + async () => await bob.client.request('following/create', { userId: aliceInB.id }), + (err: any) => { + strictEqual(err.code, 'ALREADY_FOLLOWING'); + return true; + }, + ); + }); + + test('Alice tries to follow Bob, but it is not processed', async () => { + await alice.client.request('following/create', { userId: bobInA.id }); + await sleep(); + + const following = await alice.client.request('users/following', { userId: alice.id }); + strictEqual(following.length, 0); // Not following Bob because B server doesn't return Accept + + const followers = await bob.client.request('users/followers', { userId: bob.id }); + strictEqual(followers.length, 0); // Alice's Follow is not processed + }); + }); + }); +}); diff --git a/packages/backend/test-federation/test/user.test.ts b/packages/backend/test-federation/test/user.test.ts index ebbe9ff5ba..38c1251600 100644 --- a/packages/backend/test-federation/test/user.test.ts +++ b/packages/backend/test-federation/test/user.test.ts @@ -452,110 +452,4 @@ describe('User', () => { }); }); }); - - describe('Suspension', () => { - describe('Check suspend/unsuspend consistency', () => { - let alice: LoginUser, bob: LoginUser; - let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInA, aliceInB] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - }); - - test('Bob follows Alice, and Alice gets suspended, there is no following relation, and Bob fails to follow again', async () => { - await bob.client.request('following/create', { userId: aliceInB.id }); - await sleep(); - - const followers = await alice.client.request('users/followers', { userId: alice.id }); - strictEqual(followers.length, 1); // followed by Bob - - await aAdmin.client.request('admin/suspend-user', { userId: alice.id }); - await sleep(); - - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 0); // no following relation - - await rejects( - async () => await bob.client.request('following/create', { userId: aliceInB.id }), - (err: any) => { - strictEqual(err.code, 'NO_SUCH_USER'); - return true; - }, - ); - }); - - test('Alice gets unsuspended, Bob succeeds in following Alice', async () => { - await aAdmin.client.request('admin/unsuspend-user', { userId: alice.id }); - await sleep(); - - const followers = await alice.client.request('users/followers', { userId: alice.id }); - strictEqual(followers.length, 1); // FIXME: followers are not deleted?? - - /** - * FIXME: still rejected! - * seems to can't process Undo Delete activity because it is not implemented - * related @see https://github.com/misskey-dev/misskey/issues/13273 - */ - await rejects( - async () => await bob.client.request('following/create', { userId: aliceInB.id }), - (err: any) => { - strictEqual(err.code, 'NO_SUCH_USER'); - return true; - }, - ); - - // FIXME: resolving also fails - await rejects( - async () => await resolveRemoteUser('a.test', alice.id, bob), - (err: any) => { - strictEqual(err.code, 'INTERNAL_ERROR'); - return true; - }, - ); - }); - - /** - * instead of simple unsuspension, let's tell existence by following from Alice - */ - test('Alice can follow Bob', async () => { - await alice.client.request('following/create', { userId: bobInA.id }); - await sleep(); - - const bobFollowers = await bob.client.request('users/followers', { userId: bob.id }); - strictEqual(bobFollowers.length, 1); // followed by Alice - assert(bobFollowers[0].follower != null); - const renewedaliceInB = bobFollowers[0].follower; - assert(aliceInB.username === renewedaliceInB.username); - assert(aliceInB.host === renewedaliceInB.host); - assert(aliceInB.id !== renewedaliceInB.id); // TODO: Same username and host, but their ids are different! Is it OK? - - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 0); // following are deleted - - // Bob tries to follow Alice - await bob.client.request('following/create', { userId: renewedaliceInB.id }); - await sleep(); - - const aliceFollowers = await alice.client.request('users/followers', { userId: alice.id }); - strictEqual(aliceFollowers.length, 1); - - // FIXME: but resolving still fails ... - await rejects( - async () => await resolveRemoteUser('a.test', alice.id, bob), - (err: any) => { - strictEqual(err.code, 'INTERNAL_ERROR'); - return true; - }, - ); - }); - }); - }); }); From 7a5ada1b3542544e77bd71a5d928229dd71f5b68 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 7 Jul 2025 13:18:17 +0900 Subject: [PATCH 07/85] fix generateDummyUser --- packages/backend/src/core/WebhookTestService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/core/WebhookTestService.ts b/packages/backend/src/core/WebhookTestService.ts index 9cf985b688..6b3192a58b 100644 --- a/packages/backend/src/core/WebhookTestService.ts +++ b/packages/backend/src/core/WebhookTestService.ts @@ -44,6 +44,7 @@ function generateDummyUser(override?: Partial): MiUser { avatarDecorations: [], tags: [], isSuspended: false, + isRemoteSuspended: false, isLocked: false, isBot: false, isCat: true, From e26369ed48807dba50d8f74df806f467a180d16c Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sat, 12 Jul 2025 15:00:06 +0900 Subject: [PATCH 08/85] fix: unable to horizontally scroll when pull to refresh is enabled (#16273) --- CHANGELOG.md | 1 + packages/frontend/src/components/MkPullToRefresh.vue | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14f7fc5de6..869ecff00e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Fix: 数時間後Misskeyのタブに戻った際に、タブがスロットリングされている間の更新アニメーションを延々見せ続けられる問題を修正 - Fix: 非ログイン時のハイライトノートの画像がCWの有無を考慮せず表示される問題を修正 - Fix: レンジ選択・ドロップダウンにて、操作を無効にすべきところで無効にならない問題を修正 +- Fix: Pull to refreshが有効なときに横スクロールができない問題を修正 ### Server - Enhance: sinceId/untilIdが指定可能なエンドポイントにおいて、sinceDate/untilDateも指定可能に diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue index 98247f5d0f..c792ff3488 100644 --- a/packages/frontend/src/components/MkPullToRefresh.vue +++ b/packages/frontend/src/components/MkPullToRefresh.vue @@ -69,13 +69,13 @@ function getScreenY(event: TouchEvent | MouseEvent | PointerEvent): number { function lockDownScroll() { if (scrollEl == null) return; scrollEl.style.touchAction = 'pan-x pan-down pinch-zoom'; - scrollEl.style.overscrollBehavior = 'none'; + scrollEl.style.overscrollBehavior = 'auto none'; } function unlockDownScroll() { if (scrollEl == null) return; scrollEl.style.touchAction = 'auto'; - scrollEl.style.overscrollBehavior = 'contain'; + scrollEl.style.overscrollBehavior = 'auto contain'; } function moveStartByMouse(event: MouseEvent) { From d2c4f79886b983ade0e90243632d84befdb3c0a0 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 12 Jul 2025 15:00:30 +0900 Subject: [PATCH 09/85] New Crowdin updates (#16258) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Ukrainian) * 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 (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/ca-ES.yml | 1 + locales/en-US.yml | 2 + locales/es-ES.yml | 1 + locales/ko-KR.yml | 2 + locales/th-TH.yml | 581 +++++++++++++++++++++++++++++++++++++++++----- locales/uk-UA.yml | 14 ++ locales/zh-CN.yml | 1 + locales/zh-TW.yml | 1 + 8 files changed, 539 insertions(+), 64 deletions(-) diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 9be8caf6bb..a3497bf4b2 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -2806,6 +2806,7 @@ _fileViewer: url: "URL" uploadedAt: "Pujat el" attachedNotes: "Notes amb aquest fitxer" + usage: "Ús " thisPageCanBeSeenFromTheAuthor: "Aquesta pàgina només la pot veure l'usuari que ha pujat aquest fitxer." _externalResourceInstaller: title: "Instal·lar des d'un lloc extern" diff --git a/locales/en-US.yml b/locales/en-US.yml index 6459fedda0..dfcb85402e 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2806,6 +2806,7 @@ _fileViewer: url: "URL" uploadedAt: "Uploaded at" attachedNotes: "Attached notes" + usage: "Used" thisPageCanBeSeenFromTheAuthor: "This page can only be seen by the user who uploaded this file." _externalResourceInstaller: title: "Install from external site" @@ -3182,6 +3183,7 @@ drafts: "Drafts" _drafts: select: "Select Draft" cannotCreateDraftAnymore: "The number of drafts that can be created has been exceeded." + cannotCreateDraft: "You cannot create a draft with this content." delete: "Delete Draft" deleteAreYouSure: "Delete draft?" noDrafts: "No drafts" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index fc5f6c9ef5..d4f4ee084d 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -2806,6 +2806,7 @@ _fileViewer: url: "URL" uploadedAt: "Subido el" attachedNotes: "Notas adjuntas" + usage: "Utilizado" thisPageCanBeSeenFromTheAuthor: "Esta página solo puede ser vista por el autor." _externalResourceInstaller: title: "Instalar desde sitio externo" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index e86065ebb3..f038d1b167 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -2806,6 +2806,7 @@ _fileViewer: url: "URL" uploadedAt: "업로드 날짜" attachedNotes: "첨부된 노트" + usage: "이용" thisPageCanBeSeenFromTheAuthor: "이 페이지는 파일 소유자만 열람할 수 있습니다" _externalResourceInstaller: title: "외부 사이트로부터 설치" @@ -3182,6 +3183,7 @@ drafts: "초안" _drafts: select: "초안 선택" cannotCreateDraftAnymore: "초안 작성 가능 수를 초과했습니다." + cannotCreateDraft: "이 내용으로는 초안을 작성할 수 없습니다. " delete: "초안 삭제\n" deleteAreYouSure: "초안을 삭제하시겠습니까?" noDrafts: "초안 없음\n" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 8492b8310b..2f3d70c34e 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -146,7 +146,7 @@ enterFileName: "พิมพ์ชื่อไฟล์" mute: "ปิดเสียง" unmute: "ยกเลิกการปิดเสียง" renoteMute: "ปิดเสียงรีโน้ต" -renoteUnmute: "เปิดเสียง รีโน้ต" +renoteUnmute: "เลิกปิดเสียงรีโน้ต" block: "บล็อก" unblock: "เลิกบล็อก" suspend: "ระงับ" @@ -242,8 +242,8 @@ silencedInstances: "ปิดปากเซิร์ฟเวอร์นี้ silencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปาก คั่นด้วยการขึ้นบรรทัดใหม่, บัญชีทั้งหมดของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปากเช่นกัน ทำได้เฉพาะคำขอติดตามเท่านั้น และไม่สามารถกล่าวถึงบัญชีในเซิร์ฟเวอร์นี้ได้หากไม่ได้ถูกติดตามกลับ | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก" mediaSilencedInstances: "เซิร์ฟเวอร์ที่ถูกปิดปากสื่อ" mediaSilencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปากสื่อ คั่นด้วยการขึ้นบรรทัดใหม่, ไฟล์ที่ถูกส่งจากบัญชีของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปาก แล้วจะถูกติดเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน และเอโมจิแบบกำหนดเองก็จะใช้ไม่ได้ด้วย | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก" -federationAllowedHosts: "เซิร์ฟเวอร์ที่เปิดให้บริการแบบเฟเดอเรชั่น" -federationAllowedHostsDescription: "ระบุชื่อโฮสต์ของเซิร์ฟเวอร์ที่คุณต้องการอนุญาตให้เชื่อมต่อแบบเฟเดอเรชั่น โดยต้องเว้นวรรคแต่ละบรรทัด" +federationAllowedHosts: "เซิร์ฟเวอร์ที่อนุญาตให้เชื่อมกับสหพันธ์" +federationAllowedHostsDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่อนุญาตให้เชื่อมกับสหพันธ์ โดยแยกแต่ละรายการด้วยบรรทัดใหม่" muteAndBlock: "ปิดเสียงและบล็อก" mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง" blockedUsers: "ผู้ใช้ที่ถูกบล็อก" @@ -298,9 +298,11 @@ uploadFromUrl: "อัปโหลดจาก URL" uploadFromUrlDescription: "URL ของไฟล์ที่คุณต้องการอัปโหลด" uploadFromUrlRequested: "ร้องขอการอัปโหลดแล้ว" uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์" +uploadNFiles: "อัปโหลด {n} ไฟล์" explore: "สำรวจ" messageRead: "อ่านแล้ว" noMoreHistory: "ไม่มีประวัติเพิ่มเติม" +startChat: "เริ่มแชต" nUsersRead: "อ่านโดย {n}" agreeTo: "ฉันยอมรับ {0}" agree: "ยอมรับ" @@ -325,6 +327,7 @@ dark: "มืด" lightThemes: "ธีมสว่าง" darkThemes: "ธีมมืด" syncDeviceDarkMode: "ซิงค์โหมดมืดกับการตั้งค่าอุปกรณ์ของคุณ" +switchDarkModeManuallyWhenSyncEnabledConfirm: "“{x}” เปิดอยู่ ต้องการปิดการซิงค์และสลับโหมดด้วยตนเองหรือไม่?" drive: "ไดรฟ์" fileName: "ชื่อไฟล์" selectFile: "เลือกไฟล์" @@ -365,7 +368,7 @@ reject: "ปฏิเสธ" normal: "ปกติ" instanceName: "ชื่อเซิร์ฟเวอร์" instanceDescription: "คำอธิบายแนะนำเซิร์ฟเวอร์" -maintainerName: "ผู้ดูแล" +maintainerName: "ชื่อผู้ดูแลระบบ" maintainerEmail: "อีเมลผู้ดูแลระบบ" tosUrl: "URL เงื่อนไขการให้บริการ" thisYear: "ปีนี้" @@ -423,6 +426,7 @@ antennaExcludeBots: "ยกเว้นบัญชีบอต" antennaKeywordsDescription: "คั่นด้วยเว้นวรรคสำหรับเงื่อนไข AND, หรือขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR" notifyAntenna: "แจ้งเตือนเกี่ยวกับโน้ตใหม่" withFileAntenna: "เฉพาะโน้ตที่มีไฟล์" +excludeNotesInSensitiveChannel: "ไม่รวมโน้ตจากช่องเนื้อหาละเอียดอ่อน" enableServiceworker: "เปิดใช้งานการแจ้งเตือนแบบพุชไปยังเบราว์เซอร์ของคุณ" antennaUsersDescription: "ระบุหนึ่งชื่อผู้ใช้ต่อบรรทัด" caseSensitive: "อักษรพิมพ์ใหญ่-พิมพ์เล็กความหมายต่างกัน" @@ -453,17 +457,17 @@ totpDescription: "ใช้แอปยืนยันตัวตนเพื moderator: "ผู้ควบคุม" moderation: "การกลั่นกรอง" moderationNote: "โน้ตการกลั่นกรอง" -moderationNoteDescription: "คุณสามารถใส่โน้ตส่วนตัวที่เฉพาะผู้ดูแลระบบเท่านั้นที่สามารถเข้าถึงได้" +moderationNoteDescription: "สามารถจดเมโมที่จะแบ่งปันเฉพาะระหว่างผู้ควบคุมได้" addModerationNote: "เพิ่มโน้ตการกลั่นกรอง" moderationLogs: "ปูมการควบคุมดูแล" nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} ราย" -securityKeyAndPasskey: "ความปลอดภัยและรหัสผ่าน" -securityKey: "กุญแจความปลอดภัย" +securityKeyAndPasskey: "Security key และ Passkey" +securityKey: "Security Key" lastUsed: "ใช้ล่าสุด" lastUsedAt: "ใช้งานครั้งล่าสุด: {t}" unregister: "เลิกติดตาม" passwordLessLogin: "เข้าสู่ระบบแบบไม่ใช้รหัสผ่าน" -passwordLessLoginDescription: "อนุญาตให้เข้าสู่ระบบโดยไม่ต้องใช้รหัสผ่านโดยใช้รหัสรักษาความปลอดภัยหรือรหัสผ่านเท่านั้น" +passwordLessLoginDescription: "เข้าสู่ระบบโดยไม่ใช้รหัสผ่าน โดยใช้เฉพาะ Security Key หรือ Passkey เท่านั้น" resetPassword: "รีเซ็ตรหัสผ่าน" newPasswordIs: "รหัสผ่านใหม่คือ “{password}”" reduceUiAnimation: "ลดภาพเคลื่อนไหว UI" @@ -573,8 +577,10 @@ showFixedPostForm: "แสดงแบบฟอร์มการโพสต์ showFixedPostFormInChannel: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนของไทม์ไลน์ (ช่อง)" withRepliesByDefaultForNewlyFollowed: "แสดงการตอบกลับจากผู้ใช้ที่คุณเพิ่งติดตามลงไทม์ไลน์ตามค่าเริ่มต้น" newNoteRecived: "มีโน้ตใหม่" +newNote: "โน้ตใหม่" sounds: "เสียง" sound: "เสียง" +notificationSoundSettings: "ตั้งค่าเสียงแจ้งเตือน" listen: "ฟัง" none: "ไม่มี" showInPage: "แสดงในเพจ" @@ -606,8 +612,8 @@ output: "เอาท์พุต" script: "สคริปต์" disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ" updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล" -unsetUserAvatar: "เลิกตั้งอวตาร" -unsetUserAvatarConfirm: "ต้องการเลิกตั้งอวตารใข่ไหม?" +unsetUserAvatar: "เลิกตั้งไอคอน" +unsetUserAvatarConfirm: "ต้องการเลิกตั้งไอคอนประจำตัวหรือไม่?" unsetUserBanner: "เลิกตั้งแบนเนอร์" unsetUserBannerConfirm: "ต้องการเลิกตั้งแบนเนอร์?" deleteAllFiles: "ลบไฟล์ทั้งหมด" @@ -682,13 +688,15 @@ smtpSecure: "ใช้โดยนัย SSL/TLS สำหรับการเ smtpSecureInfo: "ปิดสิ่งนี้เมื่อใช้ STARTTLS" testEmail: "ทดสอบการส่งอีเมล" wordMute: "ปิดเสียงคำ" +wordMuteDescription: "ย่อโน้ตที่มีวลีที่ระบุ สามารถดูโน้ตที่ย่อแล้วได้โดยคลิกที่โน้ตเหล่านั้น" hardWordMute: "ปิดเสียงคำแบบแข็งโป๊ก" -hardWordMuteDescription: "ซ่อนหมายเหตุที่มีวลีที่ระบุ ต่างจากการปิดเสียงคำ โน้ตต่างๆ จะถูกซ่อนไว้อย่างสมบูรณ์" +showMutedWord: "แสดงคำที่ถูกปิดเสียง" +hardWordMuteDescription: "จะซ่อนโน้ตที่มีคำที่ระบุไว้ ซึ่งไม่เหมือนการปิดเสียงคำ ในกรณีนี้โน้ตจะไม่แสดงเลย" regexpError: "เกิดข้อผิดพลาดใน regular expression" regexpErrorDescription: "เกิดข้อผิดพลาดใน regular expression บรรทัดที่ {line} ของการปิดเสียงคำ {tab} :" instanceMute: "ปิดเสียงเซิร์ฟเวอร์" userSaysSomething: "{name} พูดอะไรบางอย่าง" -userSaysSomethingAbout: "{name} พูดอะไรบางอย่างเกี่ยวกับ \"{word}\"" +userSaysSomethingAbout: "{name} พูดบางอย่างเกี่ยวกับ “{word}”" makeActive: "เปิดใช้งาน" display: "แสดงผล" copy: "คัดลอก" @@ -758,7 +766,7 @@ yes: "ใช่" no: "ไม่" driveFilesCount: "จำนวนไฟล์ไดรฟ์" driveUsage: "การใช้พื้นที่ไดรฟ์" -noCrawle: "ปฏิเสธการจัดทำดัชนีของโปรแกรมรวบรวมข้อมูล" +noCrawle: "ปฏิเสธการจัดทำดัชนีของ Crawler (โปรแกรมรวบรวมข้อมูล)" noCrawleDescription: "ขอให้เครื่องมือค้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ" lockedAccountInfo: "แม้ว่าการอนุมัติการติดตามถูกเปิดใช้งานอยู่ทุกคนก็ยังคงสามารถเห็นโน้ตของคุณได้ เว้นแต่ว่าคุณจะเปลี่ยนการเปิดเผยโน้ตของคุณเป็น “เฉพาะผู้ติดตาม”" alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนเป็นค่าเริ่มต้น" @@ -881,7 +889,7 @@ previewNoteText: "แสดงตัวอย่าง" customCss: "CSS ที่กำหนดเอง" customCssWarn: "ควรใช้การตั้งค่านี้เฉพาะต่อเมื่อคุณรู้มันใช้ทำอะไร การตั้งค่าที่ไม่เหมาะสมอาจทำให้ไคลเอ็นต์ไม่สามารถใช้งานได้อย่างถูกต้อง" global: "ทั่วโลก" -squareAvatars: "แสดงผลอวตารเป็นสี่เหลี่ยม" +squareAvatars: "แสดงไอคอนประจำตัวเป็นสี่เหลี่ยม" sent: "ส่ง" received: "ได้รับแล้ว" searchResult: "ผลการค้นหา" @@ -948,6 +956,9 @@ oneHour: "1 ชั่วโมง" oneDay: "1 วัน" oneWeek: "1 สัปดาห์" oneMonth: "หนึ่งเดือน" +threeMonths: "3 เดือน" +oneYear: "1 ปี" +threeDays: "3 วัน" reflectMayTakeTime: "อาจจำเป็นต้องใช้เวลาสักระยะหนึ่งจึงจะเห็นแสดงผลได้นะ" failedToFetchAccountInformation: "ไม่สามารถเรียกดึงข้อมูลบัญชีได้" rateLimitExceeded: "เกินขีดจำกัดอัตรา" @@ -972,6 +983,7 @@ document: "เอกสาร" numberOfPageCache: "จำนวนหน้าเพจที่แคช" numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย" logoutConfirm: "ต้องการออกจากระบบใช่ไหม?" +logoutWillClearClientData: "เมื่อออกจากระบบ ข้อมูลการตั้งค่าของไคลเอนต์จะถูกลบออกจากเบราว์เซอร์ เพื่อให้สามารถกู้คืนข้อมูลการตั้งค่าได้เมื่อกลับมาเข้าสู่ระบบอีกครั้ง โปรดเปิดใช้งานการสำรองข้อมูลการตั้งค่าอัตโนมัติ" lastActiveDate: "ใช้งานล่าสุดเมื่อ" statusbar: "แถบสถานะ" pleaseSelect: "ตัวเลือก" @@ -990,6 +1002,7 @@ failedToUpload: "การอัปโหลดล้มเหลว" cannotUploadBecauseInappropriate: "ไม่สามารถอัปโหลดไฟล์นี้ได้เนื่องจากระบบตรวจพบบางส่วนของไฟล์ว่านี้อาจจะเป็น NSFW" cannotUploadBecauseNoFreeSpace: "ไม่สามารถอัปโหลดได้เนื่องจากไม่มีพื้นที่ว่างในไดรฟ์เหลือแล้ว" cannotUploadBecauseExceedsFileSizeLimit: "ไม่สามารถอัปโหลดไฟล์นี้ได้แล้วเนื่องจากเกินขีดจำกัดของขนาดไฟล์แล้ว" +cannotUploadBecauseUnallowedFileType: "ไม่สามารถอัปโหลดได้เนื่องจากเป็นชนิดไฟล์ที่ไม่ได้รับอนุญาต" beta: "เบต้า" enableAutoSensitive: "ทำเครื่องหมายว่ามีเนื้อหาที่ละเอียดอ่อนโดยอัตโนมัติ" enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อว่ามีเนื้อหาโดยละเอียดอ่อนโดยอัตโนมัติ ผ่าน Machine Learning หากเป็นไปได้ แม้ว่าคุณจะปิดคุณสมบัตินี้ ก็อาจถูกตั้งค่าโดยอัตโนมัติ ทั้งนี้ขึ้นอยู่กับเซิร์ฟเวอร์" @@ -1009,7 +1022,7 @@ windowMaximize: "ขยายใหญ่สุด" windowMinimize: "ย่อเล็กที่สุด" windowRestore: "เลิกทำ" caption: "คำอธิบาย" -loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้" +loggedInAsBot: "เข้าสู่ระบบเป็นบอตอยู่ในขณะนี้" tools: "เครื่องมือ" cannotLoad: "ไม่สามารถโหลดได้" numberOfProfileView: "มุมมองโปรไฟล์" @@ -1058,7 +1071,7 @@ exploreOtherServers: "มองหาเซิร์ฟเวอร์อื่ letsLookAtTimeline: "มาดูไทม์ไลน์กัน" disableFederationConfirm: "ปิดใช้งานสหพันธ์เลยใช่ไหม?" disableFederationConfirmWarn: "โพสต์จะยังคงเป็นสาธารณะต่อไป เว้นแต่จะตั้งค่าเป็นอย่างอื่น" -disableFederationOk: "ปิดการใช้งาน" +disableFederationOk: "ปิดการใช้งานสหพันธ์" invitationRequiredToRegister: "เซิร์ฟเวอร์นี้เป็นแบบรับเชิญ เฉพาะผู้มีรหัสเชิญเท่านั้นถึงสามารถลงทะเบียนได้" emailNotSupported: "เซิร์ฟเวอร์นี้ไม่รองรับการส่งอีเมล" postToTheChannel: "โพสต์ลงช่อง" @@ -1088,7 +1101,7 @@ retryAllQueuesConfirmTitle: "ลองใหม่ทั้งหมดจริ retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มการโหลดเซิร์ฟเวอร์ชั่วคราวนะ" enableChartsForRemoteUser: "สร้างแผนภูมิข้อมูลผู้ใช้ระยะไกล" enableChartsForFederatedInstances: "สร้างแผนภูมิของเซิร์ฟเวอร์ระยะไกล" -enableStatsForFederatedInstances: "ดึงข้อมูลสถิติจากเซิร์ฟเวอร์ที่อยู่ห่างไกล" +enableStatsForFederatedInstances: "ดึงข้อมูลจากเซิร์ฟเวอร์ระยะไกล" showClipButtonInNoteFooter: "เพิ่ม “คลิป” ไปยังเมนูสั่งการของโน้ต" reactionsDisplaySize: "ขนาดของรีแอคชั่น" limitWidthOfReaction: "จำกัดความกว้างสูงสุดของรีแอคชั่นและแสดงให้เล็กลง" @@ -1219,13 +1232,13 @@ impressumDescription: "การติดป้ายกำกับ (Impressum) privacyPolicy: "นโยบายความเป็นส่วนตัว" privacyPolicyUrl: "URL นโยบายความเป็นส่วนตัว" tosAndPrivacyPolicy: "เงื่อนไขในการให้บริการและนโยบายความเป็นส่วนตัว" -avatarDecorations: "การตกแต่งอวตาร" +avatarDecorations: "ของตกแต่งไอคอน" attach: "แนบ" detach: "นำออก" detachAll: "เอาออกทั้งหมด" angle: "แองเกิล" flip: "พลิก" -showAvatarDecorations: "แสดงตกแต่งอวตาร" +showAvatarDecorations: "แสดงของตกแต่งไอคอน" releaseToRefresh: "ปล่อยเพื่อรีเฟรช" refreshing: "กำลังรีเฟรช..." pullDownToRefresh: "ดึงลงเพื่อรีเฟรช" @@ -1281,51 +1294,208 @@ clipNoteLimitExceeded: "ไม่สามารถเพิ่มโน้ต performance: "ประสิทธิภาพ​" modified: "แก้ไข" discard: "ละทิ้ง" -thereAreNChanges: "มีอยู่ {n} เปลี่ยนแปลง(s)" +thereAreNChanges: "มีการเปลี่ยนแปลง {n} รายการ" signinWithPasskey: "ลงชื่อเข้าใช้ด้วย Passkey" -unknownWebAuthnKey: "พาสคีย์ไม่ถูกต้องค่ะ" -passkeyVerificationFailed: "การยืนยันกุญแจดิจิทัลไม่สำเร็จค่ะ" -passkeyVerificationSucceededButPasswordlessLoginDisabled: "การยืนยันพาสคีย์สำเร็จแล้ว แต่การลงชื่อเข้าใช้แบบไม่ต้องใส่รหัสผ่านถูกปิดใช้งานแล้ว" +unknownWebAuthnKey: "เป็น Passkey ที่ยังไม่ได้ลงทะเบียน" +passkeyVerificationFailed: "การยืนยัน Passkey ล้มเหลว" +passkeyVerificationSucceededButPasswordlessLoginDisabled: "การยืนยัน Passkey สำเร็จ แต่การเข้าสู่ระบบแบบไม่ใช้รหัสผ่านถูกปิดใช้งานอยู่" messageToFollower: "ข้อความถึงผู้ติดตาม" target: "เป้า" testCaptchaWarning: "ฟังก์ชันนี้มีไว้สำหรับทดสอบ CAPTCHA เท่านั้น\nห้ามนำไปใช้ในระบบจริงโดยเด็ดขาด" prohibitedWordsForNameOfUser: "คำนี้ไม่สามารถใช้เป็นชื่อผู้ใช้ได้" -prohibitedWordsForNameOfUserDescription: "หากมีสตริงใดๆ ในรายการนี้ปรากฏอยู่ในชื่อของผู้ใช้ ชื่อนั้นจะถูกปฏิเสธ ผู้ใช้ที่มีสิทธิ์แต่ผู้ดูแลระบบนั้นจะไม่ได้รับผลกระทบใดๆจากข้อจำกัดนี้ค่ะ" +prohibitedWordsForNameOfUserDescription: "จะไม่อนุญาตให้เปลี่ยนชื่อผู้ใช้หากชื่อของผู้ใช้มีข้อความที่อยู่ในรายการนี้ แต่ผู้ใช้ที่มีสิทธิ์เป็นผู้ควบคุมจะไม่ได้รับผลกระทบจากข้อจำกัดนี้" yourNameContainsProhibitedWords: "ชื่อของคุณนั้นมีคำที่ต้องห้าม" yourNameContainsProhibitedWordsDescription: "ถ้าหากคุณต้องการใช้ชื่อนี้ กรุณาติดต่อผู้ดูแลระบบของเซิร์ฟเวอร์นะค่ะ" -federationDisabled: "เซิร์ฟเวอร์นี้ปิดการใช้งานการรวมกลุ่ม คุณไม่สามารถโต้ตอบกับผู้ใช้บนเซิร์ฟเวอร์อื่นได้" -reactAreYouSure: "คุณต้องการที่จะตอบสนองต่อ \" {emoji}\" หรือไม่?" -markAsSensitiveConfirm: "คุณต้องการทำเครื่องหมายสื่อนี้ว่าละเอียดอ่อนหรือไม่?" -unmarkAsSensitiveConfirm: "คุณต้องการลบการกำหนดความไวของสื่อนี้หรือไม่?" +thisContentsAreMarkedAsSigninRequiredByAuthor: "ผู้โพสต์ได้ตั้งค่าว่าต้องเข้าสู่ระบบจึงจะสามารถดูได้" +lockdown: "ล็อกดาวน์" +pleaseSelectAccount: "โปรดเลือกบัญชี" +availableRoles: "บทบาทที่ใช้ได้" +acknowledgeNotesAndEnable: "เปิดใช้งานหลังจากที่เข้าใจข้อควรระวังแล้ว" +federationSpecified: "เซิร์ฟเวอร์นี้ดำเนินงานในระบบกลุ่มไวท์ลิสต์ ไม่สามารถติดต่อกับเซิร์ฟเวอร์อื่นที่ไม่ได้รับอนุญาตจากผู้ดูแลระบบได้" +federationDisabled: "เซิร์ฟเวอร์นี้ปิดใช้งานสหพันธ์ ไม่สามารถติดต่อหรือแลกเปลี่ยนข้อมูลกับผู้ใช้จากเซิร์ฟเวอร์อื่นได้" +draft: "ร่าง" +confirmOnReact: "ยืนยันเมื่อทำการรีแอคชั่น" +reactAreYouSure: "ต้องการใส่รีแอคชั่นด้วย \"{emoji}\" หรือไม่?" +markAsSensitiveConfirm: "ต้องการตั้งค่าสื่อนี้ว่าเป็นเนื้อหาละเอียดอ่อนหรือไม่?" +unmarkAsSensitiveConfirm: "ต้องการยกเลิกการระบุว่าสื่อนี้มีเนื้อหาละเอียดอ่อนหรือไม่?" preferences: "การตั้งค่าสภาพแวดล้อม" +accessibility: "การช่วยการเข้าถึง" preferencesProfile: "โปรไฟล์การกำหนดค่า" +copyPreferenceId: "คัดลือก ID การตั้งค่า" +resetToDefaultValue: "คืนค่าเป็นค่าเริ่มต้น" +overrideByAccount: "เขียนทับด้วยบัญชี" +untitled: "ไม่มีชื่อ" +noName: "ไม่มีชื่อ" +skip: "ข้าม" +restore: "กู้คืน" +syncBetweenDevices: "ซิงค์ระหว่างอุปกรณ์" preferenceSyncConflictTitle: "การตั้งค่ามีอยู่บนเซิร์ฟเวอร์" -preferenceSyncConflictText: "รายการการตั้งค่าที่เปิดใช้งานการซิงโครไนซ์จะจัดเก็บค่าไว้บนเซิร์ฟเวอร์ และพบค่าที่จัดเก็บบนเซิร์ฟเวอร์สำหรับรายการการตั้งค่านี้ คุณต้องการทำอย่างไร?" +preferenceSyncConflictText: "การตั้งค่าที่เปิดใช้งานการซิงค์จะบันทึกค่าลงในเซิร์ฟเวอร์ อย่างไรก็ดี พบว่ามีค่าการตั้งค่านี้ที่เคยบันทึกไว้ในเซิร์ฟเวอร์แล้ว ต้องการดำเนินการอย่างไร?" +preferenceSyncConflictChoiceMerge: "รวมเข้าด้วยกัน" +preferenceSyncConflictChoiceServer: "เขียนทับด้วยค่าการตั้งค่าเซิร์ฟเวอร์" +preferenceSyncConflictChoiceDevice: "เขียนทับด้วยค่าการตั้งค่าอุปกรณ์" +preferenceSyncConflictChoiceCancel: "ยกเลิกการเปิดใช้งานการซิงค์" +paste: "วาง" +emojiPalette: "จานสีเอโมจิ" postForm: "แบบฟอร์มการโพสต์" +textCount: "จำนวนอักขระ" information: "เกี่ยวกับ" +chat: "แชต" +migrateOldSettings: "ย้ายข้อมูลการตั้งค่าเก่า" +migrateOldSettings_description: "โดยปกติจะทำโดยอัตโนมัติ แต่หากด้วยเหตุผลบางประการที่ไม่สามารถย้ายได้สำเร็จ สามารถสั่งย้ายด้วยตนเองได้ การตั้งค่าปัจจุบันจะถูกเขียนทับ" +compress: "บีบอัด" right: "ขวา" bottom: "ภายใต้" +top: "บน" +embed: "ฝัง" +settingsMigrating: "กำลังย้ายการตั้งค่า กรุณารอสักครู่... (สามารถย้ายด้วยตนเองภายหลังได้ที่ การตั้งค่า → อื่นๆ → ย้ายข้อมูลการตั้งค่าเก่า)" +readonly: "อ่านได้อย่างเดียว" +goToDeck: "กลับไปยังเด็ค" +federationJobs: "งานสหพันธ์" +driveAboutTip: "ในไดรฟ์จะแสดงรายการไฟล์ที่เคยอัปโหลดไว้ก่อนหน้า
\nสามารถนำมาใช้ซ้ำเมื่อแนบไฟล์ในโน้ต หรือตั้งค่าให้อัปโหลดไฟล์ล่วงหน้าเพื่อนำไปโพสต์ทีหลังได้
\nโปรดระวัง เมื่อลบไฟล์ ไฟล์นั้นจะไม่แสดงในทุกที่ที่เคยใช้ไฟล์นี้ (โน้ต, หน้าเพจ, อวตาร, แบนเนอร์ ฯลฯ)
\nสามารถสร้างโฟลเดอร์เพื่อจัดระเบียบได้" +scrollToClose: "เลื่อนเพื่อปิด" +advice: "คำแนะนำ" +realtimeMode: "โหมดเรียลไทม์" +turnItOn: "เปิดใช้งาน" +turnItOff: "ปิดใช้งาน" +emojiMute: "ปิดเสียงเอโมจิ" +emojiUnmute: "เลิกปิดเสียงเอโมจิ" +muteX: "ปิดเสียง {x}" +unmuteX: "เลิกปิดเสียง {x}" +abort: "หยุดและยกเลิก" +tip: "คำแนะนำและเคล็ดลับ" +redisplayAllTips: "แสดงคำแนะนำและเคล็ดลับทั้งหมดอีกครั้ง" +hideAllTips: "ซ่อนคำแนะนำและเคล็ดลับทั้งหมด" +defaultImageCompressionLevel: "ความละเอียดเริ่มต้นสำหรับการบีบอัดภาพ" +defaultImageCompressionLevel_description: "หากตั้งค่าต่ำ จะรักษาคุณภาพภาพได้ดีขึ้นแต่ขนาดไฟล์จะเพิ่มขึ้น
หากตั้งค่าสูง จะลดขนาดไฟล์ได้ แต่คุณภาพภาพจะลดลง" +_order: + newest: "เรียงจากใหม่ไปเก่า" + oldest: "เรียงจากเก่าไปใหม่" _chat: + noMessagesYet: "ยังไม่มีข้อความ" + newMessage: "ข้อความใหม่" + individualChat: "แชตส่วนตัว" + individualChat_description: "สามารถแชตแบบตัวต่อตัวกับผู้ใช้ที่ระบุไว้ได้" + roomChat: "ห้องแชต" + roomChat_description: "สามารถแชตแบบกลุ่มหลายคนได้\nและสามารถแชตกับผู้ใช้ที่ไม่ได้อนุญาตแชตส่วนตัวได้ หากอีกฝ่ายยอมรับ" + createRoom: "สร้างห้อง" + inviteUserToChat: "เชิญผู้ใช้และเริ่มแชตได้เลย" + yourRooms: "ห้องที่สร้างไว้" + joiningRooms: "ห้องที่เข้าร่วมอยู่" invitations: "คำเชิญ" + noInvitations: "ไม่มีคำเชิญ" + history: "ประวัติ" noHistory: "ไม่มีประวัติ" + noRooms: "ไม่มีห้อง" + inviteUser: "เชิญผู้ใช้" + sentInvitations: "คำเชิญที่ส่งไปแล้ว" + join: "เข้าร่วม" + ignore: "ไม่สนใจ" + leave: "ออกจากห้อง" members: "สมาชิก" + searchMessages: "ค้นหาข้อความ" home: "หน้าหลัก" send: "ส่ง" + newline: "ขึ้นบรรทัดใหม่" + muteThisRoom: "ปิดเสียงห้องนี้" + deleteRoom: "ลบห้อง" + chatNotAvailableForThisAccountOrServer: "แชตไม่ได้เปิดใช้งานบนเซิร์ฟเวอร์นี้ หรือบัญชีนี้" + chatIsReadOnlyForThisAccountOrServer: "แชตบนเซิร์ฟเวอร์นี้ หรือบัญชีนี้ เป็นแบบอ่านอย่างเดียว ไม่สามารถส่งข้อความใหม่ สร้างหรือเข้าร่วมห้องแชตได้" + chatNotAvailableInOtherAccount: "บัญชีคู่สนทนาไม่สามารถใช้ฟังก์ชันแชตได้" + cannotChatWithTheUser: "ไม่สามารถเริ่มแชตกับผู้ใช้นี้ได้" + cannotChatWithTheUser_description: "แชตใช้งานไม่ได้ หรือคู่สนทนายังไม่ได้เปิดแชต" + youAreNotAMemberOfThisRoomButInvited: "คุณไม่ได้เป็นผู้เข้าร่วมห้องนี้ แต่มีคำเชิญส่งมา หากต้องการเข้าร่วม กรุณายืนยันคำเชิญ" + doYouAcceptInvitation: "ต้องการยอมรับคำเชิญหรือไม่?" + chatWithThisUser: "แชตเลย" + thisUserAllowsChatOnlyFromFollowers: "ผู้ใช้นี้รับแชตเฉพาะจากผู้ติดตามเท่านั้น" + thisUserAllowsChatOnlyFromFollowing: "ผู้ใช้นี้รับแชตเฉพาะจากผู้ที่เขาติดตามเท่านั้น" + thisUserAllowsChatOnlyFromMutualFollowing: "ผู้ใช้นี้รับแชตเฉพาะจากผู้ที่ติดตามซึ่งกันและกันทั้งสองฝ่ายเท่านั้น" + thisUserNotAllowedChatAnyone: "ผู้ใช้นี้ไม่รับแชตจากใครเลย" + chatAllowedUsers: "ผู้ที่อนุญาตให้แชตด้วย" + chatAllowedUsers_note: "ไม่ว่าจะตั้งค่ายังไง คุณยังสามารถแชตกับคนที่คุณส่งข้อความไปหาได้" + _chatAllowedUsers: + everyone: "ใครก็ได้หมด" + followers: "เฉพาะผู้ติดตามเท่านั้น" + following: "เฉพาะผู้ที่ตัวเองติดตามเท่านั้น" + mutual: "เฉพาะผู้ใช้ที่ติดตามซึ่งกันและกันทั้งสองฝ่ายเท่านั้น" + none: "ไม่อนุญาตให้ใครเลย" +_emojiPalette: + palettes: "จานสี" + enableSyncBetweenDevicesForPalettes: "เปิดใช้งานการซิงค์จานสีระหว่างอุปกรณ์" + paletteForMain: "จานสีหลักที่ใช้" + paletteForReaction: "จานสีที่ใช้ในการรีแอคชั่น" _settings: + driveBanner: "สามารถจัดการและตั้งค่าไดรฟ์ ตรวจสอบการใช้งาน และตั้งค่าการอัปโหลดไฟล์ได้" + pluginBanner: "สามารถขยายความสามารถของไคลเอนต์ด้วยปลั๊กอินได้ ติดตั้ง ตั้งค่า และจัดการปลั๊กอินแต่ละตัวได้" + notificationsBanner: "สามารถตั้งค่าประเภทและขอบเขตของการแจ้งเตือนที่รับจากเซิร์ฟเวอร์ รวมถึงการแจ้งเตือนแบบพุช" + api: "API" webhook: "Webhook" + serviceConnection: "การเชื่อมต่อกับบริการ" + serviceConnectionBanner: "สามารถจัดการและตั้งค่า Access Token และ Webhook เพื่อเชื่อมต่อกับแอปหรือบริการภายนอกได้" + accountData: "ข้อมูลบัญชี" + accountDataBanner: "สามารถจัดการข้อมูลบัญชีได้โดยส่งออกหรือนำเข้าไฟล์เก็บถาวร" + muteAndBlockBanner: "สามารถตั้งค่าการซ่อนเนื้อหา และจำกัดการกระทำจากผู้ใช้เฉพาะรายได้" + accessibilityBanner: "สามารถปรับแต่งรูปลักษณ์และพฤติกรรมของไคลเอนต์เพื่อให้เหมาะกับการใช้งานของตนเองมากขึ้น" + privacyBanner: "สามารถตั้งค่าความเป็นส่วนตัวของบัญชี เช่น ขอบเขตการเผยแพร่เนื้อหา ความสามารถในการค้นหา และการอนุมัติผู้ติดตาม" + securityBanner: "สามารถตั้งค่าความปลอดภัยของบัญชี เช่น รหัสผ่าน วิธีการเข้าสู่ระบบ แอปยืนยันตัวตน Passkey เป็นต้น" preferencesBanner: "คุณสามารถกำหนดค่าพฤติกรรมโดยรวมของไคลเอนต์ได้ตามความต้องการของคุณ" + appearanceBanner: "สามารถตั้งค่ารูปลักษณ์และวิธีการแสดงผลของไคลเอนต์ตามความชอบได้" + soundsBanner: "สามารถตั้งค่าเสียงที่จะเล่นบนไคลเอนต์ได้" + timelineAndNote: "ไทม์ไลน์และโน้ต" + makeEveryTextElementsSelectable: "อนุญาตให้เลือกข้อความทั้งหมดได้" + makeEveryTextElementsSelectable_description: "หากเปิดใช้งาน อาจทำให้ความสะดวกในการใช้งานลดลงในบางสถานการณ์" + useStickyIcons: "ทำให้ไอคอนเคลื่อนตามการเลื่อน" + enableHighQualityImagePlaceholders: "แสดงภาพตัวแทนคุณภาพสูง" + uiAnimations: "ภาพเคลื่อนไหวของ UI" + showNavbarSubButtons: "แสดงปุ่มรองบนแถบนำทาง" + ifOn: "เมื่อเปิดใช้งาน" + ifOff: "เมื่อปิดใช้งาน" + enableSyncThemesBetweenDevices: "ซิงค์ธีมที่ติดตั้งระหว่างอุปกรณ์" + enablePullToRefresh: "ดึงเพื่ออัปเดต" + enablePullToRefresh_description: "สำหรับเมาส์ ให้กดปุ่มล้อกลางค้างไว้แล้วลาก" + realtimeMode_description: "เชื่อมต่อกับเซิร์ฟเวอร์และอัปเดตเนื้อหาแบบเรียลไทม์ อาจทำให้ใช้ปริมาณข้อมูลและแบตเตอรี่มากขึ้นได้" + contentsUpdateFrequency: "ความถี่ในการดึงข้อมูลเนื้อหา" + contentsUpdateFrequency_description: "ยิ่งตั้งค่าสูง เนื้อหาจะอัปเดตแบบเรียลไทม์มากขึ้น แต่ประสิทธิภาพอาจลดลง และการใช้ข้อมูลกับแบตเตอรี่จะเพิ่มมากขึ้น" + contentsUpdateFrequency_description2: "เมื่อโหมดเรียลไทม์เปิดอยู่ เนื้อหาจะอัปเดตแบบเรียลไทม์โดยไม่ขึ้นกับการตั้งค่านี้" + showUrlPreview: "แสดงตัวอย่าง URL" + showAvailableReactionsFirstInNote: "แสดงรีแอคชั่นที่ใช้ได้ไว้หน้าสุด" + _chat: + showSenderName: "แสดงชื่อผู้ส่ง" + sendOnEnter: "กด Enter เพื่อส่ง" +_preferencesProfile: + profileName: "ชื่อโปรไฟล์" + profileNameDescription: "กรุณาตั้งชื่อเพื่อระบุอุปกรณ์นี้" + profileNameDescription2: "เช่น: “คอมเครื่องหลัก”, “มือถือ” ฯลฯ" + manageProfiles: "จัดการโปรไฟล์" +_preferencesBackup: + autoBackup: "สำรองโดยอัตโนมัติ" + restoreFromBackup: "คืนค่าจากข้อมูลสำรอง" + noBackupsFoundTitle: "ไม่พบข้อมูลสำรอง" + noBackupsFoundDescription: "ไม่พบข้อมูลสำรองที่สร้างโดยอัตโนมัติ แต่หากมีข้อมูลสำรองที่บันทึกด้วยตนเอง สามารถนำเข้ามาเพื่อกู้คืนได้" + selectBackupToRestore: "กรุณาเลือกข้อมูลสำรองที่ต้องการกู้คืน" + youNeedToNameYourProfileToEnableAutoBackup: "จำเป็นต้องตั้งชื่อโปรไฟล์ก่อนจึงจะเปิดใช้งานการสำรองข้อมูลอัตโนมัติได้" + autoPreferencesBackupIsNotEnabledForThisDevice: "ยังไม่ได้เปิดใช้งานการสำรองข้อมูลอัตโนมัติบนอุปกรณ์นี้" + backupFound: "พบข้อมูลสำรองของการตั้งค่าแล้ว" _accountSettings: requireSigninToViewContents: "ต้องเข้าสู่ระบบเพื่อดูเนื้อหา" - requireSigninToViewContentsDescription1: "ต้องเข้าสู่ระบบเพื่อดูบันทึกและเนื้อหาอื่น ๆ ทั้งหมดที่คุณสร้าง คาดว่าจะมีประสิทธิผลในการป้องกันไม่ให้ข้อมูลถูกเก็บรวบรวมโดยโปรแกรมรวบรวมข้อมูล" - requireSigninToViewContentsDescription2: "นอกจากนี้ จะไม่สามารถดูจากเซิร์ฟเวอร์ที่ไม่รองรับการดูตัวอย่าง URL (OGP), การฝังในหน้าเว็บ หรือการอ้างอิงหมายเหตุได้" - requireSigninToViewContentsDescription3: "เนื้อหาที่ถูกรวมเข้ากับเซิร์ฟเวอร์ระยะไกลอาจไม่อยู่ภายใต้ข้อจำกัดเหล่านี้" + requireSigninToViewContentsDescription1: "กำหนดให้ต้องเข้าสู่ระบบก่อนจึงจะสามารถดูโน้ตหรือเนื้อหาทั้งหมดที่สร้างไว้ได้ ซึ่งช่วยป้องกันไม่ให้ข้อมูลถูกเก็บโดยบอตหรือ Crawler (โปรแกรมรวบรวมข้อมูล)" + requireSigninToViewContentsDescription2: "จะไม่สามารถแสดงผลจากเซิร์ฟเวอร์ที่ไม่รองรับการแสดงตัวอย่าง URL (OGP), การฝังในหน้าเว็บ, หรือการอ้างอิงโน้ตได้" + requireSigninToViewContentsDescription3: "เนื้อหาที่ถูกรวมผ่านสหพันธ์จากเซิร์ฟเวอร์ระยะไกลอาจไม่อยู่ภายใต้ข้อจำกัดเหล่านี้" + makeNotesFollowersOnlyBefore: "แสดงโน้ตเก่าเฉพาะกับผู้ติดตามเท่านั้น" + makeNotesFollowersOnlyBeforeDescription: "ขณะที่เปิดฟังก์ชันนี้ โน้ตที่เก่ากว่าหรือเลยเวลาที่กำหนดจะแสดงเฉพาะกับผู้ติดตามเท่านั้น หากปิดใช้งาน สถานะการเปิดเผยจะกลับไปเป็นแบบเดิม" + makeNotesHiddenBefore: "ทำให้โน้ตเก่าทั้งหมดเป็นแบบส่วนตัว" + makeNotesHiddenBeforeDescription: "ขณะที่เปิดฟังก์ชันนี้ โน้ตที่เก่ากว่าหรือเลยเวลาที่กำหนดจะแสดงเฉพาะกับตนเอง (กลายเป็นแบบส่วนตัว) หากปิดใช้งาน สถานะการเปิดเผยจะกลับไปเป็นแบบเดิม" + mayNotEffectForFederatedNotes: "โน้ตที่ถูกรวมผ่านสหพันธ์จากเซิร์ฟเวอร์ระยะไกลอาจไม่ได้รับผลจากการตั้งค่านี้" + mayNotEffectSomeSituations: "ข้อจำกัดเหล่านี้เป็นเพียงการกรองเบื้องต้น ในบางกรณี เช่น การดูจากเซิร์ฟเวอร์อื่นหรือในระหว่างการตรวจสอบโดยผู้ดูแล อาจไม่สามารถใช้งานได้" + notesHavePassedSpecifiedPeriod: "โน้ตที่เลยเวลาที่กำหนดไว้แล้ว" + notesOlderThanSpecifiedDateAndTime: "โน้ตก่อนเวลาที่กำหนดไว้" _abuseUserReport: forward: "ส่ง​ต่อ" forwardDescription: "ส่งรายงานไปยังเซิร์ฟเวอร์ระยะไกลโดยใช้บัญชีระบบที่ไม่ระบุตัวตน" resolve: "แก้ไข" accept: "ยอมรับ" reject: "ปฏิเสธ" - resolveTutorial: "ถ้าหากรายงานนี้มีเนื้อหาถูกต้อง ให้เลือก \"ยอมรับ\" เพื่อปิดเคสกรณีนี้โดยถือว่าได้รับการแก้ไขแล้ว\nถ้าหากเนื้อหาในรายงานนี้นั้นไม่ถูกต้อง ให้เลือก \"ปฏิเสธ\" เพื่อปิดเคสกรณีนี้โดยถือว่าไม่ได้รับการแก้ไข" + resolveTutorial: "ให้เลือก “ยอมรับ” หากรายงานนี้มีเนื้อหาชอบธรรม เพื่อทำเครื่องหมายว่ากรณีนี้ได้รับการแก้ไขในทางบวก\nให้เลือก “ปฏิเสธ” หากรายงานนี้มีเนื้อหาไม่สมเหตุผล เพื่อทำเครื่องหมายว่ากรณีนี้ได้รับการแก้ไขในทางลบ" _delivery: status: "สถานะการจัดส่ง" stop: "ระงับการส่ง" @@ -1335,6 +1505,7 @@ _delivery: manuallySuspended: "หยุดชั่วคราวด้วยตนเอง" goneSuspended: "เซิร์ฟเวอร์ถูกระงับเนื่องจากมีการลบเซิร์ฟเวอร์นี้" autoSuspendedForNotResponding: "เซิร์ฟเวอร์ถูกระงับเนื่องจากไม่ตอบสนอง" + softwareSuspended: "หยุดให้บริการ เนื่องจากเป็นซอฟต์แวร์ที่ถูกระงับการเผยแพร่" _bubbleGame: howToPlay: "วิธีเล่น" hold: "ถือไว้" @@ -1449,7 +1620,7 @@ _timelineDescription: _serverRules: description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ" _serverSettings: - iconUrl: "URL ไอคอน" + iconUrl: "URL ของไอคอน" appIconDescription: "ระบุไอคอนที่จะใช้เมื่อ {host} แสดงเป็นแอป" appIconUsageExample: "ตัวอย่างเช่น เมื่อถูกเพิ่มเป็น PWA หรือบุ๊กมาร์กบนหน้าจอหลักในสมาร์ทโฟน" appIconStyleRecommendation: "เนื่องจากอาจถูกครอบตัดเป็นสี่เหลี่ยมหรือวงกลม จึงแนะนำให้ใช้ภาพที่เผื่อพื้นที่รอบๆ ตัวโลโก้ไอคอนไว้" @@ -1463,7 +1634,26 @@ _serverSettings: reactionsBufferingDescription: "เมื่อเปิดใช้งานฟังก์ชันนี้ก็จะช่วยลด latency ในการสร้างปฏิกิริยา แต่อาจจะส่งผลให้ memory footprint ของ Redis เพิ่มขึ้นนะ" inquiryUrl: "URL สำหรับการติดต่อสอบถาม" inquiryUrlDescription: "ระบุ URL ของหน้าเว็บที่มีแบบฟอร์มสำหรับติดต่อผู้ดูแลเซิร์ฟเวอร์ หรือข้อมูลการติดต่อของผู้ดูแลเซิร์ฟเวอร์" - thisSettingWillAutomaticallyOffWhenModeratorsInactive: "ถ้าหากไม่มีการตรวจสอบจากผู้ดูแลระบบหรือไม่มีความเคลื่อนไหวมาเป็นระยะเวลาหนึ่ง ระบบจะทำการปิดใช้งานฟังก์ชันนี้โดยอัตโนมัติ เพื่อลดความเสี่ยงในการถูกโจมตีด้วยสแปมและอื่นๆ" + openRegistration: "เปิดให้สร้างบัญชีได้" + openRegistrationWarning: "การเปิดให้ลงทะเบียนมีความเสี่ยง แนะนำให้เปิดใช้งานเฉพาะในกรณีที่สามารถตรวจสอบเซิร์ฟเวอร์อย่างสม่ำเสมอและมีระบบรับมือกับปัญหาได้ทันท่วงที" + thisSettingWillAutomaticallyOffWhenModeratorsInactive: "หากไม่พบกิจกรรมของผู้ควบคุมในช่วงระยะเวลาหนึ่ง การตั้งค่านี้จะถูกปิดโดยอัตโนมัติเพื่อป้องกันสแปม" + deliverSuspendedSoftware: "ซอฟต์แวร์ที่หยุดการเผยแพร่" + deliverSuspendedSoftwareDescription: "เนื่องจากเหตุผลด้านช่องโหว่ เป็นต้น สามารถหยุดการแจกจ่ายโดยระบุชื่อซอฟต์แวร์ของเซิร์ฟเวอร์และช่วงของเวอร์ชันได้ ข้อมูลเวอร์ชันนี้เป็นข้อมูลที่เซิร์ฟเวอร์ให้มา จึงไม่สามารถรับประกันความน่าเชื่อถือได้ สามารถใช้การระบุช่วงเวอร์ชันแบบ semver ได้ แต่ถ้าระบุเป็น >= 2024.3.1 จะไม่รวมเวอร์ชันแบบกำหนดเอง เช่น 2024.3.1-custom.0 จึงแนะนำให้ระบุเป็น >= 2024.3.1-0 ซึ่งเป็นการระบุแบบ prerelease" + singleUserMode: "โหมดผู้ใช้คนเดียว" + singleUserMode_description: "หากมีเพียงตัวเองคนเดียวที่ใช้เซิร์ฟเวอร์นี้ การเปิดใช้งานโหมดนี้จะช่วยปรับการทำงานให้เหมาะสมที่สุด" + signToActivityPubGet: "ลงนามในคำขอ GET" + signToActivityPubGet_description: "โดยปกติควรเปิดใช้งาน แต่หากพบปัญหาเกี่ยวกับการสื่อสารในสหพันธ์ การปิดใช้งานอาจช่วยแก้ไขได้ แต่ในบางกรณี เซิร์ฟเวอร์อาจไม่สามารถสื่อสารได้เลยหากปิดใช้งานนี้" + proxyRemoteFiles: "พร็อกซีไฟล์ระยะไกล" + proxyRemoteFiles_description: "เมื่อเปิดใช้งาน จะทำหน้าที่เป็นพร็อกซีสำหรับไฟล์จากระยะไกล ช่วยในการสร้างภาพขนาดย่อและปกป้องความเป็นส่วนตัวของผู้ใช้" + allowExternalApRedirect: "อนุญาตการเปลี่ยนเส้นทางการสืบค้นผ่าน ActivityPub" + allowExternalApRedirect_description: "เมื่อเปิดใช้งาน จะอนุญาตให้เซิร์ฟเวอร์อื่นสืบค้นเนื้อหาของบุคคลที่สามผ่านเซิร์ฟเวอร์นี้ได้ แต่มีความเสี่ยงที่อาจเกิดการปลอมแปลงเนื้อหา" + userGeneratedContentsVisibilityForVisitor: "ขอบเขตการเปิดเผยเนื้อหาที่ผู้ใช้สร้างต่อบุคคลที่ไม่ได้เข้าร่วม (แขก)" + userGeneratedContentsVisibilityForVisitor_description: "ช่วยป้องกันปัญหาที่อาจเกิดขึ้นจากเนื้อหาระยะไกลที่ไม่เหมาะสม ซึ่งอาจถูกเผยแพร่ออกสู่อินเทอร์เน็ตโดยไม่ตั้งใจผ่านเซิร์ฟเวอร์ของตนเอง โดยเฉพาะในกรณีที่การดูแลควบคุมไม่ทั่วถึง" + userGeneratedContentsVisibilityForVisitor_description2: "การเปิดเผยเนื้อหาทั้งหมดในเซิร์ฟเวอร์รวมทั้งเนื้อหาที่รับมาจากระยะไกลสู่สาธารณะบนอินเทอร์เน็ตโดยไม่มีข้อจำกัดใดๆ มีความเสี่ยงโดยเฉพาะอย่างยิ่งสำหรับผู้ชมที่ไม่เข้าใจลักษณะของระบบแบบกระจาย อาจทำให้เกิดความเข้าใจผิดคิดว่าเนื้อหาที่มาจากระยะไกลนั้นเป็นเนื้อหาที่สร้างขึ้นภายในเซิร์ฟเวอร์นี้ จึงควรใช้ความระมัดระวังอย่างมาก" + _userGeneratedContentsVisibilityForVisitor: + all: "ทั้งหมดสาธารณะ" + localOnly: "เผยแพร่เป็นสาธารณะเฉพาะเนื้อหาท้องถิ่น เนื้อหาระยะไกลให้เป็นส่วนตัว" + none: "ทั้งหมดส่วนตัว" _accountMigration: moveFrom: "ย้ายจากบัญชีอื่นมาที่บัญชีนี้" moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น" @@ -1753,13 +1943,15 @@ _role: baseRole: "แม่แบบบทบาท" useBaseValue: "ใช้ตามแม่แบบบทบาท" chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด" - iconUrl: "URL ไอคอน" + iconUrl: "URL ของไอคอน" asBadge: "แสดงเป็นตรา" - descriptionOfAsBadge: "เมื่อเปิดใช้งาน ไอคอนบทบาทจะปรากฏถัดจากชื่อผู้ใช้" + descriptionOfAsBadge: "หากเปิดใช้งาน จะมีไอคอนของบทบาท แสดงถัดจากชื่อผู้ใช้" isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจากบทบาท" descriptionOfIsExplorable: "เมื่อเปิดใช้งาน ไทมไลน์บทบาทนี้และสมาชิกที่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ" displayOrder: "ลำดับการแสดงผล" descriptionOfDisplayOrder: "เลขที่สูงกว่าจะแสดงบน UI ก่อน" + preserveAssignmentOnMoveAccount: "โอนสถานะการมอบหมายไปยังบัญชีที่ย้ายไป" + preserveAssignmentOnMoveAccount_description: "เมื่อเปิดใช้งาน บัญชีที่ได้รับบทบาทนี้เมื่อถูกย้ายไปบัญชีใหม่ บทบาทนี้จะถูกถ่ายทอดไปยังบัญชีปลายทางด้วย" canEditMembersByModerator: "อนุญาตให้ผู้ควบคุมแก้ไขสมาชิก" descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ นอกเหนือจากผู้ควบคุมและผู้ดูแลระบบแล้ว จะสามารถเพิ่มถอนบทบาทนี้แก่ผู้ใช้ได้ แต่เมื่อปิดใช้ จะมีเฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถดำเนินการได้" priority: "ลำดับความสำคัญ" @@ -1779,8 +1971,9 @@ _role: canManageCustomEmojis: "จัดการเอโมจิที่กำหนดเอง" canManageAvatarDecorations: "จัดการตกแต่งอวตาร" driveCapacity: "ความจุของไดรฟ์" + maxFileSize: "ขนาดไฟล์สูงสุดที่สามารถอัปโหลดได้" alwaysMarkNsfw: "ทำเครื่องหมายไฟล์ว่าเป็น NSFW เสมอ" - canUpdateBioMedia: "อนุญาตให้ปรับปรุงไอคอนและแบนเนอร์" + canUpdateBioMedia: "อนุญาตให้เปลี่ยนไอคอนประจำตัวและแบนเนอร์" pinMax: "จํานวนสูงสุดของโน้ตที่ปักหมุดไว้" antennaMax: "จำนวนสูงสุดของเสาอากาศ" wordMuteMax: "จำนวนอักขระสูงสุดที่อนุญาตในการปิดเสียงคำ" @@ -1794,12 +1987,17 @@ _role: canHideAds: "ซ่อนโฆษณา" canSearchNotes: "การใช้การค้นหาโน้ต" canUseTranslator: "การใช้งานแปล" - avatarDecorationLimit: "จำนวนการตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้" + avatarDecorationLimit: "จำนวนของตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้" canImportAntennas: "อนุญาตให้นำเข้าเสาอากาศ" canImportBlocking: "อนุญาตให้นำเข้าการบล็อก" canImportFollowing: "อนุญาตให้นำเข้ารายการต่อไปนี้" - canImportMuting: "อนุญาตให้นำเข้าการปิดกั้น" + canImportMuting: "อนุญาตให้นำเข้าการปิดเสียง" canImportUserLists: "อนุญาตให้นำเข้ารายการ" + chatAvailability: "อนุญาตให้แชต" + uploadableFileTypes: "ประเภทไฟล์ที่สามารถอัปโหลดได้" + uploadableFileTypes_caption: "สามารถระบุ MIME type ได้ โดยใช้การขึ้นบรรทัดใหม่เพื่อแยกหลายรายการ และสามารถใช้ดอกจัน (*) เพื่อระบุแบบไวลด์การ์ดได้ (เช่น: image/*)" + uploadableFileTypes_caption2: "ไฟล์บางประเภทอาจไม่สามารถระบุชนิดได้ หากต้องการอนุญาตไฟล์ลักษณะนั้น กรุณาเพิ่ม {x} ลงในรายการที่อนุญาต" + noteDraftLimit: "จำนวนโน้ตฉบับร่างที่สามารถสร้างได้บนฝั่งเซิร์ฟเวอร์" _condition: roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ" isLocal: "ผู้ใช้ท้องถิ่น" @@ -1959,10 +2157,12 @@ _theme: install: "ติดตั้งธีม" manage: "จัดการธีม" code: "โค้ดธีม" - description: "รายละเอียด" + copyThemeCode: "คัดลอกรหัสธีม" + description: "คำอธิบาย" installed: "{name} ได้รับการติดตั้ง" installedThemes: "ธีมที่ติดตั้ง" builtinThemes: "ธีมในตัว" + instanceTheme: "ธีมของเซิร์ฟเวอร์" alreadyInstalled: "ธีมนี้ได้รับการติดตั้งแล้ว" invalid: "รูปแบบของธีมนี้ไม่ถูกต้องนะ" make: "ทำธีม" @@ -1990,7 +2190,7 @@ _theme: fg: "ข้อความ" focus: "โฟกัส" indicator: "ตัวบ่งชี้" - panel: "แผงควบคุม" + panel: "แผง" shadow: "เงา" header: "ส่วนหัว" navBg: "พื้นหลังแถบด้านข้าง" @@ -2000,7 +2200,7 @@ _theme: link: "ลิงก์" hashtag: "แฮชแท็ก" mention: "กล่าวถึง" - mentionMe: "ได้กล่าวถึง (ฉัน)" + mentionMe: "ได้กล่าวถึงคุณ" renote: "รีโน้ต" modalBg: "พื้นหลังโมดอล" divider: "ตัวแบ่ง" @@ -2024,6 +2224,7 @@ _sfx: noteMy: "โน้ตของตัวเอง" notification: "การเเจ้งเตือน" reaction: "เมื่อเลือกรีแอคชั่น" + chatMessage: "ข้อความของแชต" _soundSettings: driveFile: "ใช้เสียงจากไดรฟ์" driveFileWarn: "เลือกไฟล์ในไดรฟ์ของคุณ" @@ -2066,15 +2267,15 @@ _2fa: step3: "ป้อนโทเค็นที่แอปของคุณให้มาเพื่อเสร็จสิ้นการตั้งค่า" setupCompleted: "ตั้งค่าสำเร็จแล้ว" step4: "นับจากนี้เป็นต้นไปการพยายามเข้าสู่ระบบในอนาคตนั้น อาจจะต้องขอโทเค็นในการเข้าสู่ระบบดังกล่าว" - securityKeyNotSupported: "เบราว์เซอร์ของคุณไม่รองรับคีย์ความปลอดภัยนะ" - registerTOTPBeforeKey: "กรุณาตั้งค่าแอปยืนยันตัวตนเพื่อลงทะเบียนรหัสความปลอดภัยหรือรหัสผ่าน" - securityKeyInfo: "นอกจากนี้การตรวจสอบความถูกต้องด้วยลายนิ้วมือหรือ PIN แล้ว คุณยังสามารถตั้งค่าการตรวจสอบสิทธิ์ผ่านคีย์ความปลอดภัยของฮาร์ดแวร์ที่รองรับ FIDO2 เพื่อเพิ่มความปลอดภัยให้กับบัญชีของคุณ" - registerSecurityKey: "ลงทะเบียนรหัสความปลอดภัยหรือรหัสผ่าน" + securityKeyNotSupported: "เว็บเบราว์เซอร์ที่ใช้งานอยู่ไม่รองรับ Security Key" + registerTOTPBeforeKey: "ก่อนลงทะเบียน Security Key หรือ Passkey กรุณาตั้งค่าแอปยืนยันตัวตนก่อน" + securityKeyInfo: "ลงทะเบียนกุญแจที่มาจาก WebAuthn เช่น Security Key แบบฮาร์ดแวร์ที่รองรับ FIDO2 การยืนยันตัวตนด้วยชีวมิติหรือ PIN บนอุปกรณ์ และ Passkey" + registerSecurityKey: "ลงทะเบียน Security Key หรือ Passkey" securityKeyName: "ป้อนชื่อคีย์" - tapSecurityKey: "กรุณาทำตามเบราว์เซอร์ของคุณเพื่อลงทะเบียนรหัสความปลอดภัยหรือรหัสผ่าน" - removeKey: "ลบคีย์ความปลอดภัยออก" + tapSecurityKey: "กรุณาทำตามคำแนะนำของเบราว์เซอร์เพื่อลงทะเบียน Security Key หรือ Passkey" + removeKey: "ลบ Security Key ออก" removeKeyConfirm: "ลบข้อมูลสำรอง {name} มั้ย?" - whyTOTPOnlyRenew: "ไม่สามารถลบแอปตัวรับรองความถูกต้องได้ตราบใดที่มีการลงทะเบียนคีย์ความปลอดภัยไว้แล้ว" + whyTOTPOnlyRenew: "ไม่สามารถลบแอปตัวรับรองความถูกต้องได้ตราบใดที่ยังมีการลงทะเบียน Security Key อยู่" renewTOTP: "ตั้งค่าแอปยืนยันตัวตน" renewTOTPConfirm: "วิธีการแบบนี้จะทําให้รหัสยืนยันจากแอพก่อนหน้าของคุณหยุดทํางานเลยนะ" renewTOTPOk: "ตั้งค่าคอนฟิกใหม่" @@ -2171,6 +2372,7 @@ _permissions: "read:federation": "รับข้อมูลเกี่ยวกับสหพันธ์" "write:report-abuse": "รายงานการละเมิด" "write:chat": "เขียนหรือลบข้อความแชท" + "read:chat": "อ่านแชต" _auth: shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน" shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?" @@ -2179,8 +2381,11 @@ _auth: permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้" pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน" callback: "กำลังกลับไปที่แอปพลิเคชัน" + accepted: "การเข้าถึงได้รับอนุญาต" denied: "ปฏิเสธการเข้าใช้" + scopeUser: "กำลังดำเนินการในฐานะผู้ใช้ต่อไปนี้" pleaseLogin: "กรุณาเข้าสู่ระบบเพื่ออนุมัติแอปพลิเคชัน" + byClickingYouWillBeRedirectedToThisUrl: "หากอนุญาตการเข้าถึง ระบบจะเปลี่ยนเส้นทางไปยัง URL ด้านล่างโดยอัตโนมัติ" _antennaSources: all: "โน้ตทั้งหมด" homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม" @@ -2226,6 +2431,7 @@ _widgets: chooseList: "เลือกรายชื่อ" clicker: "คลิกเกอร์" birthdayFollowings: "วันเกิดผู้ใช้ในวันนี้" + chat: "แชต" _cw: hide: "ซ่อน" show: "โหลดเพิ่มเติม" @@ -2265,6 +2471,8 @@ _visibility: disableFederation: "การปิดใช้งานสหพันธ์" disableFederationDescription: "อย่าส่งข้อมูลไปยังเซิร์ฟเวอร์อื่น" _postForm: + quitInspiteOfThereAreUnuploadedFilesConfirm: "มีไฟล์ที่ยังไม่ได้อัปโหลด ต้องการละทิ้งและปิดฟอร์มหรือไม่?" + uploaderTip: "ไฟล์ยังไม่ได้อัปโหลด สามารถตั้งค่าต่างๆ ได้จากเมนูของไฟล์ เช่น การเปลี่ยนชื่อ การครอปรูป การใส่ลายน้ำ และการบีบอัด ไฟล์จะถูกอัปโหลดโดยอัตโนมัติเมื่อโพสต์โน้ต" replyPlaceholder: "ตอบกลับโน้ตนี้..." quotePlaceholder: "อ้างโน้ตนี้..." channelPlaceholder: "โพสต์ลงช่อง..." @@ -2285,7 +2493,7 @@ _profile: metadataDescription: "ใช้สิ่งเหล่านี้ คุณสามารถแสดงฟิลด์ข้อมูลเพิ่มเติมในโปรไฟล์ของคุณ" metadataLabel: "ป้ายชื่อ" metadataContent: "เนื้อหา" - changeAvatar: "เปลี่ยนอวาตาร์" + changeAvatar: "เปลี่ยนไอคอนประจำตัว" changeBanner: "เปลี่ยนแบนเนอร์" verifiedLinkDescription: "หากป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณ ไอคอนการยืนยันความเป็นเจ้าของจะแสดงถัดจากฟิลด์นั้น ๆ" avatarDecorationMax: "คุณสามารถเพิ่มการตกแต่งได้สูงสุด {max}" @@ -2298,7 +2506,7 @@ _exportOrImport: clips: "คลิป" followingList: "กำลังติดตาม" muteList: "ปิดเสียง" - blockingList: "บล็อค" + blockingList: "บล็อก" userLists: "รายชื่อ" excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง" excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน" @@ -2368,7 +2576,7 @@ _pages: featured: "เป็นที่นิยม" inspector: "ตัวตรวจสอบ" contents: "เนื้อหา" - content: "บล็อคหน้าเพจ" + content: "บล็อกหน้าเพจ" variables: "ตัวแปร" title: "หัวข้อ" url: "URL ของหน้า" @@ -2380,7 +2588,7 @@ _pages: fontSansSerif: "Sans Serif" eyeCatchingImageSet: "ตั้งค่าภาพขนาดย่อ" eyeCatchingImageRemove: "ลบภาพขนาดย่อ" - chooseBlock: "เพิ่มบล็อค" + chooseBlock: "เพิ่มบล็อก" enterSectionTitle: "ป้อนชื่อหัวข้อ" selectType: "เลือกชนิด" contentBlocks: "เนื้อหา" @@ -2416,6 +2624,7 @@ _notification: newNote: "โพสต์ใหม่" unreadAntennaNote: "เสาอากาศ {name}" roleAssigned: "ได้รับบทบาท" + chatRoomInvitationReceived: "ได้รับคำเชิญเข้าร่วมห้องแชต" emptyPushNotificationMessage: "อัปเดตการแจ้งเตือนแบบพุชแล้ว" achievementEarned: "รับความสำเร็จ" testNotification: "ทดสอบการแจ้งเตือน" @@ -2428,7 +2637,9 @@ _notification: followedBySomeUsers: "มีผู้ติดตาม {n} ราย" flushNotification: "ล้างประวัติการแจ้งเตือน" exportOfXCompleted: "การดำเนินการส่งออก {x} ได้เสร็จสิ้นลงแล้ว" - login: "มีคนล็อกอิน" + login: "มีการเข้าสู่ระบบ" + createToken: "สร้างโทเค็นการเข้าถึงแล้ว" + createTokenDescription: "หากไม่ทราบสาเหตุของคำเชิญ กรุณาลบโทเค็นการเข้าถึงผ่านทาง “{text}”" _types: all: "ทั้งหมด" note: "โน้ตใหม่" @@ -2442,9 +2653,11 @@ _notification: receiveFollowRequest: "ได้รับคำร้องขอติดตาม" followRequestAccepted: "อนุมัติให้ติดตามแล้ว" roleAssigned: "ให้บทบาท" + chatRoomInvitationReceived: "เชิญเข้าห้องแชต" achievementEarned: "ปลดล็อกความสำเร็จแล้ว" exportCompleted: "กระบวนการส่งออกข้อมูลได้เสร็จสิ้นสมบูรณ์แล้ว" login: "เข้าสู่ระบบ" + createToken: "สร้างโทเค็นการเข้าถึง" test: "ทดสอบระบบแจ้งเตือน" app: "การแจ้งเตือนจากแอปที่มีลิงก์" _actions: @@ -2454,6 +2667,9 @@ _notification: _deck: alwaysShowMainColumn: "แสดงคอลัมน์หลักเสมอ" columnAlign: "จัดแนวคอลัมน์" + columnGap: "ช่องห่างระว่างคอลัมน์" + deckMenuPosition: "ตำแหน่งเมนูเด็ค" + navbarPosition: "ตำแหน่งของแถบนำทาง" addColumn: "เพิ่มคอลัมน์" newNoteNotificationSettings: "ตั้งค่าการแจ้งเตือนเมื่อมีโน้ตใหม่" configureColumn: "ตั้งค่าคอลัมน์" @@ -2472,6 +2688,7 @@ _deck: useSimpleUiForNonRootPages: "แสดง UI ของ Root Page อย่างง่าย " usedAsMinWidthWhenFlexible: "ความกว้างขั้นต่ำนั้นจะถูกใช้งานสำหรับสิ่งนี้เมื่อเปิดใช้งานตัวเลือก \"ปรับความกว้างอัตโนมัติ\" หากเลือกเปิดใช้งานแล้ว" flexible: "ปรับความกว้างอัตโนมัติ" + enableSyncBetweenDevicesForProfiles: "เปิดใช้งานการซิงค์ข้อมูลโปรไฟล์ระหว่างอุปกรณ์" _columns: main: "หลัก" widgets: "วิดเจ็ต" @@ -2483,6 +2700,7 @@ _deck: mentions: "กล่าวถึงคุณ" direct: "ไดเร็กต์" roleTimeline: "บทบาทไทม์ไลน์" + chat: "แชต" _dialog: charactersExceeded: "คุณกำลังมีตัวอักขระเกินขีดจำกัดสูงสุดแล้วนะ! ปัจจุบันอยู่ที่ {current} จาก {max}" charactersBelow: "คุณกำลังใช้อักขระต่ำกว่าขีดจำกัดขั้นต่ำเลยนะ! ปัจจุบันอยู่ที่ {current} จาก {min}" @@ -2511,8 +2729,8 @@ _webhookSettings: abuseReport: "เมื่อมีการรายงานจากผู้ใช้" abuseReportResolved: "เมื่อมีการจัดการกับการรายงานจากผู้ใช้" userCreated: "เมื่อผู้ใช้ถูกสร้างขึ้น" - inactiveModeratorsWarning: "เมื่อผู้ดูแลระบบไม่ได้ใช้งานมานานระยะหนึ่ง" - inactiveModeratorsInvitationOnlyChanged: "เมื่อผู้ดูแลระบบที่ไม่ได้ใช้งานมานาน และเซิร์ฟเวอร์เปลี่ยนเป็นแบบเชิญเข้าร่วมเท่านั้น" + inactiveModeratorsWarning: "เมื่อผู้ควบคุมไม่มีความเคลื่อนไหวในช่วงระยะเวลาหนึ่ง" + inactiveModeratorsInvitationOnlyChanged: "เมื่อผู้ควบคุมไม่มีความเคลื่อนไหวในช่วงระยะเวลาหนึ่ง ระบบจะเปลี่ยนเป็นแบบใช้คำเชิญโดยอัตโนมัติ" deleteConfirm: "ต้องการลบ Webhook ใช่ไหม?" testRemarks: "คลิกปุ่มทางด้านขวาของสวิตช์เพื่อส่ง Webhook ทดสอบที่มีข้อมูลจำลอง" _abuseReport: @@ -2564,10 +2782,10 @@ _moderationLogTypes: createAd: "สร้างโฆษณาแล้ว" deleteAd: "ลบโฆษณาออกแล้ว" updateAd: "อัปเดตโฆษณาแล้ว" - createAvatarDecoration: "สร้างการตกแต่งไอคอนแล้ว" - updateAvatarDecoration: "อัปเดตการตกแต่งไอคอนแล้ว" - deleteAvatarDecoration: "ลบการตกแต่งไอคอนแล้ว" - unsetUserAvatar: "ลบไอคอนผู้ใช้" + createAvatarDecoration: "สร้างของตกแต่งไอคอนแล้ว" + updateAvatarDecoration: "อัปเดตของตกแต่งไอคอนแล้ว" + deleteAvatarDecoration: "ลบของตกแต่งไอคอนแล้ว" + unsetUserAvatar: "เลิกตั้งไอคอนประจำตัวแล้ว" unsetUserBanner: "ลบแบนเนอร์ผู้ใช้" createSystemWebhook: "สร้าง SystemWebhook" updateSystemWebhook: "อัปเดต SystemWebhook" @@ -2579,6 +2797,8 @@ _moderationLogTypes: deletePage: "เพจถูกลบออกไปแล้ว" deleteFlash: "Play ถูกลบออกไปแล้ว" deleteGalleryPost: "โพสต์แกลเลอรี่ถูกลบออกแล้ว" + deleteChatRoom: "ลบห้องแชต" + updateProxyAccountDescription: "อัปเดตคำอธิบายของบัญชีพร็อกซี" _fileViewer: title: "รายละเอียดไฟล์" type: "ประเภทไฟล์" @@ -2586,6 +2806,7 @@ _fileViewer: url: "URL" uploadedAt: "วันที่เข้าร่วม" attachedNotes: "โน้ตที่แนบมาด้วย" + usage: "ใช้แล้ว" thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปรากฏได้โดยผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้น" _externalResourceInstaller: title: "ติดตั้งจากไซต์ภายนอก" @@ -2631,8 +2852,14 @@ _dataSaver: title: "โหลดสื่อ" description: "กันไม่ให้ภาพและวิดีโอโหลดโดยอัตโนมัติ แตะรูปภาพ/วิดีโอที่ซ่อนอยู่เพื่อโหลด" _avatar: - title: "รูปไอคอน" - description: "ระงับการเคลื่อนไหวของภาพไอคอน ภาพเคลื่อนไหวอาจมีขนาดไฟล์ใหญ่กว่าภาพปกติ ดังนั้นจึงสามารถช่วยในการลดการใช้ข้อมูล" + title: "ปิดใช้งานภาพเคลื่อนไหวของไอคอนประจำตัว" + description: "ภาพเคลื่อนไหวของไอคอนประจำตัวจะหยุดทำงาน ภาพแบบเคลื่อนไหวมักมีขนาดไฟล์ใหญ่กว่าภาพปกติ จึงช่วยลดปริมาณการใช้ข้อมูลได้มากขึ้น" + _urlPreviewThumbnail: + title: "ซ่อนภาพขนาดย่อของการแสดงตัวอย่าง URL" + description: "ภาพขนาดย่อของการตัวอย่าง URL จะไม่ถูกโหลดอีกต่อไป" + _disableUrlPreview: + title: "ปิดการใช้งานแสดงตัวอย่าง URL" + description: "ปิดฟังก์ชันแสดงตัวอย่าง URL แตกต่างจากการซ่อนเพียงภาพขนาดย่อ ฟังก์ชันนี้จะช่วยลดการโหลดข้อมูลจากลิงก์ปลายทางทั้งหมด" _code: title: "ไฮไลต์โค้ด" description: "หากใช้สัญลักษณ์ไฮไลต์โค้ดใน MFM ฯลฯ สัญลักษณ์เหล่านั้นจะไม่โหลดจนกว่าจะแตะ การไฮไลต์ไวยากรณ์(syntax)จำเป็นต้องดาวน์โหลดไฟล์คำจำกัดความของไฮไลต์สำหรับแต่ละภาษา ดังนั้นการปิดใช้งานการโหลดไฟล์เหล่านี้โดยอัตโนมัติจึงคาดว่าจะช่วยลดปริมาณข้อมูลการสื่อสารได้" @@ -2683,13 +2910,15 @@ _reversi: allowIrregularRules: "อนุญาตกฎที่ไม่ปรกติ (โหมดฟรีทุกอย่าง)" disallowIrregularRules: "ไม่อนุญาตกฎที่ไม่ปรกติ" showBoardLabels: "แสดงหมายเลขแถว/คอลัมน์บนกระดาน" - useAvatarAsStone: "ใช้รูปอวตารเป็นหมาก" + useAvatarAsStone: "ใช้ไอคอนประจำตัวเป็นหมาก" _offlineScreen: title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้" header: "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้" _urlPreviewSetting: title: "การตั้งค่าการแสดงตัวอย่าง URL" enable: "เปิดใช้งานการแสดงตัวอย่าง URL" + allowRedirect: "อนุญาตการเปลี่ยนเส้นทางไปยังปลายทางของการแสดงตัวอย่าง" + allowRedirectDescription: "ตั้งค่าว่าจะติดตามลิงก์ที่เปลี่ยนเส้นทาง (redirect) เพื่อแสดงตัวอย่างหรือไม่ เมื่อมีการป้อน URL ที่มีการเปลี่ยนเส้นทาง หากปิดการใช้งาน จะช่วยประหยัดทรัพยากรของเซิร์ฟเวอร์ แต่จะไม่สามารถแสดงเนื้อหาจากปลายทางที่เปลี่ยนเส้นทางได้" timeout: "เวลาจำกัดในการโหลดตัวอย่าง URL (ms)" timeoutDescription: "หากเวลาที่ใช้ในการโหลดเกินค่านี้ จะไม่มีการสร้างการแสดงตัวอย่าง" maximumContentLength: "ค่าสูงสุดของ Content-Length (byte)" @@ -2710,6 +2939,62 @@ _contextMenu: app: "แอปพลิเคชัน" appWithShift: "แอปฟลิเคชันด้วยปุ่มยกแคร่ (Shift)" native: "UI ของเบราว์เซอร์" +_gridComponent: + _error: + requiredValue: "ค่านี้จำเป็นต้องกรอก" + columnTypeNotSupport: "การตรวจสอบค่าด้วย regex รองรับเฉพาะคอลัมน์ที่เป็น type:text" + patternNotMatch: "ค่านี้ไม่ตรงกับรูปแบบ {pattern}" + notUnique: "ค่านี้ต้องไม่ซ้ำกับค่าที่มีอยู่" +_roleSelectDialog: + notSelected: "ยังไม่มีการเลือก" +_customEmojisManager: + _gridCommon: + copySelectionRows: "คัดลอกแถวที่เลือกไว้" + copySelectionRanges: "คัดลือกที่เลือกไว้" + deleteSelectionRows: "ลบแถวที่เลือกไว้" + deleteSelectionRanges: "ล้างค่าช่วงที่เลือก" + searchSettings: "ตั้งค่าการค้นหา" + searchSettingCaption: "ตั้งค่าเงื่อนไขการค้นหาอย่างละเอียด" + searchLimit: "จำนวนรายการที่แสดง" + sortOrder: "ลำดับการเรียง" + registrationLogs: "ปูมการลงทะเบียน" + registrationLogsCaption: "จะแสดงปูมเมื่อมีการอัปเดตหรือลบเอโมจิ หากดำเนินการอัปเดต/ลบ หรือเปลี่ยนหน้า/รีโหลด หน้านี้ ปูมจะหายไป" + alertEmojisRegisterFailedDescription: "การอัปเดตหรือลบเอโมจิล้มเหลว กรุณาตรวจสอบรายละเอียดในปูมการลงทะเบียน" + _logs: + showSuccessLogSwitch: "แสดงปูมที่สำเร็จ" + failureLogNothing: "ไม่มีปูมความล้มเหลว" + logNothing: "ไม่มีปูม" + _remote: + selectionRowDetail: "รายละเอียดของแถวที่เลือก" + importSelectionRows: "นำเข้าแถวที่เลือก" + importSelectionRangesRows: "นำเข้าแถวในช่วงที่เลือก" + importEmojisButton: "นำเข้าเอโมจิที่ทำเครื่องหมายไว้" + confirmImportEmojisTitle: "นำเข้าเอโมจิ" + confirmImportEmojisDescription: "จะนำเข้าเอโมจิ {count} รายการที่ได้รับจากระยะไกล ทั้งนี้โปรดระมัดระวังเรื่องสิทธิ์การใช้งานเอโมจิ ดำเนินการหรือไม่?" + _local: + tabTitleList: "รายการเอโมจิที่ลงทะเบียนไว้แล้ว" + tabTitleRegister: "ลงทะเบียนเอโมจิ" + _list: + emojisNothing: "ยังไม่มีเอโมจิที่ลงทะเบียนไว้" + markAsDeleteTargetRows: "กำหนดแถวที่เลือกให้เป็นรายการสำหรับลบ" + markAsDeleteTargetRanges: "กำหนดช่วงแถวที่เลือกให้เป็นรายการสำหรับลบ" + alertUpdateEmojisNothingDescription: "ไม่มีการเปลี่ยนแปลงเอโมจิ" + alertDeleteEmojisNothingDescription: "ไม่มีเอโมจิที่อยู่ในรายการสำหรับลบ" + confirmMovePage: "ต้องการเปลี่ยนหน้าหรือไม่?" + confirmChangeView: "ต้องการเปลี่ยนการแสดงผลหรือไม่?" + confirmUpdateEmojisDescription: "จะอัปเดตเอโมจิ {count} รายการ ดำเนินการหรือไม่?" + confirmDeleteEmojisDescription: "จะลบเอโมจิที่ถูกทำเครื่องหมายไว้ {count} รายการ ดำเนินการหรือไม่?" + confirmResetDescription: "การเปลี่ยนแปลงทั้งหมดที่ทำมาจะถูกรีเซ็ต" + confirmMovePageDesciption: "มีการเปลี่ยนแปลงเอโมจิในหน้านี้ หากเปลี่ยนหน้าโดยไม่บันทึก การเปลี่ยนแปลงทั้งหมดจะถูกละทิ้ง" + dialogSelectRoleTitle: "ค้นหาบทบาทที่ตั้งค่าไว้ด้วยเอโมจิ" + _register: + uploadSettingTitle: "ตั้งค่าการอัปโหลด" + uploadSettingDescription: "สามารถกำหนดพฤติกรรมขณะอัปโหลดเอโมจิจากหน้าจอนี้ได้" + directoryToCategoryLabel: "ป้อนชื่อไดเรกทอรีเป็น \"category\"" + directoryToCategoryCaption: "เมื่อทำการลากและวางไดเรกทอรี ชื่อจะถูกป้อนเป็น \"category\"" + confirmRegisterEmojisDescription: "จะลงทะเบียนเอโมจิที่แสดงในรายการเป็นเอโมจิแบบกำหนดเองใหม่\nดำเนินการต่อหรือไม่? (เพื่อหลีกเลี่ยงภาระโหลดหนัก ระบบจะสามารถลงทะเบียนอีโมจิได้สูงสุด {count} รายการต่อครั้ง)" + confirmClearEmojisDescription: "ต้องการยกเลิกการแก้ไขและล้างรายการเอโมจิที่แสดงอยู่หรือไม่?" + confirmUploadEmojisDescription: "จะอัปโหลดไฟล์ {count} รายการที่ลากและวางไปยังไดรฟ์ ดำเนินการหรือไม่?" _embedCodeGen: title: "ปรับแต่งโค้ดฝัง" header: "แสดงส่วนหัว" @@ -2724,15 +3009,137 @@ _embedCodeGen: generateCode: "สร้างโค้ดสำหรับการฝัง" codeGenerated: "รหัสถูกสร้างขึ้นแล้ว" codeGeneratedDescription: "นำโค้ดที่สร้างแล้วไปวางในเว็บไซต์ของคุณเพื่อฝังเนื้อหา" +_selfXssPrevention: + warning: "คำเตือน" + title: "“ข้อความที่บอกให้วางบางอย่างในหน้าจอนี้” ทั้งหมดเป็นการหลอกลวง" + description1: "ถ้าวางบางอย่างที่นี่ อาจทำให้ผู้ไม่หวังดีเข้าควบคุมบัญชี หรือขโมยข้อมูลส่วนตัวได้" + description2: "ถ้าไม่เข้าใจอย่างชัดเจนว่าสิ่งที่กำลังจะวางคืออะไร %cให้หยุดการทำงานทันทีแล้วปิดหน้าต่างนี้" + description3: "ดูรายละเอียดเพิ่มเติมได้ที่นี่: {link}" +_followRequest: + recieved: "คำขอที่ได้รับ" + sent: "คำที่ส่งไป" _remoteLookupErrors: + _federationNotAllowed: + title: "ไม่สามารถสื่อสารกับเซิร์ฟเวอร์นี้ได้" + description: "การสื่อสารกับเซิร์ฟเวอร์นี้อาจถูกปิดใช้งาน หรือเซิร์ฟเวอร์นี้อาจจะได้บล็อกคุณ หรือคุณอาจจะได้บล็อกเซิร์ฟเวอร์นี้อยู่\nกรุณาติดต่อผู้ดูแลระบบเซิร์ฟเวอร์เพื่อสอบถามรายละเอียดเพิ่มเติม" + _uriInvalid: + title: "URI ไม่ถูกต้อง" + description: "มีปัญหาเกี่ยวกับ URI ที่ป้อน โปรดตรวจสอบว่าไม่มีอักขระที่ไม่สามารถใช้กับ URI" + _requestFailed: + title: "การร้องขอล้มเหลว" + description: "การสื่อสารกับเซิร์ฟเวอร์นี้ล้มเหลว เซิร์ฟเวอร์ปลายทางอาจล่ม หรืออาจป้อน URI ที่ไม่ถูกต้องหรือไม่มีอยู่" + _responseInvalid: + title: "ข้อมูลตอบสนองกลับไม่ถูกต้อง" + description: "สามารถเชื่อมต่อกับเซิร์ฟเวอร์นี้ได้ แต่ข้อมูลที่ได้รับไม่ถูกต้อง หากกำลังดึงข้อมูลจากเซิร์ฟเวอร์บุคคลที่สาม โปรดใช้ URI ที่สามารถดึงข้อมูลได้จากเซิร์ฟเวอร์ต้นทางโดยตรง" _noSuchObject: title: "ไม่พบหน้าที่ต้องการ" + description: "ไม่พบทรัพยากรที่ร้องขอ กรุณาตรวจสอบ URI อีกครั้ง" +_captcha: + verify: "กรุณาผ่าน CAPTCHA" + testSiteKeyMessage: "สามารถดูตัวอย่างได้โดยป้อนค่าทดสอบใน site key และ secret key\nดูรายละเอียดเพิ่มเติมได้ที่หน้าด้านล่างนี้" + _error: + _requestFailed: + title: "การร้องขอ CAPTCHA ล้มเหลว" + text: "โปรดลองใหม่ภายหลัง หรือ ตรวจสอบการตั้งค่าอีกครั้ง" + _verificationFailed: + title: "การยืนยัน CAPTCHA ล้มเหลว" + text: "กรุณาตรวจสอบอีกครั้งว่าการตั้งค่าถูกต้องหรือไม่" + _unknown: + title: "CAPTCHA เกิดข้อผิดพลาด" + text: "เกิดข้อผิดพลาดที่ไม่คาดคิด" +_bootErrors: + title: "การโหลดล้มเหลว" + serverError: "หากปัญหายังคงอยู่แม้ว่าจะรอสักครู่แล้วโหลดหน้าใหม่อีกครั้ง โปรดติดต่อผู้ดูแลระบบเซิร์ฟเวอร์พร้อมรหัสข้อผิดพลาดต่อไปนี้" + solution: "สิ่งต่อไปนี้อาจช่วยแก้ไขปัญหาได้" + solution1: "อัปเดตเบราว์เซอร์และระบบปฏิบัติการเป็นรุ่นล่าสุด" + solution2: "ปิดใช้งานตัวบล็อกโฆษณา" + solution3: "ล้างแคชเบราว์เซอร์" + solution4: "(Tor Browser) ตั้งค่า dom.webaudio.enabled เป็น true" + otherOption: "ตัวเลือกเพิ่มเติม" + otherOption1: "ลบการตั้งค่าและแคชของไคลเอนต์" + otherOption2: "เริ่มใช้งานไคลเอนต์แบบง่าย" + otherOption3: "เปิดเครื่องมือซ่อมแซม" _search: searchScopeAll: "ทั้งหมด" searchScopeLocal: "ท้องถิ่น" + searchScopeServer: "ระบุเซิร์ฟเวอร์" searchScopeUser: "ผู้ใช้เฉพาะ" + pleaseEnterServerHost: "กรุณากรอกโฮสต์ของเซิร์ฟเวอร์" + pleaseSelectUser: "กรุณาเลือกผู้ใช้" + serverHostPlaceholder: "ตัวอย่าง: misskey.example.com" +_serverSetupWizard: + installCompleted: "การติดตั้ง Misskey เสร็จสมบูรณ์แล้ว!" + firstCreateAccount: "ขั้นแรก ให้สร้างบัญชีผู้ดูแลระบบ" + accountCreated: "บัญชีผู้ดูแลระบบถูกสร้างขึ้นแล้ว!" + serverSetting: "การตั้งค่าเซิร์ฟเวอร์" + youCanEasilyConfigureOptimalServerSettingsWithThisWizard: "สามารถตั้งค่าเซิร์ฟเวอร์ได้อย่างง่ายดายด้วยวิซาร์ดนี้" + settingsYouMakeHereCanBeChangedLater: "สามารถเปลี่ยนแปลงการตั้งค่าเหล่านี้ในภายหลังได้" + howWillYouUseMisskey: "ต้องการใช้ Misskey อย่างไร?" + _use: + single: "เซิร์ฟเวอร์คนเดียว" + single_description: "ใช้งานเป็นเซิร์ฟเวอร์ส่วนตัวสำหรับตัวเองคนเดียว" + single_youCanCreateMultipleAccounts: "แม้จะใช้งานเป็นเซิร์ฟเวอร์ส่วนตัวสำหรับคนเดียว ก็สามารถสร้างบัญชีผู้ใช้หลายบัญชีได้ตามความจำเป็น" + group: "เซิร์ฟเวอร์กลุ่ม" + group_description: "เชิญผู้ใช้ที่เชื่อถือได้ มาเข้าร่วมใช้งานแบบหลายคน" + open: "เซิร์ฟเวอร์สาธารณะ" + open_description: "เปิดรับผู้ใช้จำนวนมากแบบไม่จำกัด" + openServerAdvice: "การเปิดรับผู้ใช้จำนวนมากมีความเสี่ยง ควรบริหารจัดการด้วยระบบดูแลที่เข้มงวดเพื่อรับมือกับปัญหาที่อาจเกิดขึ้น" + openServerAntiSpamAdvice: "เพื่อป้องกันไม่ให้เซิร์ฟเวอร์ของตนกลายเป็นแหล่งส่งสแปม ควรเปิดใช้งานฟีเจอร์ป้องกันบอต เช่น reCAPTCHA และใส่ใจเรื่องความปลอดภัยอย่างเคร่งครัด" + howManyUsersDoYouExpect: "คาดว่าจะมีผู้ใช้งานประมาณกี่คน?" + _scale: + small: "น้อยกว่า 100 คน (ขนาดเล็ก)" + medium: "เกิน 100 คน แต่น้อยกว่า 1000 คน (ขนาดกลาง)" + large: "เกิน 1000 คน (ขนาดใหญ่)" + largeScaleServerAdvice: "เซิร์ฟเวอร์ขนาดใหญ่อาจต้องการความรู้ด้านโครงสร้างพื้นฐานขั้นสูง เช่น การบาลานซ์โหลด หรือการทำสำเนาฐานข้อมูล" + doYouConnectToFediverse: "เชื่อมต่อกับ Fediverse หรือไม่?" + doYouConnectToFediverse_description1: "หากเชื่อมต่อกับเครือข่ายที่ประกอบด้วยเซิร์ฟเวอร์แบบกระจาย (Fediverse) จะสามารถแลกเปลี่ยนเนื้อหากับเซิร์ฟเวอร์อื่นๆ ได้" + doYouConnectToFediverse_description2: "การเชื่อมต่อกับ Fediverse เรียกว่า “สหพันธ์”" + youCanConfigureMoreFederationSettingsLater: "หลังจากนี้ยังสามารถตั้งค่าแบบขั้นสูง เช่น การกำหนดเซิร์ฟเวอร์ที่อนุญาตให้สหพันธ์ต่อกันได้เพิ่มเติม" + adminInfo: "ข้อมูลผู้ดูแลระบ" + adminInfo_description: "ตั้งค่าข้อมูลผู้ดูแลระบบที่จะใช้รับคำถามและติดต่อ" + adminInfo_mustBeFilled: "หากเปิดใช้เซิร์ฟเวอร์สาธารณะ หรือเปิดใช้งานสหพันธ์ จะต้องกรอกข้อมูลนี้" + followingSettingsAreRecommended: "แนะนำให้ตั้งค่าตามด้านล่างนี้" + applyTheseSettings: "ใช้การตั้งค่านี้" + skipSettings: "ข้ามการตั้งค่า" + settingsCompleted: "การตั้งค่าเสร็จสมบูรณ์แล้ว!" + settingsCompleted_description: "ขอบคุณที่สละเวลามาตั้งค่า ตอนนี้เซิร์ฟเวอร์พร้อมใช้งานได้ทันที" + settingsCompleted_description2: "การตั้งค่าเซิร์ฟเวอร์อย่างละเอียดสามารถทำได้จาก “แผงควบคุม”" + donationRequest: "คำขอรับบริจาค" + _donationRequest: + text1: "Misskey เป็นซอฟต์แวร์ฟรีที่พัฒนาโดยอาสาสมัคร" + text2: "เพื่อให้การพัฒนางานนี้สามารถดำเนินต่อไปได้ในอนาคต หากไม่เป็นการรบกวน รบกวนพิจารณาร่วมสมทบทุนด้วยนะคะ" + text3: "นอกจากนี้ยังมีสิทธิพิเศษสำหรับผู้สนับสนุนอีกด้วยค่ะ" +_uploader: + editImage: "แก้ไขรูปภาพ" + compressedToX: "บีบอัดเป็น {x}" + savedXPercent: "ประหยัดไป {x}%" + abortConfirm: "มีไฟล์ที่ยังไม่ได้อัปโหลด ต้องการยกเลิกหรือไม่?" + doneConfirm: "มีไฟล์ที่ยังไม่ได้อัปโหลด ต้องการดำเนินการให้เสร็จสิ้นหรือไม่?" + maxFileSizeIsX: "ขนาดไฟล์สูงสุดที่สามารถอัปโหลดได้คือ {x}" + allowedTypes: "ประเภทไฟล์ที่สามารถอัปโหลดได้" + tip: "ยังไม่มีไฟล์ถูกอัปโหลด สามารถ ตรวจสอบ ลบชื่อไฟล์ บีบอัด หรือครอปตัดภาพ ก่อนอัปโหลดได้ในหน้านี้ เมื่อพร้อมแล้วให้กดปุ่ม “อัปโหลด” เพื่อเริ่มการอัปโหลด" +_clientPerformanceIssueTip: + title: "หากรู้สึกว่าแบตเตอรี่หมดเร็ว" + makeSureDisabledAdBlocker: "โปรดปิดการใช้งานตัวบล็อกโฆษณา" + makeSureDisabledAdBlocker_description: "ตัวบล็อกโฆษณาอาจส่งผลต่อประสิทธิภาพ โปรดตรวจสอบว่าไม่ได้เปิดใช้งานผ่านฟังก์ชันของระบบปฏิบัติการ เบราว์เซอร์ หรือส่วนเสริมใดๆ" + makeSureDisabledCustomCss: "โปรดปิดการใช้งาน CSS แบบกำหนดเอง" + makeSureDisabledCustomCss_description: "การเขียนทับสไตล์อาจส่งผลต่อประสิทธิภาพ โปรดตรวจสอบว่าไม่มี CSS แบบกำหนดเองหรือส่วนเสริมที่แก้ไขสไตล์เปิดใช้งานอยู่" + makeSureDisabledAddons: "โปรดปิดการใช้งานส่วนเสริม" + makeSureDisabledAddons_description: "ส่วนเสริมบางตัวอาจรบกวนการทำงานของไคลเอนต์และทำให้ประสิทธิภาพลดลง กรุณาลองปิดส่วนเสริมในเบราว์เซอร์แล้วตรวจสอบอีกครั้ง" +_clip: + tip: "คลิปเป็นฟังก์ชันที่สามารถรวมโน้ตเข้าด้วยกัน" +_userLists: + tip: "สามารถสร้างรายชื่อที่มีผู้ใช้ใดก็ได้ เมื่อสร้างแล้ว รายชื่อนั้นจะแสดงเป็นไทม์ไลน์ได้" +watermark: "ลายน้ำ" +defaultPreset: "พรีเซ็ตเริ่มต้น" _watermarkEditor: + tip: "สามารถเพิ่มลายน้ำ เช่น ข้อมูลเครดิต ลงในภาพได้" + quitWithoutSaveConfirm: "ต้องการออกโดยไม่บันทึกหรือไม่?" driveFileTypeWarn: "ไม่รองรับไฟล์นี้" + driveFileTypeWarnDescription: "กรุณาเลือกไฟล์ภาพ" + title: "แก้ไขลายน้ำ" + cover: "ซ้อนทับทั่วทั้งพื้นที่" + repeat: "ปูให้เต็มพื้นที่" opacity: "ความทึบแสง" scale: "ขนาด" text: "ข้อความ" @@ -2740,4 +3147,50 @@ _watermarkEditor: type: "รูปแบบ" image: "รูปภาพ" advanced: "ขั้นสูง" + stripe: "ริ้ว" + stripeWidth: "ความกว้างเส้น" + stripeFrequency: "จำนวนเส้น" angle: "แองเกิล" + polkadot: "ลายจุด" + checker: "ช่องตาราง" + polkadotMainDotOpacity: "ความทึบของจุดหลัก" + polkadotMainDotRadius: "ขนาดของจุดหลัก" + polkadotSubDotOpacity: "ความทึบของจุดรอง" + polkadotSubDotRadius: "ขนาดของจุดรอง" + polkadotSubDotDivisions: "จำนวนจุดรอง" +_imageEffector: + title: "เอฟเฟกต์" + addEffect: "เพิ่มเอฟเฟกต์" + discardChangesConfirm: "ต้องการทิ้งการเปลี่ยนแปลงแล้วออกหรือไม่?" + _fxs: + chromaticAberration: "ความคลาดสี" + glitch: "กลิตช์" + mirror: "กระจก" + invert: "กลับสี" + grayscale: "ขาวดำเทา" + colorAdjust: "ปรับแก้สี" + colorClamp: "บีบอัดสี" + colorClampAdvanced: "บีบอัดสี (ขั้นสูง)" + distort: "บิดเบี้ยว" + threshold: "สองสี" + zoomLines: "เส้นความเข้มข้น" + stripe: "ริ้ว" + polkadot: "ลายจุด" + checker: "ช่องตาราง" + blockNoise: "บล็อกที่มีการรบกวน" + tearing: "ฉีกขาด" +drafts: "ร่าง" +_drafts: + select: "เลือกฉบับร่าง" + cannotCreateDraftAnymore: "ถึงจำนวนจำกัดที่ฉบับร่างที่สามารถสร้างได้แล้ว" + cannotCreateDraft: "ไม่สามารถสร้างฉบับร่างด้วยเนื้อหานี้ได้" + delete: "ลบฉบับร่าง" + deleteAreYouSure: "ต้องการลบฉบับร่างหรือไม่?" + noDrafts: "ไม่มีฉบับร่าง" + replyTo: "ตอบกลับ {user}" + quoteOf: "อ้างอิงถึงโน้ตของ {user}" + postTo: "โพสต์ไปยัง {channel}" + saveToDraft: "บันทึกเป็นฉบับร่าง" + restoreFromDraft: "คืนค่าจากฉบับร่าง" + restore: "กู้คืน" + listDrafts: "รายการฉบับร่าง" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 0c7308a016..c6708ec0e2 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -8,6 +8,9 @@ search: "Пошук" notifications: "Сповіщення" username: "Ім'я користувача" password: "Пароль" +initialPasswordForSetup: "Початковий пароль для налаштування" +initialPasswordIsIncorrect: "Початковий пароль для налаштування неправильний" +initialPasswordForSetupDescription: "Використайте пароль, вказаний у конфігураційному файлі, якщо ви встановлювали Misskey власноруч.\nЯкщо використовуєте сервіси хостингу Misskey, використайте наданий пароль.\nЯкщо ви не маєте паролю, лишіть порожнім щоб продовжити. " forgotPassword: "Я забув пароль" fetchingAsApObject: "Отримуємо з федіверсу..." ok: "OK" @@ -45,6 +48,7 @@ pin: "Закріпити" unpin: "Відкріпити" copyContent: "Скопіювати контент" copyLink: "Скопіювати посилання" +copyRemoteLink: "Копіювати віддалене посилання" delete: "Видалити" deleteAndEdit: "Видалити й редагувати" deleteAndEditConfirm: "Ви впевнені, що хочете видалити цю нотатку та відредагувати її? Ви втратите всі реакції, поширення та відповіді на неї." @@ -57,6 +61,7 @@ copyUserId: "Копіювати ID користувача" copyNoteId: "блокнот ID користувача" copyFileId: "Скопіювати ідентифікатор файлу." searchUser: "Пошук користувачів" +searchThisUsersNotes: "Пошук нотаток користувача" reply: "Відповісти" loadMore: "Показати більше" showMore: "Показати більше" @@ -105,9 +110,11 @@ enterEmoji: "Введіть емодзі" renote: "Поширити" unrenote: "Відміна поширення" renoted: "Поширити запис." +renotedToX: "Поширено до {name}" cantRenote: "Неможливо поширити." cantReRenote: "Поширення не можливо поширити." quote: "Цитата" +inChannelRenote: "Поширено у канал" pinnedNote: "Закріплений запис" pinned: "Закріпити" you: "Ви" @@ -116,6 +123,7 @@ sensitive: "NSFW" add: "Додати" reaction: "Реакції" reactions: "Реакції" +emojiPicker: "Вибір реакції" reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати." rememberNoteVisibility: "Пам’ятати параметри видимісті" attachCancel: "Видалити вкладення" @@ -289,7 +297,9 @@ folderName: "Ім'я теки" createFolder: "Створити теку" renameFolder: "Перейменувати теку" deleteFolder: "Видалити теку" +folder: "Тека" addFile: "Додати файл" +showFile: "Показати файл" emptyDrive: "Диск порожній" emptyFolder: "Тека порожня" unableToDelete: "Видалення неможливе" @@ -302,6 +312,7 @@ copyUrl: "Копіювати URL" rename: "Перейменувати" avatar: "Аватар" banner: "Банер" +displayOfSensitiveMedia: "Показ чутливого медіа" whenServerDisconnected: "Коли зв’язок із сервером втрачено" disconnectedFromServer: "Зв’язок із сервером було перервано" reload: "Оновити" @@ -348,8 +359,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Увімкнути hCaptcha" hcaptchaSiteKey: "Ключ сайту" hcaptchaSecretKey: "Секретний ключ" +mcaptcha: "MCaptcha" +enableMcaptcha: "Увімкнути MCaptcha" mcaptchaSiteKey: "Ключ сайту" mcaptchaSecretKey: "Секретний ключ" +mcaptchaInstanceUrl: "Посилання на сервер MCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "Увімкнути reCAPTCHA" recaptchaSiteKey: "Ключ сайту" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 3ab1f2e45a..f236202abe 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -2806,6 +2806,7 @@ _fileViewer: url: "URL" uploadedAt: "添加日期" attachedNotes: "附加到的帖子" + usage: "使用" thisPageCanBeSeenFromTheAuthor: "此页只能被该文件的上传者查看。" _externalResourceInstaller: title: "从外部站点安装" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 4982dd093c..c6ff0bb1a3 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -2806,6 +2806,7 @@ _fileViewer: url: "URL" uploadedAt: "加入日期" attachedNotes: "含有附件的貼文" + usage: "使用情況" thisPageCanBeSeenFromTheAuthor: "本頁面僅限上傳了這個檔案的使用者可以檢視。" _externalResourceInstaller: title: "從外部網站安裝" From 5ecaf5095ecd4678dd3e05abcc5b0781f6fddb93 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sat, 12 Jul 2025 15:13:35 +0900 Subject: [PATCH 10/85] =?UTF-8?q?enhance:=20=E3=82=A6=E3=82=A9=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=83=9E=E3=83=BC=E3=82=AF=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E3=82=92=E3=83=AD=E3=83=BC=E3=83=AB=E3=81=A7=E5=88=B6=E5=BE=A1?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + locales/index.d.ts | 4 ++++ locales/ja-JP.yml | 1 + packages/backend/src/core/RoleService.ts | 3 +++ .../backend/src/models/json-schema/role.ts | 4 ++++ packages/frontend-shared/js/const.ts | 1 + .../frontend/src/composables/use-uploader.ts | 7 +++++-- .../frontend/src/pages/admin/roles.editor.vue | 20 +++++++++++++++++++ packages/frontend/src/pages/admin/roles.vue | 8 ++++++++ .../frontend/src/pages/settings/drive.vue | 2 +- packages/misskey-js/src/autogen/types.ts | 1 + 11 files changed, 49 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 869ecff00e..27810a5b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Feat: クリップ内でノートを検索できるように - Feat: Playを検索できるように - Feat: モデレーションにおいて、特定のドライブファイルを添付しているチャットメッセージを一覧できるように +- Enhance: ウォーターマーク機能をロールで制御可能に ### Client - Feat: モデログを検索できるように diff --git a/locales/index.d.ts b/locales/index.d.ts index d5ec9f5d77..ceb3cc672d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -7795,6 +7795,10 @@ export interface Locale extends ILocale { * サーバーサイドのノートの下書きの作成可能数 */ "noteDraftLimit": string; + /** + * ウォーターマーク機能の使用可否 + */ + "watermarkAvailable": string; }; "_condition": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 760f89ef64..6af846f7ee 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2019,6 +2019,7 @@ _role: uploadableFileTypes_caption: "MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)" uploadableFileTypes_caption2: "ファイルによっては種別を判定できないことがあります。そのようなファイルを許可する場合は {x} を指定に追加してください。" noteDraftLimit: "サーバーサイドのノートの下書きの作成可能数" + watermarkAvailable: "ウォーターマーク機能の使用可否" _condition: roleAssignedTo: "マニュアルロールにアサイン済み" isLocal: "ローカルユーザー" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 314f7e221a..cddfc0094e 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -67,6 +67,7 @@ export type RolePolicies = { chatAvailability: 'available' | 'readonly' | 'unavailable'; uploadableFileTypes: string[]; noteDraftLimit: number; + watermarkAvailable: boolean; }; export const DEFAULT_POLICIES: RolePolicies = { @@ -111,6 +112,7 @@ export const DEFAULT_POLICIES: RolePolicies = { 'audio/*', ], noteDraftLimit: 10, + watermarkAvailable: true, }; @Injectable() @@ -433,6 +435,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { return [...set]; }), noteDraftLimit: calc('noteDraftLimit', vs => Math.max(...vs)), + watermarkAvailable: calc('watermarkAvailable', vs => vs.some(v => v === true)), }; } diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index a3f679129d..c9cdbd5d89 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -313,6 +313,10 @@ export const packedRolePoliciesSchema = { type: 'integer', optional: false, nullable: false, }, + watermarkAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, }, } as const; diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 4498a5e2b2..5c33c38f44 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -112,6 +112,7 @@ export const ROLE_POLICIES = [ 'chatAvailability', 'uploadableFileTypes', 'noteDraftLimit', + 'watermarkAvailable', ] as const; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; diff --git a/packages/frontend/src/composables/use-uploader.ts b/packages/frontend/src/composables/use-uploader.ts index 6f4ed81f82..826d8c5203 100644 --- a/packages/frontend/src/composables/use-uploader.ts +++ b/packages/frontend/src/composables/use-uploader.ts @@ -104,6 +104,8 @@ export function useUploader(options: { multiple?: boolean; features?: UploaderFeatures; } = {}) { + const $i = ensureSignin(); + const events = new EventEmitter<{ 'itemUploaded': (ctx: { item: UploaderItem; }) => void; }>(); @@ -132,7 +134,7 @@ export function useUploader(options: { uploaded: null, uploadFailed: false, compressionLevel: prefer.s.defaultImageCompressionLevel, - watermarkPresetId: uploaderFeatures.value.watermark ? prefer.s.defaultWatermarkPresetId : null, + watermarkPresetId: uploaderFeatures.value.watermark && $i.policies.watermarkAvailable ? prefer.s.defaultWatermarkPresetId : null, file: markRaw(file), }); const reactiveItem = items.value.at(-1)!; @@ -264,6 +266,7 @@ export function useUploader(options: { if ( uploaderFeatures.value.watermark && + $i.policies.watermarkAvailable && WATERMARK_SUPPORTED_TYPES.includes(item.file.type) && !item.preprocessing && !item.uploading && @@ -500,7 +503,7 @@ export function useUploader(options: { let preprocessedFile: Blob | File = item.file; - const needsWatermark = item.watermarkPresetId != null && WATERMARK_SUPPORTED_TYPES.includes(preprocessedFile.type); + const needsWatermark = item.watermarkPresetId != null && WATERMARK_SUPPORTED_TYPES.includes(preprocessedFile.type) && $i.policies.watermarkAvailable; const preset = prefer.s.watermarkPresets.find(p => p.id === item.watermarkPresetId); if (needsWatermark && preset != null) { const canvas = window.document.createElement('canvas'); diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index a266e1df6f..c172e22688 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -780,6 +780,26 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + +
+ + + + + + + + + +
+
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index dee0fb1e5c..e78a4bbc11 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -291,6 +291,14 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + {{ i18n.ts._role.new }} diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue index 0614b1242b..1b99f6dea5 100644 --- a/packages/frontend/src/pages/settings/drive.vue +++ b/packages/frontend/src/pages/settings/drive.vue @@ -87,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- + diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index df6a22ec41..51e4b4f45d 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -5225,6 +5225,7 @@ export type components = { /** @enum {string} */ chatAvailability: 'available' | 'readonly' | 'unavailable'; noteDraftLimit: number; + watermarkAvailable: boolean; }; ReversiGameLite: { /** Format: id */ From f954b1e27622bc4b2747f9cb57393c91d37fa82d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 12 Jul 2025 06:17:58 +0000 Subject: [PATCH 11/85] Bump version to 2025.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 8e4a28b2e3..72ccb3be67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.7.0-beta.0", + "version": "2025.7.0-beta.1", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 8042c7088c..f13ab7594f 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.7.0-beta.0", + "version": "2025.7.0-beta.1", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 02ac7d002938c595db3ecba88750d697ba989e09 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 13 Jul 2025 20:24:31 +0900 Subject: [PATCH 12/85] wip --- .../backend/src/core/NoteCreateService.ts | 2 + .../backend/src/core/UserFollowingService.ts | 7 ++-- .../backend/src/core/UserSuspendService.ts | 40 ++++++++++--------- .../activitypub/ApDeliverManagerService.ts | 1 + packages/backend/src/models/Following.ts | 7 ++++ .../api/endpoints/federation/followers.ts | 3 +- .../api/endpoints/federation/following.ts | 3 +- .../server/api/endpoints/federation/stats.ts | 2 + .../server/api/endpoints/users/followers.ts | 2 + .../server/api/endpoints/users/following.ts | 2 + .../api/endpoints/users/recommendation.ts | 3 +- 11 files changed, 48 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 469426f87e..3f045efd03 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -545,6 +545,7 @@ export class NoteCreateService implements OnApplicationShutdown { // TODO: キャッシュ this.followingsRepository.findBy({ followeeId: user.id, + isFollowerSuspended: false, notify: 'normal', }).then(async followings => { if (note.visibility !== 'specified') { @@ -850,6 +851,7 @@ export class NoteCreateService implements OnApplicationShutdown { where: { followeeId: user.id, followerHost: IsNull(), + isFollowerSuspended: false, isFollowerHibernated: false, }, select: ['followerId', 'withReplies'], diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index e7a6be99fb..f5d8222a8e 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -229,9 +229,7 @@ export class UserFollowingService implements OnModuleInit { followee: { id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] }, - follower: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] - }, + follower: MiUser, silent = false, withReplies?: boolean, ): Promise { @@ -244,6 +242,7 @@ export class UserFollowingService implements OnModuleInit { followerId: follower.id, followeeId: followee.id, withReplies: withReplies, + isFollowerSuspended: follower.isSuspended, // 非正規化 followerHost: follower.host, @@ -734,6 +733,7 @@ export class UserFollowingService implements OnModuleInit { return this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: userId }) + .andWhere('following.isFollowerSuspended = false') .getMany(); } @@ -743,6 +743,7 @@ export class UserFollowingService implements OnModuleInit { where: { followerId, followeeId, + isFollowerSuspended: false, }, }); } diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 7920e58e36..601859990a 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -49,8 +49,8 @@ export class UserSuspendService { }); (async () => { - await this.postSuspend(user).catch(e => {}); - await this.unFollowAll(user).catch(e => {}); + await this.postSuspend(user).catch((e: any) => {}); + await this.unFollowAll(user).catch((e: any) => {}); })(); } @@ -67,7 +67,8 @@ export class UserSuspendService { }); (async () => { - await this.postUnsuspend(user).catch(e => {}); + await this.postUnsuspend(user).catch((e: any) => {}); + await this.restoreFollowings(user).catch((e: any) => {}); })(); } @@ -140,23 +141,26 @@ export class UserSuspendService { @bindThis private async unFollowAll(follower: MiUser) { - const followings = await this.followingsRepository.find({ - where: { + await this.followingsRepository.update( + { followerId: follower.id, - followeeId: Not(IsNull()), }, - }); - - const jobs: RelationshipJobData[] = []; - for (const following of followings) { - if (following.followeeId && following.followerId) { - jobs.push({ - from: { id: following.followerId }, - to: { id: following.followeeId }, - silent: true, - }); + { + isFollowerSuspended: true, } - } - this.queueService.createUnfollowJob(jobs); + ); + } + + @bindThis + private async restoreFollowings(follower: MiUser) { + // フォロー関係を復元(isFollowerSuspended: false)に変更 + await this.followingsRepository.update( + { + followerId: follower.id, + }, + { + isFollowerSuspended: false, + } + ); } } diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 0140ce9fd6..c0d667253f 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -119,6 +119,7 @@ class DeliverManager { where: { followeeId: this.actor.id, followerHost: Not(IsNull()), + isFollowerSuspended: false, }, select: { followerSharedInbox: true, diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts index 62cbc29f26..c30a7d42d6 100644 --- a/packages/backend/src/models/Following.ts +++ b/packages/backend/src/models/Following.ts @@ -10,6 +10,8 @@ import { MiUser } from './User.js'; @Entity('following') @Index(['followerId', 'followeeId'], { unique: true }) @Index(['followeeId', 'followerHost', 'isFollowerHibernated']) +@Index(['followeeId', 'followerHost', 'isFollowerSuspended']) +@Index(['followerId', 'isFollowerSuspended']) export class MiFollowing { @PrimaryColumn(id()) public id: string; @@ -45,6 +47,11 @@ export class MiFollowing { }) public isFollowerHibernated: boolean; + @Column('boolean', { + default: false, + }) + public isFollowerSuspended: boolean; + // タイムラインにその人のリプライまで含めるかどうか @Column('boolean', { default: false, diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 296bc7c5a8..f4d4caf988 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -50,7 +50,8 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('following.followeeHost = :host', { host: ps.host }); + .andWhere('following.followeeHost = :host', { host: ps.host }) + .andWhere('following.isFollowerSuspended = false'); const followings = await query .limit(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 091bf442af..fa8af37c1c 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -50,7 +50,8 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('following.followerHost = :host', { host: ps.host }); + .andWhere('following.followerHost = :host', { host: ps.host }) + .andWhere('following.isFollowerSuspended = false'); const followings = await query .limit(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index 69900bff9a..3093aa1e86 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -94,11 +94,13 @@ export default class extends Endpoint { // eslint- this.followingsRepository.count({ where: { followeeHost: Not(IsNull()), + isFollowerSuspended: false, }, }), this.followingsRepository.count({ where: { followerHost: Not(IsNull()), + isFollowerSuspended: false, }, }), ]); diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 84c4c80d01..3afba603a2 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -125,6 +125,7 @@ export default class extends Endpoint { // eslint- where: { followeeId: user.id, followerId: me.id, + isFollowerSuspended: false, }, }); if (!isFollowing) { @@ -136,6 +137,7 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('following.followeeId = :userId', { userId: user.id }) + .andWhere('following.isFollowerSuspended = false') .innerJoinAndSelect('following.follower', 'follower'); const followings = await query diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 047f9a053b..fb85106e11 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -133,6 +133,7 @@ export default class extends Endpoint { // eslint- where: { followeeId: user.id, followerId: me.id, + isFollowerSuspended: false, }, }); if (!isFollowing) { @@ -144,6 +145,7 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('following.followerId = :userId', { userId: user.id }) + .andWhere('following.isFollowerSuspended = false') .innerJoinAndSelect('following.followee', 'followee'); if (ps.birthday) { diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 769a72d7a1..3ef7848ebb 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -68,7 +68,8 @@ export default class extends Endpoint { // eslint- const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); + .where('following.followerId = :followerId', { followerId: me.id }) + .andWhere('following.isFollowerSuspended = false'); query .andWhere(`user.id NOT IN (${ followingQuery.getQuery() })`); From e5a0d2775a04c4bca587c64c7f6a36e66308e016 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 13 Jul 2025 21:52:33 +0900 Subject: [PATCH 13/85] add migration --- ...2410859370-FollowingIsFollowerSuspended.js | 21 +++++++++++++++++++ packages/backend/src/models/Following.ts | 5 ++--- 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js diff --git a/packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js b/packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js new file mode 100644 index 0000000000..8542bcdd8b --- /dev/null +++ b/packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +module.exports = class FollowingIsFollowerSuspended1752410859370 { + name = 'FollowingIsFollowerSuspended1752410859370' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_ce62b50d882d4e9dee10ad0d2f"`); + await queryRunner.query(`ALTER TABLE "following" ADD "isFollowerSuspended" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`CREATE INDEX "IDX_1896254b78a41a50e0396fdabd" ON "following" ("followeeId", "followerHost", "isFollowerSuspended", "isFollowerHibernated") `); + await queryRunner.query(`CREATE INDEX "IDX_d2b8dbf0b772042f4fe241a29d" ON "following" ("followerId", "followeeId", "isFollowerSuspended") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_d2b8dbf0b772042f4fe241a29d"`); + await queryRunner.query(`DROP INDEX "public"."IDX_1896254b78a41a50e0396fdabd"`); + await queryRunner.query(`ALTER TABLE "following" DROP COLUMN "isFollowerSuspended"`); + await queryRunner.query(`CREATE INDEX "IDX_ce62b50d882d4e9dee10ad0d2f" ON "following" ("followeeId", "followerHost", "isFollowerHibernated") `); + } +} diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts index c30a7d42d6..b9ac2e5005 100644 --- a/packages/backend/src/models/Following.ts +++ b/packages/backend/src/models/Following.ts @@ -9,9 +9,8 @@ import { MiUser } from './User.js'; @Entity('following') @Index(['followerId', 'followeeId'], { unique: true }) -@Index(['followeeId', 'followerHost', 'isFollowerHibernated']) -@Index(['followeeId', 'followerHost', 'isFollowerSuspended']) -@Index(['followerId', 'isFollowerSuspended']) +@Index(['followerId', 'followeeId', 'isFollowerSuspended']) +@Index(['followeeId', 'followerHost', 'isFollowerSuspended', 'isFollowerHibernated']) export class MiFollowing { @PrimaryColumn(id()) public id: string; From 77a11e369f2d8bbc5bc9b26c0e00b317a8555885 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 13 Jul 2025 23:48:39 +0900 Subject: [PATCH 14/85] add test --- .../backend/test/unit/UserSuspendService.ts | 366 ++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 packages/backend/test/unit/UserSuspendService.ts diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts new file mode 100644 index 0000000000..89edc6d116 --- /dev/null +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -0,0 +1,366 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +process.env.NODE_ENV = 'test'; + +import { jest } from '@jest/globals'; +import { Test } from '@nestjs/testing'; +import { DataSource } from 'typeorm'; +import type { TestingModule } from '@nestjs/testing'; +import { GlobalModule } from '@/GlobalModule.js'; +import { UserSuspendService } from '@/core/UserSuspendService.js'; +import { + MiFollowing, + MiUser, + FollowingsRepository, + FollowRequestsRepository, + UsersRepository, +} from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { QueueService } from '@/core/QueueService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { secureRndstr } from '@/misc/secure-rndstr.js'; + +export async function sleep(ms = 250): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +describe('UserSuspendService', () => { + let app: TestingModule; + let userSuspendService: UserSuspendService; + let usersRepository: UsersRepository; + let followingsRepository: FollowingsRepository; + let followRequestsRepository: FollowRequestsRepository; + let userEntityService: jest.Mocked; + let queueService: jest.Mocked; + let globalEventService: jest.Mocked; + let apRendererService: jest.Mocked; + let moderationLogService: jest.Mocked; + + async function createUser(data: Partial = {}): Promise { + const user = { + id: secureRndstr(16), + username: secureRndstr(16), + usernameLower: secureRndstr(16).toLowerCase(), + host: null, + isSuspended: false, + ...data, + } as MiUser; + + await usersRepository.insert(user); + return user; + } + + async function createFollowing(follower: MiUser, followee: MiUser, data: Partial = {}): Promise { + const following = { + id: secureRndstr(16), + followerId: follower.id, + followeeId: followee.id, + isFollowerSuspended: false, + isFollowerHibernated: false, + withReplies: false, + notify: null, + followerHost: follower.host, + followerInbox: null, + followerSharedInbox: null, + followeeHost: followee.host, + followeeInbox: null, + followeeSharedInbox: null, + ...data, + } as MiFollowing; + + await followingsRepository.insert(following); + return following; + } + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: [GlobalModule], + providers: [ + UserSuspendService, + { + provide: UserEntityService, + useFactory: () => ({ + isLocalUser: jest.fn(), + genLocalUserUri: jest.fn(), + }), + }, + { + provide: QueueService, + useFactory: () => ({ + deliver: jest.fn(), + }), + }, + { + provide: GlobalEventService, + useFactory: () => ({ + publishInternalEvent: jest.fn(), + }), + }, + { + provide: ApRendererService, + useFactory: () => ({ + addContext: jest.fn(), + renderDelete: jest.fn(), + renderUndo: jest.fn(), + }), + }, + { + provide: ModerationLogService, + useFactory: () => ({ + log: jest.fn(), + }), + }, + ], + }).compile(); + + app.enableShutdownHooks(); + + userSuspendService = app.get(UserSuspendService); + usersRepository = app.get(DI.usersRepository); + followingsRepository = app.get(DI.followingsRepository); + followRequestsRepository = app.get(DI.followRequestsRepository); + userEntityService = app.get(UserEntityService) as jest.Mocked; + queueService = app.get(QueueService) as jest.Mocked; + globalEventService = app.get(GlobalEventService) as jest.Mocked; + apRendererService = app.get(ApRendererService) as jest.Mocked; + moderationLogService = app.get(ModerationLogService) as jest.Mocked; + + // Reset mocks + jest.clearAllMocks(); + }); + + afterEach(async () => { + await app.close(); + }); + + describe('suspend', () => { + test('should suspend user and update database', async () => { + const user = await createUser(); + const moderator = await createUser(); + + await userSuspendService.suspend(user, moderator); + + // ユーザーが凍結されているかチェック + const suspendedUser = await usersRepository.findOneBy({ id: user.id }); + expect(suspendedUser?.isSuspended).toBe(true); + + // モデレーションログが記録されているかチェック + expect(moderationLogService.log).toHaveBeenCalledWith(moderator, 'suspend', { + userId: user.id, + userUsername: user.username, + userHost: user.host, + }); + }); + + test('should mark follower relationships as suspended', async () => { + const user = await createUser(); + const followee1 = await createUser(); + const followee2 = await createUser(); + const moderator = await createUser(); + + // ユーザーがフォローしている関係を作成 + await createFollowing(user, followee1); + await createFollowing(user, followee2); + + await userSuspendService.suspend(user, moderator); + await sleep(); + + // フォロー関係が論理削除されているかチェック + const followings = await followingsRepository.find({ + where: { followerId: user.id }, + }); + + expect(followings).toHaveLength(2); + followings.forEach(following => { + expect(following.isFollowerSuspended).toBe(true); + }); + }); + + test('should publish internal event for suspension', async () => { + const user = await createUser(); + const moderator = await createUser(); + + await userSuspendService.suspend(user, moderator); + await sleep(); + + // 内部イベントが発行されているかチェック(非同期処理のため少し待つ) + await new Promise(resolve => setTimeout(resolve, 100)); + + expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith( + 'userChangeSuspendedState', + { id: user.id, isSuspended: true }, + ); + }); + }); + + describe('unsuspend', () => { + test('should unsuspend user and update database', async () => { + const user = await createUser({ isSuspended: true }); + const moderator = await createUser(); + + await userSuspendService.unsuspend(user, moderator); + await sleep(); + + // ユーザーの凍結が解除されているかチェック + const unsuspendedUser = await usersRepository.findOneBy({ id: user.id }); + expect(unsuspendedUser?.isSuspended).toBe(false); + + // モデレーションログが記録されているかチェック + expect(moderationLogService.log).toHaveBeenCalledWith(moderator, 'unsuspend', { + userId: user.id, + userUsername: user.username, + userHost: user.host, + }); + }); + + test('should restore follower relationships', async () => { + const user = await createUser({ isSuspended: true }); + const followee1 = await createUser(); + const followee2 = await createUser(); + const moderator = await createUser(); + + // 凍結状態のフォロー関係を作成 + await createFollowing(user, followee1, { isFollowerSuspended: true }); + await createFollowing(user, followee2, { isFollowerSuspended: true }); + + await userSuspendService.unsuspend(user, moderator); + await sleep(); + + // フォロー関係が復元されているかチェック + const followings = await followingsRepository.find({ + where: { followerId: user.id }, + }); + + expect(followings).toHaveLength(2); + followings.forEach(following => { + expect(following.isFollowerSuspended).toBe(false); + }); + }); + + test('should publish internal event for unsuspension', async () => { + const user = await createUser({ isSuspended: true }); + const moderator = await createUser(); + + await userSuspendService.unsuspend(user, moderator); + await sleep(); + + // 内部イベントが発行されているかチェック(非同期処理のため少し待つ) + await new Promise(resolve => setTimeout(resolve, 100)); + + expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith( + 'userChangeSuspendedState', + { id: user.id, isSuspended: false }, + ); + }); + }); + + describe('integration test: suspend and unsuspend cycle', () => { + test('should preserve follow relationships through suspend/unsuspend cycle', async () => { + const user = await createUser(); + const followee1 = await createUser(); + const followee2 = await createUser(); + const moderator = await createUser(); + + // 初期のフォロー関係を作成 + await createFollowing(user, followee1); + await createFollowing(user, followee2); + + // 初期状態の確認 + let followings = await followingsRepository.find({ + where: { followerId: user.id }, + }); + expect(followings).toHaveLength(2); + followings.forEach(following => { + expect(following.isFollowerSuspended).toBe(false); + }); + + // 凍結 + await userSuspendService.suspend(user, moderator); + await sleep(); + + // 凍結後の状態確認 + followings = await followingsRepository.find({ + where: { followerId: user.id }, + }); + expect(followings).toHaveLength(2); + followings.forEach(following => { + expect(following.isFollowerSuspended).toBe(true); + }); + + // 凍結解除 + const suspendedUser = await usersRepository.findOneByOrFail({ id: user.id }); + await userSuspendService.unsuspend(suspendedUser, moderator); + await sleep(); + + // 凍結解除後の状態確認 + followings = await followingsRepository.find({ + where: { followerId: user.id }, + }); + expect(followings).toHaveLength(2); + followings.forEach(following => { + expect(following.isFollowerSuspended).toBe(false); + }); + }); + }); + + describe('ActivityPub delivery', () => { + test('should deliver Delete activity on suspend of local user', async () => { + const localUser = await createUser({ host: null }); + const moderator = await createUser(); + + userEntityService.isLocalUser.mockReturnValue(true); + userEntityService.genLocalUserUri.mockReturnValue(`https://example.com/users/${localUser.id}`); + apRendererService.renderDelete.mockReturnValue({ type: 'Delete' } as any); + apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Delete' } as any); + + await userSuspendService.suspend(localUser, moderator); + await sleep(); + + // ActivityPub配信が呼ばれているかチェック + expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser); + expect(apRendererService.renderDelete).toHaveBeenCalled(); + expect(apRendererService.addContext).toHaveBeenCalled(); + }); + + test('should deliver Undo Delete activity on unsuspend of local user', async () => { + const localUser = await createUser({ host: null, isSuspended: true }); + const moderator = await createUser(); + + userEntityService.isLocalUser.mockReturnValue(true); + userEntityService.genLocalUserUri.mockReturnValue(`https://example.com/users/${localUser.id}`); + apRendererService.renderDelete.mockReturnValue({ type: 'Delete' } as any); + apRendererService.renderUndo.mockReturnValue({ type: 'Undo' } as any); + apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Undo' } as any); + + await userSuspendService.unsuspend(localUser, moderator); + await sleep(); + + // ActivityPub配信が呼ばれているかチェック + expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser); + expect(apRendererService.renderDelete).toHaveBeenCalled(); + expect(apRendererService.renderUndo).toHaveBeenCalled(); + expect(apRendererService.addContext).toHaveBeenCalled(); + }); + + test('should not deliver any activity on suspend of remote user', async () => { + const remoteUser = await createUser({ host: 'remote.example.com' }); + const moderator = await createUser(); + + userEntityService.isLocalUser.mockReturnValue(false); + + await userSuspendService.suspend(remoteUser, moderator); + await sleep(); + + // ActivityPub配信が呼ばれていないことをチェック + expect(userEntityService.isLocalUser).toHaveBeenCalledWith(remoteUser); + expect(apRendererService.renderDelete).not.toHaveBeenCalled(); + expect(queueService.deliver).not.toHaveBeenCalled(); + }); + }); +}); From 3a5870b9a537f4aefae44e6f53e45dc1c44eb937 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 13 Jul 2025 23:54:16 +0900 Subject: [PATCH 15/85] add migration; copy suspended state from user table --- ...ngIsFollowerSuspendedCopySuspendedState.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js diff --git a/packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js b/packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js new file mode 100644 index 0000000000..4620442a13 --- /dev/null +++ b/packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +module.exports = class FollowingIsFollowerSuspendedCopySuspendedState1752410900000 { + name = 'FollowingIsFollowerSuspendedCopySuspendedState1752410900000' + + async up(queryRunner) { + // Update existing records based on user suspension status + await queryRunner.query(` + UPDATE "following" + SET "isFollowerSuspended" = "user"."isSuspended" + FROM "user" + WHERE "following"."followerId" = "user"."id" + `); + } + + async down(queryRunner) { + } +} From cbc85eef3e5bc8032783e31da25e27a2ce1c20e7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 14 Jul 2025 14:15:29 +0900 Subject: [PATCH 16/85] wip --- packages/backend/src/core/FanoutTimelineService.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/backend/src/core/FanoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts index 24999bf4da..3d1f571066 100644 --- a/packages/backend/src/core/FanoutTimelineService.ts +++ b/packages/backend/src/core/FanoutTimelineService.ts @@ -8,6 +8,7 @@ import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import { MiUser } from '@/models/User.js'; export type FanoutTimelineName = ( // home timeline @@ -112,4 +113,17 @@ export class FanoutTimelineService { public purge(name: FanoutTimelineName) { return this.redisForTimelines.del('list:' + name); } + + @bindThis + public purgeByUserIds(userIds: MiUser['id'][]) { + return Promise.all(userIds.flatMap(userId => [ + this.purge(`homeTimeline:${userId}`), + this.purge(`homeTimelineWithFiles:${userId}`), + this.purge(`localTimelineWithReplyTo:${userId}`), + this.purge(`userTimeline:${userId}`), + this.purge(`userTimelineWithFiles:${userId}`), + this.purge(`userTimelineWithReplies:${userId}`), + this.purge(`userTimelineWithChannel:${userId}`), + ])); + } } From 4da98fe3946720f8e19f2e57d69057517fa99f4d Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 14 Jul 2025 14:20:44 +0900 Subject: [PATCH 17/85] =?UTF-8?q?test(backend):=20=E9=9D=9EFTT=E6=99=82?= =?UTF-8?q?=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/test/e2e/timelines.ts | 3196 ++++++++++++------------ 1 file changed, 1650 insertions(+), 1546 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index e53c3d8f34..237b5885c5 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -9,1561 +9,1665 @@ import * as assert from 'assert'; import { setTimeout } from 'node:timers/promises'; import { Redis } from 'ioredis'; -import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js'; +import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl, role } from '../utils.js'; import { loadConfig } from '@/config.js'; +import { SignupResponse, Role } from 'misskey-js/entities.js'; function genHost() { return randomString() + '.example.com'; } -function waitForPushToTl() { - return setTimeout(500); -} - let redisForTimelines: Redis; +let root: SignupResponse; +let roleAdmin: Role; describe('Timelines', () => { - beforeAll(() => { + beforeAll(async () => { redisForTimelines = new Redis(loadConfig().redisForTimelines); + root = await signup({ username: 'root' }); + //console.log(await api('admin/update-meta', { enableFanoutTimeline }, root)); + }, 1000 * 60 * 2); + + describe.each([ + { enableFanoutTimeline: true }, + { enableFanoutTimeline: false }, + ])('Timelines (enableFanoutTimeline: $enableFanoutTimeline)', ({ enableFanoutTimeline }) => { + function waitForPushToTl() { + return setTimeout(250); + } + + beforeAll(async () => { + console.log(await api('admin/update-meta', { enableFanoutTimeline }, root)); + }, 1000 * 60 * 2); + + describe('Home TL', () => { + test('自分の visibility: followers なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi'); + }); + + test('フォローしているユーザーのノートが含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi' }); + const carolNote = await post(carol, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('フォローしているユーザーの visibility: followers なノートが含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); + const carolNote = await post(carol, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + 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('withReplies: false でフォローしているユーザーの他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('withReplies: true でフォローしているユーザーの他人へのDM返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('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(250); + const carolNote = await post(carol, { text: 'hi', visibility: 'followers' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('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(250); + const carolNote = await post(carol, { text: 'hi', visibility: 'followers' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + 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('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(250); + 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('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + }); + + test('withReplies: false でフォローしているユーザーのそのユーザー自身への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); + }); + + test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('自分の他人への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi' }); + const aliceNote = await post(alice, { text: 'hi', replyId: bobNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + }); + + test('フォローしているユーザーの他人の投稿のリノートが含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { renoteId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { renoteId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { + withRenotes: false, + }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('[withRenotes: false] フォローしているユーザーの他人の投稿の引用が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { + withRenotes: false, + }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('フォローしているユーザーの他人への visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('ミュートしているユーザーのノートの、関係のないユーザによる引用ノートの、フォローしているユーザーによるリノートが含まれない', async () => { + const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id }); + const bobNote = await post(bob, { renoteId: daveNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、フォローしているユーザーによるリノートが含まれない', async () => { + const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id }); + const bobNote = await post(bob, { renoteId: daveNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('フォローしているリモートユーザーのノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: bob.id }, alice); + + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: bob.id }, alice); + + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + 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'), + ]); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { fileIds: [bobFile.id] }); + const carolNote1 = await post(carol, { text: 'hi' }); + const carolNote2 = await post(carol, { fileIds: [carolFile.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice); + + 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 * 30); + + test('フォローしているユーザーのチャンネル投稿が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('自分の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi'); + }); + + test('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi'); + }); + + test('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + const aliceNote = await post(alice, { text: 'ok', visibility: 'specified', visibleUserIds: [bob.id], replyId: bobNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'ok'); + }); + + /* TODO + test('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok'); + }); + */ + + // ↑の挙動が理想だけど実装が面倒かも + test('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('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(); + + if (enableFanoutTimeline) { + // 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); + } else { + assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0); + } + }); + + test('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); + }); + + test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', 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); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'yo' }); + const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + + await setTimeout(250); + + const res = await api('notes/timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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); + }); + }); + + describe('Local TL', () => { + test('visibility: home なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi', visibility: 'home' }); + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('他人の他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + }); + + test('他人のその人自身への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); + }); + + test('チャンネル投稿が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('リモートユーザーのノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + // 含まれても良いと思うけど実装が面倒なので含まれない + test('フォローしているユーザーの visibility: home なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi', visibility: 'home' }); + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('ミュートしているユーザーのノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('following/update', { userId: bob.id, withReplies: true }, alice); + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('ミュートしているユーザーのノートの、関係のないユーザによる引用ノートの、リノートが含まれない', async () => { + const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); + + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id }); + const bobNote = await post(bob, { renoteId: daveNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、リノートが含まれない', async () => { + const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); + + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id }); + const bobNote = await post(bob, { renoteId: daveNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + 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('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await setTimeout(250); + 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('[withReplies: true] 他人の他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('[withFiles: true] ファイル付きノートのみ含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { fileIds: [file.id] }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice); + + 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('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', 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); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'yo' }); + const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + + await setTimeout(250); + + 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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + }); + + describe('Social TL', () => { + test('ローカルユーザーのノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('ローカルユーザーの visibility: home なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('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(250); + 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('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(250); + 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('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(250); + 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('他人の他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + 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 => note.id === bobNote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + }); + + test('リモートユーザーのノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('フォローしているリモートユーザーのノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: bob.id }, alice); + + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: bob.id }, alice); + + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await setTimeout(250); + 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('[withReplies: true] 他人の他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('[withFiles: true] ファイル付きノートのみ含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { fileIds: [file.id] }); + + await waitForPushToTl(); + + const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice); + + 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('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', 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); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'yo' }); + const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + + await setTimeout(250); + + const res = await api('notes/hybrid-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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + }); + + describe('User List TL', () => { + test('リスインしているフォローしていないユーザーのノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); + }); + + test('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice); + await setTimeout(250); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi'); + }); + + test('リスインしている自分の visibility: followers なノートが含まれる', async () => { + const [alice] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: alice.id }, alice); + await setTimeout(250); + const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi'); + }); + + test('リスインしているユーザーのチャンネルノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { fileIds: [file.id] }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice); + + 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('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi'); + }); + + test('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'yo' }); + const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + + await setTimeout(250); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + assert.strictEqual(res.body.length, 1); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + }); + + describe('User TL', () => { + test('ノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('フォローしていないユーザーの visibility: followers なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('フォローしているユーザーの visibility: followers なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi'); + }); + + test('自身の visibility: followers なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: alice.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi'); + }); + + test('チャンネル投稿が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('[withReplies: false] 他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false); + }); + + test('[withReplies: true] 他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); + }); + + test('[withReplies: true] 他人への visibility: specified な返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false); + }); + + test('[withFiles: true] ファイル付きノートのみ含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); + const bobNote1 = await post(bob, { text: 'hi' }); + const bobNote2 = await post(bob, { fileIds: [file.id] }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice); + + 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('[withChannelNotes: true] チャンネル投稿が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => { + const [bob] = await Promise.all([signup()]); + + const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + }); + + test('ミュートしているユーザーに関連する投稿が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('ミュートしているユーザーのノートの、関係のないユーザによる引用ノートの、リノートが含まれない', async () => { + const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); + + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id }); + const bobNote = await post(bob, { renoteId: daveNote.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、リノートが含まれない', async () => { + const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await api('mute/create', { userId: carol.id }, alice); + await setTimeout(250); + const carolNote = await post(carol, { text: 'hi' }); + const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id }); + const bobNote = await post(bob, { renoteId: daveNote.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + test('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('mute/create', { userId: bob.id }, alice); + await setTimeout(250); + 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 }); + const bobNote4 = await post(bob, { renoteId: bobNote2.id }); + const bobNote5 = await post(bob, { renoteId: bobNote3.id }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id }, alice); + + 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); + assert.strictEqual(res.body.some(note => note.id === bobNote4.id), true); + assert.strictEqual(res.body.some(note => note.id === bobNote5.id), true); + }); + + test('自身の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + }); + + test('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); + }); + + /** @see https://github.com/misskey-dev/misskey/issues/14000 */ + test('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('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: リノートミュート済みユーザーのテスト + // TODO: ページネーションのテスト }); - - describe('Home TL', () => { - test.concurrent('自分の visibility: followers なノートが含まれる', async () => { - const [alice] = await Promise.all([signup()]); - - const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi' }); - const carolNote = await post(carol, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); - const carolNote = await post(carol, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('following/update', { userId: bob.id, withReplies: true }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('following/update', { userId: bob.id, withReplies: true }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - 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/timeline', { limit: 100 }, alice); - - 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 () => { - 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/timeline', { limit: 100 }, alice); - - 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: 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()]); - - 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 setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified', visibleUserIds: [carolNote.id] }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - 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/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('自分の他人への返信が含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi' }); - const aliceNote = await post(alice, { text: 'hi', replyId: bobNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { renoteId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { renoteId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { - withRenotes: false, - }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { - withRenotes: false, - }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('following/update', { userId: bob.id, withReplies: true }, alice); - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id }); - const bobNote = await post(bob, { renoteId: daveNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); - assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、フォローしているユーザーによるリノートが含まれない', async () => { - const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id }); - const bobNote = await post(bob, { renoteId: daveNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); - assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); - - await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('following/create', { userId: bob.id }, alice); - - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); - - await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('following/create', { userId: bob.id }, alice); - - const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - 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'), - ]); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { fileIds: [bobFile.id] }); - const carolNote1 = await post(carol, { text: 'hi' }); - const carolNote2 = await post(carol, { fileIds: [carolFile.id] }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice); - - 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 * 30); - - test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('自分の visibility: specified なノートが含まれる', async () => { - const [alice] = await Promise.all([signup()]); - - const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); - const aliceNote = await post(alice, { text: 'ok', visibility: 'specified', visibleUserIds: [bob.id], replyId: bobNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); - assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'ok'); - }); - - /* TODO - test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); - const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok'); - }); - */ - - // ↑の挙動が理想だけど実装が面倒かも - test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); - const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/timeline', { limit: 100 }, alice); - - 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', () => { - test.concurrent('visibility: home なノートが含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const carolNote = await post(carol, { text: 'hi', visibility: 'home' }); - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); - const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('リモートユーザーのノートが含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); - - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - // 含まれても良いと思うけど実装が面倒なので含まれない - test.concurrent('フォローしているユーザーの visibility: home なノートが含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi', visibility: 'home' }); - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('following/update', { userId: bob.id, withReplies: true }, alice); - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - 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 () => { - const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id }); - const bobNote = await post(bob, { renoteId: daveNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); - assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、リノートが含まれない', async () => { - const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id }); - const bobNote = await post(bob, { renoteId: daveNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); - assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - 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: 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()]); - - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { fileIds: [file.id] }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice); - - 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', () => { - test.concurrent('ローカルユーザーのノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('ローカルユーザーの visibility: home なノートが含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); - - await waitForPushToTl(); - - const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); - - await waitForPushToTl(); - - const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); - - 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 api('following/create', { userId: bob.id }, alice); - 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/hybrid-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 でフォローしているユーザーの他人の 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()]); - - const carolNote = await post(carol, { text: 'hi' }); - 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 => note.id === bobNote.id), false); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); - }); - - test.concurrent('リモートユーザーのノートが含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); - - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/local-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); - - await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('following/create', { userId: bob.id }, alice); - - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); - - await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('following/create', { userId: bob.id }, alice); - - const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); - - await waitForPushToTl(); - - const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); - - 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()]); - - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { fileIds: [file.id] }); - - await waitForPushToTl(); - - const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice); - - 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', () => { - test.concurrent('リスインしているフォローしていないユーザーのノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { text: 'hi', replyId: bobNote1.id }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice); - 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/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: false }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('users/lists/update-membership', { listId: list.id, userId: bob.id, withReplies: true }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'home' }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - 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 () => { - const [alice] = await Promise.all([signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: alice.id }, alice); - await setTimeout(1000); - const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { fileIds: [file.id] }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice); - - 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()]); - - 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 setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); - - await waitForPushToTl(); - - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - }); - - describe('User TL', () => { - test.concurrent('ノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi' }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('フォローしていないユーザーの visibility: followers なノートが含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await setTimeout(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id }, alice); - - 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 () => { - const [alice] = await Promise.all([signup()]); - - const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: alice.id }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); - const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('[withReplies: false] 他人への返信が含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const carolNote = await post(carol, { text: 'hi' }); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const carolNote = await post(carol, { text: 'hi' }); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); - - 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 () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - const carolNote = await post(carol, { text: 'hi' }); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { text: 'hi', replyId: carolNote.id, visibility: 'specified' }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); - - 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 () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'); - const bobNote1 = await post(bob, { text: 'hi' }); - const bobNote2 = await post(bob, { fileIds: [file.id] }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice); - - 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()]); - - const channel = await api('channels/create', { name: 'channel' }, bob).then(x => x.body); - const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); - const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => { - const [bob] = await Promise.all([signup()]); - - const channel = await api('channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); - const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - }); - - test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const bobNote = await post(bob, { text: 'hi', renoteId: carolNote.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによる引用ノートの、リノートが含まれない', async () => { - const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const daveNote = await post(dave, { text: 'quote hi', renoteId: carolNote.id }); - const bobNote = await post(bob, { renoteId: daveNote.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('ミュートしているユーザーのノートの、関係のないユーザによるリプライの、リノートが含まれない', async () => { - const [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - - await api('following/create', { userId: bob.id }, alice); - await api('mute/create', { userId: carol.id }, alice); - await setTimeout(1000); - const carolNote = await post(carol, { text: 'hi' }); - const daveNote = await post(dave, { text: 'quote hi', replyId: carolNote.id }); - const bobNote = await post(bob, { renoteId: daveNote.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, limit: 100 }, alice); - - assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); - }); - - test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - await api('mute/create', { userId: bob.id }, alice); - 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 }); - const bobNote4 = await post(bob, { renoteId: bobNote2.id }); - const bobNote5 = await post(bob, { renoteId: bobNote3.id }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id }, alice); - - 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); - assert.strictEqual(res.body.some(note => note.id === bobNote4.id), true); - assert.strictEqual(res.body.some(note => note.id === bobNote5.id), true); - }); - - test.concurrent('自身の visibility: specified なノートが含まれる', async () => { - const [alice] = await Promise.all([signup()]); - - const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice); - - assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); - }); - - test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const bobNote = await post(bob, { text: 'hi', visibility: 'specified' }); - - await waitForPushToTl(); - - const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); - - assert.strictEqual(res.body.some(note => 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: リノートミュート済みユーザーのテスト - // TODO: ページネーションのテスト }); From 479525a71df1b0125bc317367bfd73b3c2cfa27f Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 14 Jul 2025 14:21:23 +0900 Subject: [PATCH 18/85] clean up --- packages/backend/test/e2e/timelines.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 237b5885c5..a4f255abac 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -25,7 +25,6 @@ describe('Timelines', () => { beforeAll(async () => { redisForTimelines = new Redis(loadConfig().redisForTimelines); root = await signup({ username: 'root' }); - //console.log(await api('admin/update-meta', { enableFanoutTimeline }, root)); }, 1000 * 60 * 2); describe.each([ @@ -37,7 +36,7 @@ describe('Timelines', () => { } beforeAll(async () => { - console.log(await api('admin/update-meta', { enableFanoutTimeline }, root)); + await api('admin/update-meta', { enableFanoutTimeline }, root); }, 1000 * 60 * 2); describe('Home TL', () => { From 6ad3f07d485b18db71244c386386a82643ffaf33 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 14 Jul 2025 14:36:07 +0900 Subject: [PATCH 19/85] revert --- packages/backend/src/core/FanoutTimelineService.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/backend/src/core/FanoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts index 3d1f571066..b00814219b 100644 --- a/packages/backend/src/core/FanoutTimelineService.ts +++ b/packages/backend/src/core/FanoutTimelineService.ts @@ -113,17 +113,4 @@ export class FanoutTimelineService { public purge(name: FanoutTimelineName) { return this.redisForTimelines.del('list:' + name); } - - @bindThis - public purgeByUserIds(userIds: MiUser['id'][]) { - return Promise.all(userIds.flatMap(userId => [ - this.purge(`homeTimeline:${userId}`), - this.purge(`homeTimelineWithFiles:${userId}`), - this.purge(`localTimelineWithReplyTo:${userId}`), - this.purge(`userTimeline:${userId}`), - this.purge(`userTimelineWithFiles:${userId}`), - this.purge(`userTimelineWithReplies:${userId}`), - this.purge(`userTimelineWithChannel:${userId}`), - ])); - } } From 16cb881fa289891b21528fbaa33fb6c44f863d84 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 14 Jul 2025 14:36:32 +0900 Subject: [PATCH 20/85] clean up --- packages/backend/src/core/FanoutTimelineService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/core/FanoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts index b00814219b..24999bf4da 100644 --- a/packages/backend/src/core/FanoutTimelineService.ts +++ b/packages/backend/src/core/FanoutTimelineService.ts @@ -8,7 +8,6 @@ import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; -import { MiUser } from '@/models/User.js'; export type FanoutTimelineName = ( // home timeline From 08cc5a99bb026a9e9f308254ae814f1b9a2f620d Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 15 Jul 2025 09:20:48 +0900 Subject: [PATCH 21/85] Don't remove notes when reply / renote is removed (#16287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: make NO ACTION on channel/reply/renote removal * chore(docs): add description to show a possibility of reply null with replyId non-null * fix: packing NoteDraft fails when reply / renote is removed * feat: show drafts targeting removed renote / reply as "削除された投稿への投稿" --- ...52502434151-no-action-on-draft-relation.js | 24 +++++++++++++++++++ .../core/entities/NoteDraftEntityService.ts | 20 ++++++++++++---- packages/backend/src/models/NoteDraft.ts | 10 +++++--- .../src/models/json-schema/note-draft.ts | 2 ++ .../src/components/MkNoteDraftsDialog.vue | 14 +++++++++++ packages/misskey-js/src/autogen/types.ts | 2 ++ 6 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 packages/backend/migration/1752502434151-no-action-on-draft-relation.js diff --git a/packages/backend/migration/1752502434151-no-action-on-draft-relation.js b/packages/backend/migration/1752502434151-no-action-on-draft-relation.js new file mode 100644 index 0000000000..e3c63b79c7 --- /dev/null +++ b/packages/backend/migration/1752502434151-no-action-on-draft-relation.js @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class NoActionOnDraftRelation1752502434151 { + name = 'NoActionOnDraftRelation1752502434151' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_CHANNEL_ID"`); + await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_USER_ID"`); + await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_RENOTE_ID"`); + await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_REPLY_ID"`); + await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_e4983f28b4b18b03491536052f5" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_e4983f28b4b18b03491536052f5"`); + await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_REPLY_ID" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_RENOTE_ID" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_USER_ID" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_CHANNEL_ID" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } +} diff --git a/packages/backend/src/core/entities/NoteDraftEntityService.ts b/packages/backend/src/core/entities/NoteDraftEntityService.ts index 26455029d5..3ef8cdaa12 100644 --- a/packages/backend/src/core/entities/NoteDraftEntityService.ts +++ b/packages/backend/src/core/entities/NoteDraftEntityService.ts @@ -5,6 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; +import { EntityNotFoundError } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Packed } from '@/misc/json-schema.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; @@ -90,6 +91,17 @@ export class NoteDraftEntityService implements OnModuleInit { const packedFiles = options?._hint_?.packedFiles; const packedUsers = options?._hint_?.packedUsers; + async function nullIfEntityNotFound(promise: Promise): Promise { + try { + return await promise; + } catch (err) { + if (err instanceof EntityNotFoundError) { + return null; + } + throw err; + } + } + const packed: Packed<'NoteDraft'> = await awaitAll({ id: noteDraft.id, createdAt: this.idService.parse(noteDraft.id).date.toISOString(), @@ -117,15 +129,15 @@ export class NoteDraftEntityService implements OnModuleInit { } : undefined, ...(opts.detail ? { - reply: noteDraft.replyId ? this.noteEntityService.pack(noteDraft.replyId, me, { + reply: noteDraft.replyId ? nullIfEntityNotFound(this.noteEntityService.pack(noteDraft.replyId, me, { detail: false, skipHide: opts.skipHide, - }) : undefined, + })) : undefined, - renote: noteDraft.renoteId ? this.noteEntityService.pack(noteDraft.renoteId, me, { + renote: noteDraft.renoteId ? nullIfEntityNotFound(this.noteEntityService.pack(noteDraft.renoteId, me, { detail: true, skipHide: opts.skipHide, - }) : undefined, + })) : undefined, poll: noteDraft.hasPoll ? { choices: noteDraft.pollChoices, diff --git a/packages/backend/src/models/NoteDraft.ts b/packages/backend/src/models/NoteDraft.ts index edae254bb8..39d85e53dc 100644 --- a/packages/backend/src/models/NoteDraft.ts +++ b/packages/backend/src/models/NoteDraft.ts @@ -24,8 +24,9 @@ export class MiNoteDraft { }) public replyId: MiNote['id'] | null; + // There is a possibility that replyId is not null but reply is null when the reply note is deleted. @ManyToOne(type => MiNote, { - onDelete: 'CASCADE', + createForeignKeyConstraints: false, }) @JoinColumn() public reply: MiNote | null; @@ -38,8 +39,9 @@ export class MiNoteDraft { }) public renoteId: MiNote['id'] | null; + // There is a possibility that renoteId is not null but renote is null when the renote note is deleted. @ManyToOne(type => MiNote, { - onDelete: 'CASCADE', + createForeignKeyConstraints: false, }) @JoinColumn() public renote: MiNote | null; @@ -114,8 +116,10 @@ export class MiNoteDraft { }) public channelId: MiChannel['id'] | null; + // There is a possibility that channelId is not null but channel is null when the channel is deleted. + // (deleting channel is not implemented so it's not happening now but may happen in the future) @ManyToOne(type => MiChannel, { - onDelete: 'CASCADE', + createForeignKeyConstraints: false, }) @JoinColumn() public channel: MiChannel | null; diff --git a/packages/backend/src/models/json-schema/note-draft.ts b/packages/backend/src/models/json-schema/note-draft.ts index 20c56d0795..504b263a6d 100644 --- a/packages/backend/src/models/json-schema/note-draft.ts +++ b/packages/backend/src/models/json-schema/note-draft.ts @@ -51,11 +51,13 @@ export const packedNoteDraftSchema = { type: 'object', optional: true, nullable: true, ref: 'Note', + description: 'The reply target note contents if exists. If the reply target has been deleted since the draft was created, this will be null while replyId is not null.', }, renote: { type: 'object', optional: true, nullable: true, ref: 'Note', + description: 'The renote target note contents if exists. If the renote target has been deleted since the draft was created, this will be null while renoteId is not null.', }, visibility: { type: 'string', diff --git a/packages/frontend/src/components/MkNoteDraftsDialog.vue b/packages/frontend/src/components/MkNoteDraftsDialog.vue index 7d41740264..5b8211b715 100644 --- a/packages/frontend/src/components/MkNoteDraftsDialog.vue +++ b/packages/frontend/src/components/MkNoteDraftsDialog.vue @@ -42,6 +42,13 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+ + + +
+
+ + + +
{{ i18n.tsx._drafts.postTo({ channel: draft.channel.name }) }}
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 51e4b4f45d..7594117deb 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4401,7 +4401,9 @@ export type components = { * @example xxxxxxxxxx */ renoteId?: string | null; + /** @description The reply target note contents if exists. If the reply target has been deleted since the draft was created, this will be null while replyId is not null. */ reply?: components['schemas']['Note'] | null; + /** @description The renote target note contents if exists. If the renote target has been deleted since the draft was created, this will be null while renoteId is not null. */ renote?: components['schemas']['Note'] | null; /** @enum {string} */ visibility: 'public' | 'home' | 'followers' | 'specified'; From ad7bf096e1d858cddb097fb9b32bfc2387b63894 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 15 Jul 2025 09:32:46 +0900 Subject: [PATCH 22/85] =?UTF-8?q?enhance(backend):=20username=E3=81=AB?= =?UTF-8?q?=E5=AF=BE=E3=81=97=E3=81=A6=E3=82=82prohibitedWordsForNameOfUse?= =?UTF-8?q?r=E3=82=92=E9=81=A9=E7=94=A8=20(#16282)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance(backend): usernameに対してもprohibitedWordsForNameOfUserを適用 Resolve #16281 * fix locales/index/d.ts --- locales/index.d.ts | 2 +- locales/ja-JP.yml | 2 +- packages/backend/src/core/SignupService.ts | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index ceb3cc672d..8d757ff579 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5231,7 +5231,7 @@ export interface Locale extends ILocale { */ "prohibitedWordsForNameOfUser": string; /** - * このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。 + * このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。ユーザー名(username)に対しても全て小文字に置き換えて検査します。 */ "prohibitedWordsForNameOfUserDescription": string; /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 6af846f7ee..161edfe8bb 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1303,7 +1303,7 @@ messageToFollower: "フォロワーへのメッセージ" target: "対象" testCaptchaWarning: "CAPTCHAのテストを目的とした機能です。本番環境で使用しないでください。" prohibitedWordsForNameOfUser: "禁止ワード(ユーザーの名前)" -prohibitedWordsForNameOfUserDescription: "このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。" +prohibitedWordsForNameOfUserDescription: "このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。ユーザー名(username)に対しても全て小文字に置き換えて検査します。" yourNameContainsProhibitedWords: "変更しようとした名前に禁止された文字列が含まれています" yourNameContainsProhibitedWordsDescription: "名前に禁止されている文字列が含まれています。この名前を使用したい場合は、サーバー管理者にお問い合わせください。" thisContentsAreMarkedAsSigninRequiredByAuthor: "投稿者により、表示にはログインが必要と設定されています" diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 5462cb0b13..a85da62b86 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -93,6 +93,11 @@ export class SignupService { if (isPreserved) { throw new Error('USED_USERNAME'); } + + const hasProhibitedWords = this.utilityService.isKeyWordIncluded(username.toLowerCase(), this.meta.prohibitedWordsForNameOfUser); + if (hasProhibitedWords) { + throw new Error('USED_USERNAME'); + } } const keyPair = await new Promise((res, rej) => From c5928980f8c400fb372bf25b0c25648de12df77d Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 15 Jul 2025 10:13:15 +0900 Subject: [PATCH 23/85] skip test about reply --- packages/backend/test/e2e/timelines.ts | 30 ++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index a4f255abac..e3516845f4 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -103,6 +103,8 @@ describe('Timelines', () => { }); test('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -155,6 +157,8 @@ describe('Timelines', () => { }); test('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -175,6 +179,8 @@ describe('Timelines', () => { }); test('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -227,6 +233,8 @@ describe('Timelines', () => { }); test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -243,6 +251,8 @@ describe('Timelines', () => { }); test('自分の他人への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); const bobNote = await post(bob, { text: 'hi' }); @@ -519,6 +529,8 @@ describe('Timelines', () => { }); test('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); @@ -535,14 +547,10 @@ describe('Timelines', () => { /* TODO test('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); - const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); - await waitForPushToTl(); - const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok'); }); @@ -800,6 +808,8 @@ describe('Timelines', () => { }); test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -816,6 +826,8 @@ describe('Timelines', () => { }); test('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); await setTimeout(250); @@ -920,6 +932,8 @@ describe('Timelines', () => { }); test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -954,6 +968,8 @@ describe('Timelines', () => { }); test('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -974,6 +990,8 @@ describe('Timelines', () => { }); test('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); await api('following/create', { userId: bob.id }, alice); @@ -1048,6 +1066,8 @@ describe('Timelines', () => { }); test('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob] = await Promise.all([signup(), signup()]); await setTimeout(250); @@ -1446,6 +1466,8 @@ describe('Timelines', () => { }); test('[withReplies: false] 他人への返信が含まれない', async () => { + /* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return; + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); const carolNote = await post(carol, { text: 'hi' }); From 26e6c148cb10e33f4d5039b1f00e2bbe4788d8bd Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 15 Jul 2025 12:01:41 +0900 Subject: [PATCH 24/85] Fix #16289 --- .../server/api/endpoints/notes/timeline.ts | 8 +++- packages/backend/test/e2e/timelines.ts | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index c76cca1518..1f3631ae3d 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -237,7 +237,13 @@ export default class extends Endpoint { // eslint- } if (ps.withRenotes === false) { - query.andWhere('note.renoteId IS NULL'); + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); } //#endregion diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index e3516845f4..f9ad66c119 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -282,6 +282,48 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); }); + test('[withRenotes: false] フォローしているユーザーの投稿が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + const bobNote = await post(bob, { text: 'hi' }); + const carolNote = await post(carol, { text: 'hi' }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { + limit: 100, + withRenotes: false, + }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('[withRenotes: false] フォローしているユーザーのファイルのみの投稿が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('following/create', { userId: bob.id }, alice); + await setTimeout(250); + 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'), + ]); + const bobNote = await post(bob, { fileIds: [bobFile.id] }); + const carolNote = await post(carol, { fileIds: [carolFile.id] }); + + await waitForPushToTl(); + + const res = await api('notes/timeline', { + limit: 100, + withRenotes: false, + }, alice); + + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + test('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); From 6f6fdfe28e3638e09ee1452fd2a8bb4271e1d0ce Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Wed, 16 Jul 2025 15:49:05 +0900 Subject: [PATCH 25/85] Migration cleanup (#16288) * chore: apply several @Index and @ManyToOne to match actual migration code * chore: several decorator updates with typeorm bug workaround with patches * feat: add final cleanup migration * dev: add .editorconfig settings for generated migrations * chore: update dockerfile to build package with patches * chore: update federation test compose to include patches * chore: revert few dependency update * chore: don't check disableRegistration on test env * test: add test for checking migration script * chore: set proxyRemoteFiles true in test config * chore: enter invitation code in signup test * fix: register send button is not disabled when invitationCode is not input --- .editorconfig | 4 + .github/misskey/test.yml | 2 + .github/workflows/test-backend.yml | 44 +++++++++++ Dockerfile | 2 + cypress/e2e/basic.cy.ts | 2 + package.json | 3 + .../1752509043847-migration-cleanup.js | 79 +++++++++++++++++++ packages/backend/package.json | 1 + .../backend/scripts/check_migrations_clean.js | 26 ++++++ .../AbuseReportNotificationRecipient.ts | 16 ++-- packages/backend/src/models/Emoji.ts | 2 + packages/backend/src/models/Meta.ts | 7 +- packages/backend/src/models/Note.ts | 3 +- packages/backend/src/models/NoteDraft.ts | 10 ++- packages/backend/src/models/UserProfile.ts | 2 +- .../src/server/api/SignupApiService.ts | 3 +- .../backend/test-federation/compose.tpl.yml | 4 + packages/backend/test-federation/compose.yml | 8 ++ .../src/components/MkSignupDialog.form.vue | 3 +- patches/typeorm.patch | 17 ++++ pnpm-lock.yaml | 9 ++- 21 files changed, 228 insertions(+), 19 deletions(-) create mode 100644 packages/backend/migration/1752509043847-migration-cleanup.js create mode 100644 packages/backend/scripts/check_migrations_clean.js create mode 100644 patches/typeorm.patch diff --git a/.editorconfig b/.editorconfig index def7baa1a8..ccf388f06e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,3 +13,7 @@ trim_trailing_whitespace = false [*.{yml,yaml}] indent_style = space + +[packages/backend/migration/*.js] +indent_style = space +indent_size = 4 diff --git a/.github/misskey/test.yml b/.github/misskey/test.yml index 3c807e8b9e..513bfb1ac0 100644 --- a/.github/misskey/test.yml +++ b/.github/misskey/test.yml @@ -15,3 +15,5 @@ redis: host: 127.0.0.1 port: 56312 id: aidx + +proxyRemoteFiles: true diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 0c40f2c52a..e4b139ef63 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -152,3 +152,47 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} files: ./packages/backend/coverage/coverage-final.json + + migration: + name: Migration tests (backend) + runs-on: ubuntu-latest + strategy: + matrix: + node-version-file: + - .node-version + #- .github/min.node-version + + services: + postgres: + image: postgres:15 + ports: + - 54312:5432 + env: + POSTGRES_DB: test-misskey + POSTGRES_HOST_AUTH_METHOD: trust + + steps: + - uses: actions/checkout@v4.2.2 + with: + submodules: true + - name: Setup pnpm + uses: pnpm/action-setup@v4.1.0 + - name: Get current date + id: current-date + run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + - name: Use Node.js + uses: actions/setup-node@v4.4.0 + with: + node-version-file: ${{ matrix.node-version-file }} + cache: 'pnpm' + - run: pnpm i --frozen-lockfile + - name: Check pnpm-lock.yaml + run: git diff --exit-code pnpm-lock.yaml + - name: Copy Configure + run: cp .github/misskey/test.yml .config + - name: Build + run: pnpm build + - name: Run migrations + run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend migrate + - name: Check no migrations are remaining + run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend check-migrations diff --git a/Dockerfile b/Dockerfile index 77277db8cb..62b737c084 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ WORKDIR /misskey COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] COPY --link ["scripts", "./scripts"] +COPY --link ["patches", "./patches"] COPY --link ["packages/backend/package.json", "./packages/backend/"] COPY --link ["packages/frontend-shared/package.json", "./packages/frontend-shared/"] COPY --link ["packages/frontend/package.json", "./packages/frontend/"] @@ -53,6 +54,7 @@ WORKDIR /misskey COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] COPY --link ["scripts", "./scripts"] +COPY --link ["patches", "./patches"] COPY --link ["packages/backend/package.json", "./packages/backend/"] COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"] COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"] diff --git a/cypress/e2e/basic.cy.ts b/cypress/e2e/basic.cy.ts index 2ce39737ed..4f2f700146 100644 --- a/cypress/e2e/basic.cy.ts +++ b/cypress/e2e/basic.cy.ts @@ -78,6 +78,8 @@ describe('After setup instance', () => { cy.get('[data-cy-signup-password] input').type('alice1234'); cy.get('[data-cy-signup-submit]').should('be.disabled'); cy.get('[data-cy-signup-password-retype] input').type('alice1234'); + cy.get('[data-cy-signup-submit]').should('be.disabled'); + cy.get('[data-cy-signup-invitation-code] input').type('test-invitation-code'); cy.get('[data-cy-signup-submit]').should('not.be.disabled'); cy.get('[data-cy-signup-submit]').click(); diff --git a/package.json b/package.json index 72ccb3be67..b6516ee5e1 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,9 @@ "pnpm": { "overrides": { "@aiscript-dev/aiscript-languageserver": "-" + }, + "patchedDependencies": { + "typeorm": "patches/typeorm.patch" } } } diff --git a/packages/backend/migration/1752509043847-migration-cleanup.js b/packages/backend/migration/1752509043847-migration-cleanup.js new file mode 100644 index 0000000000..450e22af0c --- /dev/null +++ b/packages/backend/migration/1752509043847-migration-cleanup.js @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class MigrationCleanup1752509043847 { + name = 'MigrationCleanup1752509043847' + + async up(queryRunner) { + // 1745378064470-composite-note-index.js created a index ON "note" ("userId", "id" DESC) as IDX_724b311e6f883751f261ebe378 but should be named IDX_a6f649630f55af3888e5a42919 + await queryRunner.query(`ALTER INDEX "IDX_724b311e6f883751f261ebe378" RENAME TO "IDX_a6f649630f55af3888e5a42919"`); + + // 1713656541000-abuse-report-notification.js generated system_webhook with hand-written SQL with CURRENT_TIMESTAMP as the default value, but its representation in TypeORM is `now()` + // see https://github.com/typeorm/typeorm/blob/f351757a15b9d2bd9d4222c69dcfd2316f46b5d1/src/driver/postgres/PostgresDriver.ts#L1575 + await queryRunner.query(`ALTER TABLE "system_webhook" ALTER COLUMN "updatedAt" SET DEFAULT now()`); + + // 1702718871541-ffVisibility.js defined a enum type "user_profile_followersVisibility_enum" but it should be "user_profile_followersvisibility_enum" (lowercase 'v') in typeorm + await queryRunner.query(`ALTER TYPE "public"."user_profile_followersVisibility_enum" RENAME TO "user_profile_followersvisibility_enum"`); + + // 1713656541000-abuse-report-notification.js generated abuse_report_notification_recipient with hand-written SQL with CURRENT_TIMESTAMP as the default value, but its representation in TypeORM is `now()` + await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "updatedAt" SET DEFAULT now()`); + + // 1690796169261-play-visibility.js added visibility column to flash table but it forgot to set NOT NULL constraint + await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" SET NOT NULL`); + + // 1736686850345-createNoteDraft.js created note_draft with hand-written SQL but several types and comments are not correctly defined + await queryRunner.query(`CREATE TYPE "public"."note_draft_visibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "id" SET DATA TYPE character varying(32)`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "replyId" SET DATA TYPE character varying(32)`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "renoteId" SET DATA TYPE character varying(32)`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "userId" SET DATA TYPE character varying(32)`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "channelId" SET DATA TYPE character varying(32)`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET DATA TYPE character varying(32) array`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET DATA TYPE character varying(32) array`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibility" SET DATA TYPE "public"."note_draft_visibility_enum" USING visibility::note_draft_visibility_enum`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."replyId" IS 'The ID of reply target.'`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."renoteId" IS 'The ID of renote target.'`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."userId" IS 'The ID of author.'`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."channelId" IS 'The ID of source channel.'`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "hasPoll" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollChoices" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollMultiple" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "localOnly" SET NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET NOT NULL`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "localOnly" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollMultiple" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollChoices" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "hasPoll" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" DROP NOT NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."channelId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."userId" IS 'The ID of author.'`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."renoteId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note_draft"."replyId" IS NULL`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibility" SET DATA TYPE varchar`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET DATA TYPE varchar[]`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET DATA TYPE varchar[]`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "channelId" SET DATA TYPE varchar`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "userId" SET DATA TYPE varchar`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "renoteId" SET DATA TYPE varchar`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "replyId" SET DATA TYPE varchar`); + await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "id" SET DATA TYPE varchar`); + await queryRunner.query(`DROP TYPE "public"."note_draft_visibility_enum"`); + + await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" DROP NOT NULL`); + + await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP`); + + await queryRunner.query(`ALTER TYPE "public"."user_profile_followersvisibility_enum" RENAME TO "user_profile_followersVisibility_enum"`); + + await queryRunner.query(`ALTER TABLE "system_webhook" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP`); + + await queryRunner.query(`ALTER INDEX "IDX_a6f649630f55af3888e5a42919" RENAME TO "IDX_724b311e6f883751f261ebe378"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 2173ce71a5..99482e71bc 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -33,6 +33,7 @@ "test:fed": "pnpm jest:fed", "test-and-coverage": "pnpm jest-and-coverage", "test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e", + "check-migrations": "node scripts/check_migrations_clean.js", "generate-api-json": "node ./scripts/generate_api_json.js" }, "optionalDependencies": { diff --git a/packages/backend/scripts/check_migrations_clean.js b/packages/backend/scripts/check_migrations_clean.js new file mode 100644 index 0000000000..ce67b1cd81 --- /dev/null +++ b/packages/backend/scripts/check_migrations_clean.js @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +// This script checks if the database migrations has been generated correctly. + +import dataSource from '../ormconfig.js'; + +await dataSource.initialize(); + +const sqlInMemory = await dataSource.driver.createSchemaBuilder().log(); + +if (sqlInMemory.upQueries.length > 0 || sqlInMemory.downQueries.length > 0) { + console.error('There are several pending migrations. Please make sure you have generated the migrations correctly, or configured entities class correctly.'); + for (const query of sqlInMemory.upQueries) { + console.error(`- ${query.query}`); + } + for (const query of sqlInMemory.downQueries) { + console.error(`- ${query.query}`); + } + process.exit(1); +} else { + console.log('All migrations are clean.'); + process.exit(0); +} diff --git a/packages/backend/src/models/AbuseReportNotificationRecipient.ts b/packages/backend/src/models/AbuseReportNotificationRecipient.ts index fbff880afc..17ec6abed5 100644 --- a/packages/backend/src/models/AbuseReportNotificationRecipient.ts +++ b/packages/backend/src/models/AbuseReportNotificationRecipient.ts @@ -22,7 +22,7 @@ export class MiAbuseReportNotificationRecipient { /** * 有効かどうか. */ - @Index() + @Index('IDX_abuse_report_notification_recipient_isActive') @Column('boolean', { default: true, }) @@ -47,7 +47,7 @@ export class MiAbuseReportNotificationRecipient { /** * 通知方法. */ - @Index() + @Index('IDX_abuse_report_notification_recipient_method') @Column('varchar', { length: 64, }) @@ -56,10 +56,11 @@ export class MiAbuseReportNotificationRecipient { /** * 通知先のユーザID. */ - @Index() + @Index('IDX_abuse_report_notification_recipient_userId') @Column({ ...id(), nullable: true, + default: null, }) public userId: MiUser['id'] | null; @@ -75,17 +76,20 @@ export class MiAbuseReportNotificationRecipient { /** * 通知先のユーザプロフィール. */ - @ManyToOne(type => MiUserProfile, {}) + @ManyToOne(type => MiUserProfile, { + onDelete: 'CASCADE', + }) @JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' }) public userProfile: MiUserProfile | null; /** * 通知先のシステムWebhookId. */ - @Index() + @Index('IDX_abuse_report_notification_recipient_systemWebhookId') @Column({ ...id(), nullable: true, + default: null, }) public systemWebhookId: string | null; @@ -95,6 +99,6 @@ export class MiAbuseReportNotificationRecipient { @ManyToOne(type => MiSystemWebhook, { onDelete: 'CASCADE', }) - @JoinColumn() + @JoinColumn({ name: 'systemWebhookId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_systemWebhookId' }) public systemWebhook: MiSystemWebhook | null; } diff --git a/packages/backend/src/models/Emoji.ts b/packages/backend/src/models/Emoji.ts index d62b6e9f6f..8dff8fd153 100644 --- a/packages/backend/src/models/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -8,6 +8,7 @@ import { id } from './util/id.js'; @Entity('emoji') @Index(['name', 'host'], { unique: true }) +@Index('IDX_EMOJI_ROLE_IDS', { synchronize: false }) // GIN for roleIdsThatCanBeUsedThisEmojiAsReaction in production export class MiEmoji { @PrimaryColumn(id()) public id: string; @@ -32,6 +33,7 @@ export class MiEmoji { @Column('varchar', { length: 128, nullable: true, }) + @Index('IDX_EMOJI_CATEGORY') public category: string | null; @Column('varchar', { diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 3ee6190d45..85c10ab666 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -59,7 +59,7 @@ export class MiMeta { public maintainerEmail: string | null; @Column('boolean', { - default: false, + default: true, }) public disableRegistration: boolean; @@ -570,7 +570,7 @@ export class MiMeta { public bannedEmailDomains: string[]; @Column('varchar', { - length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }', + length: 1024, array: true, default: ['admin', 'administrator', 'root', 'system', 'maintainer', 'host', 'mod', 'moderator', 'owner', 'superuser', 'staff', 'auth', 'i', 'me', 'everyone', 'all', 'mention', 'mentions', 'example', 'user', 'users', 'account', 'accounts', 'official', 'help', 'helps', 'support', 'supports', 'info', 'information', 'informations', 'announce', 'announces', 'announcement', 'announcements', 'notice', 'notification', 'notifications', 'dev', 'developer', 'developers', 'tech', 'misskey'], }) public preservedUsernames: string[]; @@ -635,7 +635,7 @@ export class MiMeta { public urlPreviewMaximumContentLength: number; @Column('boolean', { - default: true, + default: false, }) public urlPreviewRequireContentLength: boolean; @@ -648,6 +648,7 @@ export class MiMeta { @Column('varchar', { length: 1024, nullable: true, + default: null, }) public urlPreviewUserAgent: string | null; diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 0560ee17c0..9822ec94e4 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -20,7 +20,8 @@ import type { MiDriveFile } from './DriveFile.js'; // You should not use `@Index({ concurrent: true })` decorator because database initialization for test will fail // because it will always run CREATE INDEX in transaction based on decorators. // Not appending `{ concurrent: true }` to `@Index` will not cause any problem in production, -@Index(['userId', 'id']) + +@Index(['userId', 'id']) // Note: this index is ("userId", "id" DESC) in production, but not in test. @Entity('note') export class MiNote { @PrimaryColumn(id()) diff --git a/packages/backend/src/models/NoteDraft.ts b/packages/backend/src/models/NoteDraft.ts index 39d85e53dc..6483748bc2 100644 --- a/packages/backend/src/models/NoteDraft.ts +++ b/packages/backend/src/models/NoteDraft.ts @@ -12,11 +12,13 @@ import { MiNote } from './Note.js'; import type { MiDriveFile } from './DriveFile.js'; @Entity('note_draft') +@Index('IDX_NOTE_DRAFT_FILE_IDS', { synchronize: false }) // GIN for fileIds in production +@Index('IDX_NOTE_DRAFT_VISIBLE_USER_IDS', { synchronize: false }) // GIN for visibleUserIds in production export class MiNoteDraft { @PrimaryColumn(id()) public id: string; - @Index() + @Index('IDX_NOTE_DRAFT_REPLY_ID') @Column({ ...id(), nullable: true, @@ -31,7 +33,7 @@ export class MiNoteDraft { @JoinColumn() public reply: MiNote | null; - @Index() + @Index('IDX_NOTE_DRAFT_RENOTE_ID') @Column({ ...id(), nullable: true, @@ -57,7 +59,7 @@ export class MiNoteDraft { }) public cw: string | null; - @Index() + @Index('IDX_NOTE_DRAFT_USER_ID') @Column({ ...id(), comment: 'The ID of author.', @@ -108,7 +110,7 @@ export class MiNoteDraft { }) public hashtag: string | null; - @Index() + @Index('IDX_NOTE_DRAFT_CHANNEL_ID') @Column({ ...id(), nullable: true, diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index c4c1fa5ec9..501b539210 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -29,7 +29,7 @@ export class MiUserProfile { }) public location: string | null; - @Index() + // Note: There's index named IDX_de22cd2b445eee31ae51cdbe99 for SUBSTR("birthday", 6, 5) @Column('char', { length: 10, nullable: true, comment: 'The birthday (YYYY-MM-DD) of the User.', diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 3ec5e5d3e6..53336a087d 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -129,7 +129,8 @@ export class SignupApiService { let ticket: MiRegistrationTicket | null = null; - if (this.meta.disableRegistration) { + // テスト時はこの機構は障害となるため無効にする + if (process.env.NODE_ENV !== 'test' && this.meta.disableRegistration) { if (invitationCode == null || typeof invitationCode !== 'string') { reply.code(400); return; diff --git a/packages/backend/test-federation/compose.tpl.yml b/packages/backend/test-federation/compose.tpl.yml index e4483acd7a..3d2ed21337 100644 --- a/packages/backend/test-federation/compose.tpl.yml +++ b/packages/backend/test-federation/compose.tpl.yml @@ -74,6 +74,10 @@ services: source: ../../../pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml read_only: true + - type: bind + source: ../../../patches + target: /misskey/patches + read_only: true - type: bind source: ./certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml index bd0ac15a31..330cc33854 100644 --- a/packages/backend/test-federation/compose.yml +++ b/packages/backend/test-federation/compose.yml @@ -74,6 +74,10 @@ services: source: ../../../pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml read_only: true + - type: bind + source: ../../../patches + target: /misskey/patches + read_only: true - type: bind source: ./certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt @@ -118,6 +122,10 @@ services: source: ../../../pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml read_only: true + - type: bind + source: ../../../patches + target: /misskey/patches + read_only: true working_dir: /misskey command: > bash -c " diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index aebec7a8f6..0f8713d4af 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- + @@ -138,6 +138,7 @@ const shouldDisableSubmitting = computed((): boolean => { instance.enableTurnstile && !turnstileResponse.value || instance.enableTestcaptcha && !testcaptchaResponse.value || instance.emailRequiredForSignup && emailState.value !== 'ok' || + instance.disableRegistration && invitationCode.value === '' || usernameState.value !== 'ok' || passwordRetypeState.value !== 'match'; }); diff --git a/patches/typeorm.patch b/patches/typeorm.patch new file mode 100644 index 0000000000..d5b4323781 --- /dev/null +++ b/patches/typeorm.patch @@ -0,0 +1,17 @@ +diff --git a/driver/postgres/PostgresDriver.js b/driver/postgres/PostgresDriver.js +index 278f29c1f3deec4939bb4ed90e6edae167f704e0..9a84c3098dda915d6c33e24d925a8fa09af9095e 100644 +--- a/driver/postgres/PostgresDriver.js ++++ b/driver/postgres/PostgresDriver.js +@@ -785,10 +785,10 @@ class PostgresDriver { + const tableColumnDefault = typeof tableColumn.default === "string" + ? JSON.parse(tableColumn.default.substring(1, tableColumn.default.length - 1)) + : tableColumn.default; +- return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault); ++ return OrmUtils_1.OrmUtils.deepCompare(columnMetadata.default, tableColumnDefault ?? null); + } + const columnDefault = this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata)); +- return columnDefault === tableColumn.default; ++ return columnDefault === tableColumn.default || columnDefault === undefined && tableColumn.default.toLowerCase() === 'null'; + } + /** + * Normalizes "isUnique" value of the column. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2bb00d45cd..4cce64a0b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,11 @@ overrides: lodash: 4.17.21 '@aiscript-dev/aiscript-languageserver': '-' +patchedDependencies: + typeorm: + hash: 2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56 + path: patches/typeorm.patch + importers: .: @@ -422,7 +427,7 @@ importers: version: 4.2.0 typeorm: specifier: 0.3.24 - version: 0.3.24(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2) + version: 0.3.24(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2) typescript: specifier: 5.8.3 version: 5.8.3 @@ -21835,7 +21840,7 @@ snapshots: typedarray@0.0.6: {} - typeorm@0.3.24(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2): + typeorm@0.3.24(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2): dependencies: '@sqltools/formatter': 1.2.5 ansis: 3.17.0 From e1834beae8b74876f6056f7be9b356142cbb80f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 16 Jul 2025 06:52:02 +0000 Subject: [PATCH 26/85] Bump version to 2025.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 b6516ee5e1..182ea74cff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.7.0-beta.1", + "version": "2025.7.0-beta.2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index f13ab7594f..747225af2f 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.7.0-beta.1", + "version": "2025.7.0-beta.2", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From f5d4b2744ba39e906365ada2a58c6acf07c4123e Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 18:36:43 +0900 Subject: [PATCH 27/85] fix migration --- .../migration/1752410859370-FollowingIsFollowerSuspended.js | 3 ++- ...410900000-FollowingIsFollowerSuspendedCopySuspendedState.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js b/packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js index 8542bcdd8b..ce63c16fb7 100644 --- a/packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js +++ b/packages/backend/migration/1752410859370-FollowingIsFollowerSuspended.js @@ -2,7 +2,8 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -module.exports = class FollowingIsFollowerSuspended1752410859370 { + +export class FollowingIsFollowerSuspended1752410859370 { name = 'FollowingIsFollowerSuspended1752410859370' async up(queryRunner) { diff --git a/packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js b/packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js index 4620442a13..185d8cbe1a 100644 --- a/packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js +++ b/packages/backend/migration/1752410900000-FollowingIsFollowerSuspendedCopySuspendedState.js @@ -2,7 +2,8 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -module.exports = class FollowingIsFollowerSuspendedCopySuspendedState1752410900000 { + +export class FollowingIsFollowerSuspendedCopySuspendedState1752410900000 { name = 'FollowingIsFollowerSuspendedCopySuspendedState1752410900000' async up(queryRunner) { From 85f4730308a016cd48dcaf60127caa80653a126e Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 19:10:20 +0900 Subject: [PATCH 28/85] update test --- packages/backend/test/e2e/timelines.ts | 161 +++++++++++++----- .../backend/test/unit/UserSuspendService.ts | 30 ++-- 2 files changed, 130 insertions(+), 61 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index f9ad66c119..757d86dbec 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -11,7 +11,7 @@ import { setTimeout } from 'node:timers/promises'; import { Redis } from 'ioredis'; import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl, role } from '../utils.js'; import { loadConfig } from '@/config.js'; -import { SignupResponse, Role } from 'misskey-js/entities.js'; +import { SignupResponse, Role, Note } from 'misskey-js/entities.js'; function genHost() { return randomString() + '.example.com'; @@ -654,26 +654,45 @@ describe('Timelines', () => { assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0); }); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', async () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); + }); - await setTimeout(250); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); - const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 2); - 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); + const res = await api('notes/timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 3); + 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), true); + assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa'); + }); }); }); @@ -912,25 +931,43 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', async () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); + }); - await setTimeout(250); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); - 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); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + const res = await api('notes/local-timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 3); + 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), true); + assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa'); + }); }); }); @@ -1152,25 +1189,61 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', async () => { + /* + * bob = 未フォローのローカルユーザー (凍結対象でない) + * carol = 未フォローのローカルユーザー (凍結対象) + * dave = フォローしているローカルユーザー (凍結対象) + * elle = フォローしているリモートユーザー (凍結対象) + */ + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse, dave: SignupResponse, elle: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note, daveNote: Note, elleNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol, dave, elle] = await Promise.all([signup(), signup(), signup(), signup(), signup({ host: genHost() })]); - await waitForPushToTl(); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + daveNote = await post(dave, { text: 'hello' }); + elleNote = await post(elle, { text: 'hi there' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await api('following/create', { userId: dave.id }, alice); + await api('following/create', { userId: elle.id }, alice); - await setTimeout(250); + await waitForPushToTl(); + }); - const res = await api('notes/hybrid-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); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + await api('admin/suspend-user', { userId: carol.id }, root); + await api('admin/suspend-user', { userId: dave.id }, root); + await api('admin/suspend-user', { userId: elle.id }, root); + await setTimeout(250); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); + assert.strictEqual(res.body.some(note => note.id === elleNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await api('admin/unsuspend-user', { userId: dave.id }, root); + await api('admin/unsuspend-user', { userId: elle.id }, root); + await setTimeout(250); + + const res = await api('notes/local-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 5); + 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), true); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), true); + assert.strictEqual(res.body.some(note => note.id === elleNote.id), true); + }); }); }); diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index 89edc6d116..ce8d35f408 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test'; import { jest } from '@jest/globals'; import { Test } from '@nestjs/testing'; -import { DataSource } from 'typeorm'; +import { setTimeout } from 'node:timers/promises'; import type { TestingModule } from '@nestjs/testing'; import { GlobalModule } from '@/GlobalModule.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; @@ -26,10 +26,6 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; -export async function sleep(ms = 250): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} - describe('UserSuspendService', () => { let app: TestingModule; let userSuspendService: UserSuspendService; @@ -169,7 +165,7 @@ describe('UserSuspendService', () => { await createFollowing(user, followee2); await userSuspendService.suspend(user, moderator); - await sleep(); + await setTimeout(250); // フォロー関係が論理削除されているかチェック const followings = await followingsRepository.find({ @@ -187,10 +183,10 @@ describe('UserSuspendService', () => { const moderator = await createUser(); await userSuspendService.suspend(user, moderator); - await sleep(); + await setTimeout(250); // 内部イベントが発行されているかチェック(非同期処理のため少し待つ) - await new Promise(resolve => setTimeout(resolve, 100)); + await setTimeout(100); expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith( 'userChangeSuspendedState', @@ -205,7 +201,7 @@ describe('UserSuspendService', () => { const moderator = await createUser(); await userSuspendService.unsuspend(user, moderator); - await sleep(); + await setTimeout(250); // ユーザーの凍結が解除されているかチェック const unsuspendedUser = await usersRepository.findOneBy({ id: user.id }); @@ -230,7 +226,7 @@ describe('UserSuspendService', () => { await createFollowing(user, followee2, { isFollowerSuspended: true }); await userSuspendService.unsuspend(user, moderator); - await sleep(); + await setTimeout(250); // フォロー関係が復元されているかチェック const followings = await followingsRepository.find({ @@ -248,10 +244,10 @@ describe('UserSuspendService', () => { const moderator = await createUser(); await userSuspendService.unsuspend(user, moderator); - await sleep(); + await setTimeout(250); // 内部イベントが発行されているかチェック(非同期処理のため少し待つ) - await new Promise(resolve => setTimeout(resolve, 100)); + await setTimeout(100); expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith( 'userChangeSuspendedState', @@ -282,7 +278,7 @@ describe('UserSuspendService', () => { // 凍結 await userSuspendService.suspend(user, moderator); - await sleep(); + await setTimeout(250); // 凍結後の状態確認 followings = await followingsRepository.find({ @@ -296,7 +292,7 @@ describe('UserSuspendService', () => { // 凍結解除 const suspendedUser = await usersRepository.findOneByOrFail({ id: user.id }); await userSuspendService.unsuspend(suspendedUser, moderator); - await sleep(); + await setTimeout(250); // 凍結解除後の状態確認 followings = await followingsRepository.find({ @@ -320,7 +316,7 @@ describe('UserSuspendService', () => { apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Delete' } as any); await userSuspendService.suspend(localUser, moderator); - await sleep(); + await setTimeout(250); // ActivityPub配信が呼ばれているかチェック expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser); @@ -339,7 +335,7 @@ describe('UserSuspendService', () => { apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Undo' } as any); await userSuspendService.unsuspend(localUser, moderator); - await sleep(); + await setTimeout(250); // ActivityPub配信が呼ばれているかチェック expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser); @@ -355,7 +351,7 @@ describe('UserSuspendService', () => { userEntityService.isLocalUser.mockReturnValue(false); await userSuspendService.suspend(remoteUser, moderator); - await sleep(); + await setTimeout(250); // ActivityPub配信が呼ばれていないことをチェック expect(userEntityService.isLocalUser).toHaveBeenCalledWith(remoteUser); From 5164a94e96edce6980c11faceaf657a522af6d03 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 19:10:40 +0900 Subject: [PATCH 29/85] clean up --- packages/backend/test/e2e/timelines.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index f9ad66c119..2c53dfff2e 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -19,7 +19,6 @@ function genHost() { let redisForTimelines: Redis; let root: SignupResponse; -let roleAdmin: Role; describe('Timelines', () => { beforeAll(async () => { From 90eccd4d9503954055cba0b2533349e06af9d7e7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 19:25:55 +0900 Subject: [PATCH 30/85] fix --- packages/backend/test/e2e/timelines.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index c22565ce75..642e176dbb 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -653,7 +653,7 @@ describe('Timelines', () => { assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0); }); - describe('凍結', async () => { + describe('凍結', () => { let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; let aliceNote: Note, bobNote: Note, carolNote: Note; @@ -930,7 +930,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - describe('凍結', async () => { + describe('凍結', () => { let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; let aliceNote: Note, bobNote: Note, carolNote: Note; @@ -1188,7 +1188,7 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - describe('凍結', async () => { + describe('凍結', () => { /* * bob = 未フォローのローカルユーザー (凍結対象でない) * carol = 未フォローのローカルユーザー (凍結対象) From 399e527cc22ad93b7e7d58bf3312cccd732af6a3 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 20:18:06 +0900 Subject: [PATCH 31/85] ??? --- .../backend/src/core/UserSuspendService.ts | 4 +- packages/backend/test/e2e/timelines.ts | 55 ++++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 601859990a..c9a505ef3f 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -50,7 +50,7 @@ export class UserSuspendService { (async () => { await this.postSuspend(user).catch((e: any) => {}); - await this.unFollowAll(user).catch((e: any) => {}); + await this.suspendFollowings(user).catch((e: any) => {}); })(); } @@ -140,7 +140,7 @@ export class UserSuspendService { } @bindThis - private async unFollowAll(follower: MiUser) { + private async suspendFollowings(follower: MiUser) { await this.followingsRepository.update( { followerId: follower.id, diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 642e176dbb..b6728c6c33 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -9,9 +9,9 @@ import * as assert from 'assert'; import { setTimeout } from 'node:timers/promises'; import { Redis } from 'ioredis'; +import { SignupResponse, Role, Note } from 'misskey-js/entities.js'; import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl, role } from '../utils.js'; import { loadConfig } from '@/config.js'; -import { SignupResponse, Role, Note } from 'misskey-js/entities.js'; function genHost() { return randomString() + '.example.com'; @@ -693,6 +693,47 @@ describe('Timelines', () => { assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa'); }); }); + + describe('凍結(リモート)', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; + + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + + await waitForPushToTl(); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 3); + 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), true); + }); + }); }); describe('Local TL', () => { @@ -949,7 +990,7 @@ describe('Timelines', () => { await setTimeout(100); const res = await api('notes/local-timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 2); + 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); @@ -961,7 +1002,6 @@ describe('Timelines', () => { const res = await api('notes/local-timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 3); 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), true); @@ -1208,6 +1248,8 @@ describe('Timelines', () => { elleNote = await post(elle, { text: 'hi there' }); await api('following/create', { userId: dave.id }, alice); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); await api('following/create', { userId: elle.id }, alice); await waitForPushToTl(); @@ -1219,8 +1261,8 @@ describe('Timelines', () => { await api('admin/suspend-user', { userId: elle.id }, root); await setTimeout(250); - const res = await api('notes/local-timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 2); + const res = await api('notes/hybrid-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); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); @@ -1234,9 +1276,8 @@ describe('Timelines', () => { await api('admin/unsuspend-user', { userId: elle.id }, root); await setTimeout(250); - const res = await api('notes/local-timeline', { limit: 100 }, alice); + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 5); 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), true); From f50364b100d4aa573d85f643eb7e75ba8d3b9688 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 21:33:39 +0900 Subject: [PATCH 32/85] update test --- packages/backend/test/e2e/timelines.ts | 71 ++++++++++++------- .../backend/test/unit/UserSuspendService.ts | 54 ++++++++++++++ 2 files changed, 100 insertions(+), 25 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index b6728c6c33..c1400a2cff 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 { SignupResponse, Role, Note } from 'misskey-js/entities.js'; -import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl, role } from '../utils.js'; +import { SignupResponse, Note, UserList } from 'misskey-js/entities.js'; +import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js'; import { loadConfig } from '@/config.js'; function genHost() { @@ -667,12 +667,12 @@ describe('Timelines', () => { carolNote = await post(carol, { text: 'kon\'nichiwa' }); await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); }); test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - await api('admin/suspend-user', { userId: carol.id }, root); - await setTimeout(100); - const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.length, 2); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); @@ -709,12 +709,12 @@ describe('Timelines', () => { carolNote = await post(carol, { text: 'kon\'nichiwa' }); await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); }); test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - await api('admin/suspend-user', { userId: carol.id }, root); - await setTimeout(100); - const res = await api('notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.body.length, 2); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); @@ -1253,14 +1253,14 @@ describe('Timelines', () => { await api('following/create', { userId: elle.id }, alice); await waitForPushToTl(); - }); - test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { await api('admin/suspend-user', { userId: carol.id }, root); await api('admin/suspend-user', { userId: dave.id }, root); await api('admin/suspend-user', { userId: elle.id }, root); await setTimeout(250); + }); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); @@ -1531,26 +1531,47 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); }); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + describe('凍結', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; + let list: UserList; - await waitForPushToTl(); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await api('admin/suspend-user', { userId: carol.id }, root); + 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 setTimeout(250); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - assert.strictEqual(res.body.length, 1); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + }); }); }); diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index ce8d35f408..8d6b01b730 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -25,6 +25,11 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { randomString } from '../utils.js'; + +function genHost() { + return randomString() + '.example.com'; +} describe('UserSuspendService', () => { let app: TestingModule; @@ -359,4 +364,53 @@ describe('UserSuspendService', () => { expect(queueService.deliver).not.toHaveBeenCalled(); }); }); + + describe('remote user suspension', () => { + test('should suspend remote user without AP delivery', async () => { + const remoteUser = await createUser({ host: genHost() }); + const moderator = await createUser(); + + await userSuspendService.suspend(remoteUser, moderator); + await setTimeout(250); + + // ユーザーが凍結されているかチェック + const suspendedUser = await usersRepository.findOneBy({ id: remoteUser.id }); + expect(suspendedUser?.isSuspended).toBe(true); + + // モデレーションログが記録されているかチェック + expect(moderationLogService.log).toHaveBeenCalledWith(moderator, 'suspend', { + userId: remoteUser.id, + userUsername: remoteUser.username, + userHost: remoteUser.host, + }); + + // ActivityPub配信が呼ばれていないことを確認 + expect(queueService.deliver).not.toHaveBeenCalled(); + }); + }); + + describe('remote user unsuspension', () => { + test('should unsuspend remote user without AP delivery', async () => { + const remoteUser = await createUser({ host: genHost(), isSuspended: true }); + const moderator = await createUser(); + + await userSuspendService.unsuspend(remoteUser, moderator); + + await setTimeout(250); + + // ユーザーの凍結が解除されているかチェック + const unsuspendedUser = await usersRepository.findOneBy({ id: remoteUser.id }); + expect(unsuspendedUser?.isSuspended).toBe(false); + + // モデレーションログが記録されているかチェック + expect(moderationLogService.log).toHaveBeenCalledWith(moderator, 'unsuspend', { + userId: remoteUser.id, + userUsername: remoteUser.username, + userHost: remoteUser.host, + }); + + // ActivityPub配信が呼ばれていないことを確認 + expect(queueService.deliver).not.toHaveBeenCalled(); + }); + }); }); From 537a49a44883d923d36a6f4300ceaec5f16834a7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 21:53:19 +0900 Subject: [PATCH 33/85] fixed --- packages/backend/test/e2e/timelines.ts | 64 ++++++++++++++++++++------ 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index c1400a2cff..68a0afe8ff 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -1233,30 +1233,23 @@ describe('Timelines', () => { * bob = 未フォローのローカルユーザー (凍結対象でない) * carol = 未フォローのローカルユーザー (凍結対象) * dave = フォローしているローカルユーザー (凍結対象) - * elle = フォローしているリモートユーザー (凍結対象) */ - let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse, dave: SignupResponse, elle: SignupResponse; - let aliceNote: Note, bobNote: Note, carolNote: Note, daveNote: Note, elleNote: Note; + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse, dave: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note, daveNote: Note; beforeAll(async () => { - [alice, bob, carol, dave, elle] = await Promise.all([signup(), signup(), signup(), signup(), signup({ host: genHost() })]); + [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); + await api('following/create', { userId: dave.id }, alice); aliceNote = await post(alice, { text: 'hi' }); bobNote = await post(bob, { text: 'yo' }); carolNote = await post(carol, { text: 'kon\'nichiwa' }); daveNote = await post(dave, { text: 'hello' }); - elleNote = await post(elle, { text: 'hi there' }); - - await api('following/create', { userId: dave.id }, alice); - - await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); - await api('following/create', { userId: elle.id }, alice); await waitForPushToTl(); await api('admin/suspend-user', { userId: carol.id }, root); await api('admin/suspend-user', { userId: dave.id }, root); - await api('admin/suspend-user', { userId: elle.id }, root); await setTimeout(250); }); @@ -1267,13 +1260,11 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); - assert.strictEqual(res.body.some(note => note.id === elleNote.id), false); }); test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { await api('admin/unsuspend-user', { userId: carol.id }, root); await api('admin/unsuspend-user', { userId: dave.id }, root); - await api('admin/unsuspend-user', { userId: elle.id }, root); await setTimeout(250); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); @@ -1282,6 +1273,50 @@ describe('Timelines', () => { 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.some(note => note.id === daveNote.id), true); + }); + }); + + describe('凍結 (リモート)', () => { + /* + * carol = 未フォローのリモートユーザー (凍結対象) + * elle = フォローしているリモートユーザー (凍結対象) + */ + let alice: SignupResponse, carol: SignupResponse, elle: SignupResponse; + let aliceNote: Note, carolNote: Note, elleNote: Note; + + beforeAll(async () => { + [alice, carol, elle] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: elle.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + elleNote = await post(elle, { text: 'hi there' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await api('admin/suspend-user', { userId: elle.id }, root); + await setTimeout(250); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === elleNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await api('admin/unsuspend-user', { userId: elle.id }, root); + await setTimeout(250); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === elleNote.id), true); }); }); @@ -1531,7 +1566,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); }); - describe('凍結', () => { let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; let aliceNote: Note, bobNote: Note, carolNote: Note; @@ -1541,9 +1575,9 @@ describe('Timelines', () => { [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); 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); - aliceNote = await post(alice, { text: 'hi' }); bobNote = await post(bob, { text: 'yo' }); carolNote = await post(carol, { text: 'kon\'nichiwa' }); From eee0aba8afb588fd64f36064727aa5e78b59bb8d Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 22:18:11 +0900 Subject: [PATCH 34/85] cherry pick --- packages/backend/test/e2e/timelines.ts | 289 ++++++++++++++++++++----- 1 file changed, 229 insertions(+), 60 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 2c53dfff2e..68a0afe8ff 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -9,9 +9,9 @@ import * as assert from 'assert'; import { setTimeout } from 'node:timers/promises'; import { Redis } from 'ioredis'; -import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl, role } from '../utils.js'; +import { SignupResponse, Note, UserList } from 'misskey-js/entities.js'; +import { api, post, randomString, sendEnvUpdateRequest, signup, uploadUrl } from '../utils.js'; import { loadConfig } from '@/config.js'; -import { SignupResponse, Role } from 'misskey-js/entities.js'; function genHost() { return randomString() + '.example.com'; @@ -653,26 +653,86 @@ describe('Timelines', () => { assert.strictEqual(await redisForTimelines.exists(`list:homeTimeline:${bob.id}`), 0); }); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); - await setTimeout(250); + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); - const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 2); - 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('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 3); + 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), true); + assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa'); + }); + }); + + describe('凍結(リモート)', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; + + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: bob.id }, alice); + await api('following/create', { userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/timeline', { limit: 100 }, alice); + assert.strictEqual(res.body.length, 2); + 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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.length, 3); + 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), true); + }); }); }); @@ -911,25 +971,42 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); + }); - await setTimeout(250); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); - 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); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + 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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + 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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'kon\'nichiwa'); + }); }); }); @@ -1151,25 +1228,97 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true); }, 1000 * 10); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + /* + * bob = 未フォローのローカルユーザー (凍結対象でない) + * carol = 未フォローのローカルユーザー (凍結対象) + * dave = フォローしているローカルユーザー (凍結対象) + */ + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse, dave: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note, daveNote: Note; - await api('following/create', { userId: bob.id }, alice); - await api('following/create', { userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol, dave] = await Promise.all([signup(), signup(), signup(), signup()]); - await waitForPushToTl(); + await api('following/create', { userId: dave.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + daveNote = await post(dave, { text: 'hello' }); - await api('admin/suspend-user', { userId: carol.id }, root); + await waitForPushToTl(); - await setTimeout(250); + await api('admin/suspend-user', { userId: carol.id }, root); + await api('admin/suspend-user', { userId: dave.id }, root); + await setTimeout(250); + }); - const res = await api('notes/hybrid-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); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/hybrid-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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await api('admin/unsuspend-user', { userId: dave.id }, root); + await setTimeout(250); + + const res = await api('notes/hybrid-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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + assert.strictEqual(res.body.some(note => note.id === daveNote.id), true); + }); + }); + + describe('凍結 (リモート)', () => { + /* + * carol = 未フォローのリモートユーザー (凍結対象) + * elle = フォローしているリモートユーザー (凍結対象) + */ + let alice: SignupResponse, carol: SignupResponse, elle: SignupResponse; + let aliceNote: Note, carolNote: Note, elleNote: Note; + + beforeAll(async () => { + [alice, carol, elle] = await Promise.all([signup(), signup({ host: genHost() }), signup({ host: genHost() })]); + + await sendEnvUpdateRequest({ key: 'FORCE_FOLLOW_REMOTE_USER_FOR_TESTING', value: 'true' }); + await api('following/create', { userId: elle.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + elleNote = await post(elle, { text: 'hi there' }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await api('admin/suspend-user', { userId: elle.id }, root); + await setTimeout(250); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === elleNote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await api('admin/unsuspend-user', { userId: elle.id }, root); + await setTimeout(250); + + const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === elleNote.id), true); + }); }); }); @@ -1417,26 +1566,46 @@ describe('Timelines', () => { assert.strictEqual(res.body.some(note => note.id === bobNote.id), false); }); - test('凍結: 凍結後に凍結されたユーザーのノートは見えなくなる', async () => { - const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + describe('凍結', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note; + let list: UserList; - const list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); - await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); - const aliceNote = await post(alice, { text: 'hi' }); - const bobNote = await post(bob, { text: 'yo' }); - const carolNote = await post(carol, { text: 'kon\'nichiwa' }); + beforeAll(async () => { + [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); - await waitForPushToTl(); + list = await api('users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('admin/suspend-user', { userId: carol.id }, root); + await api('users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('users/lists/push', { listId: list.id, userId: carol.id }, alice); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); - await setTimeout(250); + await waitForPushToTl(); - const res = await api('notes/user-list-timeline', { listId: list.id }, alice); - assert.strictEqual(res.body.length, 1); - assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); - assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); + + test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.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('凍結解除後に凍結されていたユーザーのノートは見えるようになる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some(note => note.id === aliceNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobNote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + }); }); }); From 7476dda563de30d0927c109cf15935fbdbc4961f Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 22:32:50 +0900 Subject: [PATCH 35/85] add renote test --- packages/backend/test/e2e/timelines.ts | 49 +++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 68a0afe8ff..106b2857b5 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -674,7 +674,6 @@ describe('Timelines', () => { test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 2); 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); @@ -686,7 +685,6 @@ describe('Timelines', () => { const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 3); 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), true); @@ -694,6 +692,50 @@ describe('Timelines', () => { }); }); + describe('凍結 (Renote)', () => { + let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; + let aliceNote: Note, bobNote: Note, carolNote: Note, bobRenote: Note, carolRenote: Note; + + beforeAll(async () => { + [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); + aliceNote = await post(alice, { text: 'hi' }); + bobNote = await post(bob, { text: 'yo' }); + carolNote = await post(carol, { text: 'kon\'nichiwa' }); + bobRenote = await post(bob, { renoteId: carolNote.id }); + carolRenote = await post(carol, { renoteId: bobNote.id }); + + await waitForPushToTl(); + + await api('admin/suspend-user', { userId: carol.id }, root); + await setTimeout(100); + }); + + test('凍結後に凍結されたユーザーに対するRenoteや凍結されたユーザーのRenoteが見えなくなる', async () => { + const res = await api('notes/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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), false); + assert.strictEqual(res.body.some(note => note.id === bobRenote.id), false); + assert.strictEqual(res.body.some(note => note.id === carolRenote.id), false); + }); + + test('凍結解除後に凍結されていたユーザーに対するRenoteや凍結されたユーザーのRenoteが見えなくなる', async () => { + await api('admin/unsuspend-user', { userId: carol.id }, root); + await setTimeout(100); + + const res = await api('notes/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); + assert.strictEqual(res.body.some(note => note.id === carolNote.id), true); + assert.strictEqual(res.body.some(note => note.id === bobRenote.id), true); + assert.strictEqual(res.body.some(note => note.id === carolRenote.id), true); + }); + }); + describe('凍結(リモート)', () => { let alice: SignupResponse, bob: SignupResponse, carol: SignupResponse; let aliceNote: Note, bobNote: Note, carolNote: Note; @@ -716,7 +758,7 @@ describe('Timelines', () => { test('凍結後に凍結されたユーザーのノートは見えなくなる', async () => { const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 2); + 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); @@ -728,7 +770,6 @@ describe('Timelines', () => { const res = await api('notes/timeline', { limit: 100 }, alice); - assert.strictEqual(res.body.length, 3); 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), true); From 3f4e80f5bf040a5530b5e689eec18d3acf582fce Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 23:09:35 +0900 Subject: [PATCH 36/85] Fix https://github.com/misskey-dev/misskey/issues/16293 --- .../src/core/FanoutTimelineEndpointService.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 97b617096a..04d6d2c89c 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -20,6 +20,8 @@ import { CacheService } from '@/core/CacheService.js'; import { isReply } from '@/misc/is-reply.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; +type NoteFilter = (note: MiNote) => boolean; + type TimelineOptions = { untilId: string | null, sinceId: string | null, @@ -28,7 +30,7 @@ type TimelineOptions = { me?: { id: MiUser['id'] } | undefined | null, useDbFallback: boolean, redisTimelines: FanoutTimelineName[], - noteFilter?: (note: MiNote) => boolean, + noteFilter?: NoteFilter, alwaysIncludeMyNotes?: boolean; ignoreAuthorFromBlock?: boolean; ignoreAuthorFromMute?: boolean; @@ -79,7 +81,7 @@ export class FanoutTimelineEndpointService { const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId; if (!shouldFallbackToDb) { - let filter = ps.noteFilter ?? (_note => true); + let filter = ps.noteFilter ?? (_note => true) as NoteFilter; if (ps.alwaysIncludeMyNotes && ps.me) { const me = ps.me; @@ -145,15 +147,13 @@ export class FanoutTimelineEndpointService { { const parentFilter = filter; filter = (note) => { - const noteJoined = note as MiNote & { - renoteUser: MiUser | null; - replyUser: MiUser | null; - }; + console.log(JSON.stringify(note, null, 2)); + if (!ps.ignoreAuthorFromUserSuspension) { if (note.user!.isSuspended) return false; } - if (note.userId !== note.renoteUserId && noteJoined.renoteUser?.isSuspended) return false; - if (note.userId !== note.replyUserId && noteJoined.replyUser?.isSuspended) return false; + if (note.userId !== note.renoteUserId && note.renote?.user?.isSuspended) return false; + if (note.userId !== note.replyUserId && note.reply?.user?.isSuspended) return false; return parentFilter(note); }; @@ -200,7 +200,7 @@ export class FanoutTimelineEndpointService { return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit); } - private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean, idCompare: (a: string, b: string) => number): Promise { + private async getAndFilterFromDb(noteIds: string[], noteFilter: NoteFilter, idCompare: (a: string, b: string) => number): Promise { const query = this.notesRepository.createQueryBuilder('note') .where('note.id IN (:...noteIds)', { noteIds: noteIds }) .innerJoinAndSelect('note.user', 'user') From 4e69755afad86887d24371687630d1d971445fa8 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 16 Jul 2025 23:09:56 +0900 Subject: [PATCH 37/85] remove debug log --- packages/backend/src/core/FanoutTimelineEndpointService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 04d6d2c89c..94c5691bf4 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -147,8 +147,6 @@ export class FanoutTimelineEndpointService { { const parentFilter = filter; filter = (note) => { - console.log(JSON.stringify(note, null, 2)); - if (!ps.ignoreAuthorFromUserSuspension) { if (note.user!.isSuspended) return false; } From bb4af983b711c7cfb2c7ed005507f73510660373 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 10:45:26 +0900 Subject: [PATCH 38/85] clean up --- packages/backend/src/core/UserSuspendService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index c9a505ef3f..53daba9701 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -13,7 +13,6 @@ 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 { RelationshipJobData } from '@/queue/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @Injectable() From 1fc2ae025c559f9531b22c470be69c657457ed1f Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 11:00:58 +0900 Subject: [PATCH 39/85] wip: addAllKnowingSharedInbox --- .../activitypub/ApDeliverManagerService.ts | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index c0d667253f..025c939831 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -9,10 +9,12 @@ 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 { ApLoggerService } from './ApLoggerService.js'; interface IRecipe { type: string; @@ -27,12 +29,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 +49,15 @@ class DeliverManager { /** * Constructor - * @param userEntityService * @param followingsRepository * @param queueService * @param actor Actor * @param activity Activity to deliver */ constructor( - private userEntityService: UserEntityService, private followingsRepository: FollowingsRepository, private queueService: QueueService, + private logger: Logger, actor: { id: MiUser['id']; host: null; }, activity: IActivity | null, @@ -91,6 +99,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 @@ -105,10 +125,27 @@ class DeliverManager { */ @bindThis public async execute(): Promise { + //#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(); + 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))) { @@ -143,34 +180,40 @@ class DeliverManager { inboxes.set(recipe.to.inbox, false); } + //#endregion // deliver await this.queueService.deliverMany(this.actor, this.activity, inboxes); + 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 queueService: QueueService, + 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 { const manager = new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, + this.logger, actor, activity, ); @@ -187,9 +230,9 @@ export class ApDeliverManagerService { @bindThis public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise { const manager = new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, + this.logger, actor, activity, ); @@ -206,9 +249,9 @@ export class ApDeliverManagerService { @bindThis public async deliverToUsers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, targets: MiRemoteUser[]): Promise { const manager = new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, + this.logger, actor, activity, ); @@ -219,10 +262,9 @@ export class ApDeliverManagerService { @bindThis public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager { return new DeliverManager( - this.userEntityService, this.followingsRepository, this.queueService, - + this.logger, actor, activity, ); From a24c4951dd47ef9c66eb87c7814e963da45f816d Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 11:06:14 +0900 Subject: [PATCH 40/85] wip --- .../backend/src/core/UserSuspendService.ts | 52 +++---------------- 1 file changed, 8 insertions(+), 44 deletions(-) diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 53daba9701..d52955ca3a 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -7,12 +7,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import type { FollowingsRepository, FollowRequestsRepository, UsersRepository } 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 { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @Injectable() @@ -28,9 +28,9 @@ export class UserSuspendService { private followRequestsRepository: FollowRequestsRepository, private userEntityService: UserEntityService, - private queueService: QueueService, private globalEventService: GlobalEventService, private apRendererService: ApRendererService, + private apDeliverManagerService: ApDeliverManagerService, private moderationLogService: ModerationLogService, ) { } @@ -83,28 +83,10 @@ export class UserSuspendService { }); 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(); + manager.execute(); } } @@ -113,28 +95,10 @@ 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(); + manager.execute(); } } From 5d5aa09459b2c037b629c313d0eaf26d44e2afff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:42:34 +0900 Subject: [PATCH 41/85] chore(deps): update [misskey-js] update dependencies (#16199) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/misskey-js/generator/package.json | 8 +- packages/misskey-js/package.json | 12 +- pnpm-lock.yaml | 871 ++++++++++++++++++--- 3 files changed, 777 insertions(+), 114 deletions(-) diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json index cc8083094d..f566d6dc21 100644 --- a/packages/misskey-js/generator/package.json +++ b/packages/misskey-js/generator/package.json @@ -8,13 +8,13 @@ }, "devDependencies": { "@readme/openapi-parser": "2.7.0", - "@types/node": "22.15.31", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", + "@types/node": "22.16.4", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", "openapi-types": "12.1.3", "openapi-typescript": "7.8.0", "ts-case-convert": "2.1.0", - "tsx": "4.19.4", + "tsx": "4.20.3", "typescript": "5.8.3" }, "files": [ diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 747225af2f..4347e8e00d 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -36,11 +36,11 @@ }, "devDependencies": { "@microsoft/api-extractor": "7.52.8", - "@swc/jest": "0.2.38", + "@swc/jest": "0.2.39", "@types/jest": "29.5.14", - "@types/node": "22.15.31", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", + "@types/node": "22.16.4", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", @@ -50,8 +50,8 @@ "execa": "8.0.1", "tsd": "0.32.0", "typescript": "5.8.3", - "esbuild": "0.25.5", - "glob": "11.0.2" + "esbuild": "0.25.6", + "glob": "11.0.3" }, "files": [ "built" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cce64a0b5..d3c2456f9a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -738,7 +738,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.4 - version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))(vue@3.5.16(typescript@5.8.3)) + version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) '@vue/compiler-sfc': specifier: 3.5.16 version: 3.5.16 @@ -876,7 +876,7 @@ importers: version: 1.13.1(vue@3.5.16(typescript@5.8.3)) vite: specifier: 6.3.5 - version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) vue: specifier: 3.5.16 version: 3.5.16(typescript@5.8.3) @@ -928,7 +928,7 @@ importers: version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) '@storybook/react-vite': specifier: 8.6.14 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) '@storybook/test': specifier: 8.6.14 version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) @@ -943,7 +943,7 @@ importers: version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.16(typescript@5.8.3)) '@storybook/vue3-vite': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))(vue@3.5.16(typescript@5.8.3)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) '@tabler/icons-webfont': specifier: 3.34.0 version: 3.34.0 @@ -991,7 +991,7 @@ importers: version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) '@vitest/coverage-v8': specifier: 3.2.3 - version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) '@vue/compiler-core': specifier: 3.5.16 version: 3.5.16 @@ -1063,10 +1063,10 @@ importers: version: 1.0.3 vitest: specifier: 3.2.3 - version: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + version: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) vitest-fetch-mock: specifier: 0.4.5 - version: 0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + version: 0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) vue-component-type-helpers: specifier: 2.2.10 version: 2.2.10 @@ -1096,7 +1096,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.4 - version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))(vue@3.5.16(typescript@5.8.3)) + version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) '@vue/compiler-sfc': specifier: 3.5.16 version: 3.5.16 @@ -1153,7 +1153,7 @@ importers: version: 11.1.0 vite: specifier: 6.3.5 - version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) vue: specifier: 3.5.16 version: 3.5.16(typescript@5.8.3) @@ -1193,7 +1193,7 @@ importers: version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) '@vitest/coverage-v8': specifier: 3.2.3 - version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) '@vue/runtime-core': specifier: 3.5.16 version: 3.5.16 @@ -1371,34 +1371,34 @@ importers: devDependencies: '@microsoft/api-extractor': specifier: 7.52.8 - version: 7.52.8(@types/node@22.15.31) + version: 7.52.8(@types/node@22.16.4) '@swc/jest': - specifier: 0.2.38 - version: 0.2.38(@swc/core@1.12.0) + specifier: 0.2.39 + version: 0.2.39(@swc/core@1.12.0) '@types/jest': specifier: 29.5.14 version: 29.5.14 '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.28.0)(typescript@5.8.3) esbuild: - specifier: 0.25.5 - version: 0.25.5 + specifier: 0.25.6 + version: 0.25.6 execa: specifier: 8.0.1 version: 8.0.1 glob: - specifier: 11.0.2 - version: 11.0.2 + specifier: 11.0.3 + version: 11.0.3 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.15.31) + version: 29.7.0(@types/node@22.16.4) jest-fetch-mock: specifier: 3.0.3 version: 3.0.3(encoding@0.1.13) @@ -1427,14 +1427,14 @@ importers: specifier: 2.7.0 version: 2.7.0(openapi-types@12.1.3) '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.28.0)(typescript@5.8.3) openapi-types: specifier: 12.1.3 version: 12.1.3 @@ -1445,8 +1445,8 @@ importers: specifier: 2.1.0 version: 2.1.0 tsx: - specifier: 4.19.4 - version: 4.19.4 + specifier: 4.20.3 + version: 4.20.3 typescript: specifier: 5.8.3 version: 5.8.3 @@ -2010,6 +2010,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.6': + resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.4': resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} engines: {node: '>=18'} @@ -2022,6 +2028,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.6': + resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.4': resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} engines: {node: '>=18'} @@ -2034,6 +2046,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.6': + resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.4': resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} engines: {node: '>=18'} @@ -2046,6 +2064,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.6': + resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.4': resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} engines: {node: '>=18'} @@ -2058,6 +2082,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.6': + resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.4': resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} engines: {node: '>=18'} @@ -2070,6 +2100,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.6': + resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.4': resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} engines: {node: '>=18'} @@ -2082,6 +2118,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.6': + resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.4': resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} engines: {node: '>=18'} @@ -2094,6 +2136,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.6': + resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.4': resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} engines: {node: '>=18'} @@ -2106,6 +2154,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.6': + resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.4': resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} engines: {node: '>=18'} @@ -2118,6 +2172,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.6': + resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.4': resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} engines: {node: '>=18'} @@ -2130,6 +2190,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.6': + resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.4': resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} engines: {node: '>=18'} @@ -2142,6 +2208,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.6': + resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.4': resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} engines: {node: '>=18'} @@ -2154,6 +2226,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.6': + resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.4': resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} engines: {node: '>=18'} @@ -2166,6 +2244,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.6': + resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.4': resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} engines: {node: '>=18'} @@ -2178,6 +2262,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.6': + resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.4': resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} engines: {node: '>=18'} @@ -2190,6 +2280,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.6': + resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.4': resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} engines: {node: '>=18'} @@ -2202,6 +2298,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.6': + resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.4': resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} engines: {node: '>=18'} @@ -2214,6 +2316,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.25.6': + resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.4': resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} engines: {node: '>=18'} @@ -2226,6 +2334,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.6': + resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.4': resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} engines: {node: '>=18'} @@ -2238,6 +2352,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.25.6': + resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.4': resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} engines: {node: '>=18'} @@ -2250,6 +2370,18 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.6': + resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.6': + resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.4': resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} engines: {node: '>=18'} @@ -2262,6 +2394,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.6': + resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.4': resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} engines: {node: '>=18'} @@ -2274,6 +2412,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.6': + resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.4': resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} engines: {node: '>=18'} @@ -2286,6 +2430,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.6': + resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.4': resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} engines: {node: '>=18'} @@ -2298,6 +2448,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.6': + resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.7.0': resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2694,6 +2850,14 @@ packages: '@ioredis/commands@1.2.0': resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2727,6 +2891,10 @@ packages: resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/create-cache-key-function@30.0.2': + resolution: {integrity: sha512-AwlDAHwEHDi+etw9vKWx9HeIApVos8GD/sSTpHtDkqhm9OWuEUPKKPP6EaS17yv0GSzBB3TeeJFLyJ5LPjRqWg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/environment@29.7.0': resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2747,6 +2915,10 @@ packages: resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/reporters@29.7.0': resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2760,6 +2932,10 @@ packages: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@30.0.1': + resolution: {integrity: sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/source-map@29.6.3': resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2780,6 +2956,10 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/types@30.0.1': + resolution: {integrity: sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0': resolution: {integrity: sha512-qYDdL7fPwLRI+bJNurVcis+tNgJmvWjH4YTBGXTA8xMuxFrnAz6E5o35iyzyKbq5J5Lr8mJGfrR5GXl+WGwhgQ==} peerDependencies: @@ -3713,6 +3893,9 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sinclair/typebox@0.34.37': + resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==} + '@sindresorhus/is@5.6.0': resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} engines: {node: '>=14.16'} @@ -4304,6 +4487,12 @@ packages: peerDependencies: '@swc/core': '*' + '@swc/jest@0.2.39': + resolution: {integrity: sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA==} + engines: {npm: '>= 7.0.0'} + peerDependencies: + '@swc/core': '*' + '@swc/types@0.1.22': resolution: {integrity: sha512-D13mY/ZA4PPEFSy6acki9eBT/3WgjMoRqNcdpIvjaYLQ44Xk5BdaL7UkDxAh6Z9UOe7tCCp67BVmZCojYp9owg==} @@ -4526,12 +4715,18 @@ packages: '@types/istanbul-lib-coverage@2.0.4': resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + '@types/istanbul-lib-report@3.0.0': resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} '@types/istanbul-reports@3.0.1': resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/jest@29.5.14': resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} @@ -4592,6 +4787,9 @@ packages: '@types/node@22.15.31': resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==} + '@types/node@22.16.4': + resolution: {integrity: sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==} + '@types/nodemailer@6.4.17': resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==} @@ -4751,6 +4949,9 @@ packages: '@types/yargs@17.0.19': resolution: {integrity: sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==} + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@types/yauzl@2.10.0': resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} @@ -4762,6 +4963,14 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/eslint-plugin@8.37.0': + resolution: {integrity: sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.37.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/parser@8.34.0': resolution: {integrity: sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4769,22 +4978,45 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/parser@8.37.0': + resolution: {integrity: sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/project-service@8.34.0': resolution: {integrity: sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/project-service@8.37.0': + resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/scope-manager@8.34.0': resolution: {integrity: sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.37.0': + resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.34.0': resolution: {integrity: sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/tsconfig-utils@8.37.0': + resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/type-utils@8.34.0': resolution: {integrity: sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4792,16 +5024,33 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/type-utils@8.37.0': + resolution: {integrity: sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/types@8.34.0': resolution: {integrity: sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.37.0': + resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.34.0': resolution: {integrity: sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/typescript-estree@8.37.0': + resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.34.0': resolution: {integrity: sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4809,10 +5058,21 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.37.0': + resolution: {integrity: sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/visitor-keys@8.34.0': resolution: {integrity: sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.37.0': + resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -6364,6 +6624,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.25.6: + resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -6454,6 +6719,10 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@9.28.0: resolution: {integrity: sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6763,6 +7032,10 @@ packages: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} @@ -6918,6 +7191,11 @@ packages: engines: {node: 20 || >=22} hasBin: true + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + 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 @@ -7506,6 +7784,10 @@ packages: resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==} engines: {node: 20 || >=22} + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7592,6 +7874,10 @@ packages: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-resolve-dependencies@29.7.0: resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8196,6 +8482,10 @@ packages: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + minimatch@3.0.8: resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} @@ -10107,10 +10397,12 @@ packages: superagent@10.2.1: resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==} engines: {node: '>=14.18.0'} + deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net supertest@7.1.1: resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==} engines: {node: '>=14.18.0'} + deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net supports-color@10.0.0: resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==} @@ -10368,6 +10660,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + engines: {node: '>=18.0.0'} + hasBin: true + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -10788,6 +11085,9 @@ packages: vue-component-type-helpers@2.2.10: resolution: {integrity: sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==} + vue-component-type-helpers@3.0.1: + resolution: {integrity: sha512-j23mCB5iEbGsyIhnVdXdWUOg+UdwmVxpKnYYf2j+4ppCt5VSFXKjwu9YFt0QYxUaf5G99PuHsVfRScjHCRSsGQ==} + vue-demi@0.14.7: resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} @@ -12004,150 +12304,228 @@ snapshots: '@esbuild/aix-ppc64@0.25.5': optional: true + '@esbuild/aix-ppc64@0.25.6': + optional: true + '@esbuild/android-arm64@0.25.4': optional: true '@esbuild/android-arm64@0.25.5': optional: true + '@esbuild/android-arm64@0.25.6': + optional: true + '@esbuild/android-arm@0.25.4': optional: true '@esbuild/android-arm@0.25.5': optional: true + '@esbuild/android-arm@0.25.6': + optional: true + '@esbuild/android-x64@0.25.4': optional: true '@esbuild/android-x64@0.25.5': optional: true + '@esbuild/android-x64@0.25.6': + optional: true + '@esbuild/darwin-arm64@0.25.4': optional: true '@esbuild/darwin-arm64@0.25.5': optional: true + '@esbuild/darwin-arm64@0.25.6': + optional: true + '@esbuild/darwin-x64@0.25.4': optional: true '@esbuild/darwin-x64@0.25.5': optional: true + '@esbuild/darwin-x64@0.25.6': + optional: true + '@esbuild/freebsd-arm64@0.25.4': optional: true '@esbuild/freebsd-arm64@0.25.5': optional: true + '@esbuild/freebsd-arm64@0.25.6': + optional: true + '@esbuild/freebsd-x64@0.25.4': optional: true '@esbuild/freebsd-x64@0.25.5': optional: true + '@esbuild/freebsd-x64@0.25.6': + optional: true + '@esbuild/linux-arm64@0.25.4': optional: true '@esbuild/linux-arm64@0.25.5': optional: true + '@esbuild/linux-arm64@0.25.6': + optional: true + '@esbuild/linux-arm@0.25.4': optional: true '@esbuild/linux-arm@0.25.5': optional: true + '@esbuild/linux-arm@0.25.6': + optional: true + '@esbuild/linux-ia32@0.25.4': optional: true '@esbuild/linux-ia32@0.25.5': optional: true + '@esbuild/linux-ia32@0.25.6': + optional: true + '@esbuild/linux-loong64@0.25.4': optional: true '@esbuild/linux-loong64@0.25.5': optional: true + '@esbuild/linux-loong64@0.25.6': + optional: true + '@esbuild/linux-mips64el@0.25.4': optional: true '@esbuild/linux-mips64el@0.25.5': optional: true + '@esbuild/linux-mips64el@0.25.6': + optional: true + '@esbuild/linux-ppc64@0.25.4': optional: true '@esbuild/linux-ppc64@0.25.5': optional: true + '@esbuild/linux-ppc64@0.25.6': + optional: true + '@esbuild/linux-riscv64@0.25.4': optional: true '@esbuild/linux-riscv64@0.25.5': optional: true + '@esbuild/linux-riscv64@0.25.6': + optional: true + '@esbuild/linux-s390x@0.25.4': optional: true '@esbuild/linux-s390x@0.25.5': optional: true + '@esbuild/linux-s390x@0.25.6': + optional: true + '@esbuild/linux-x64@0.25.4': optional: true '@esbuild/linux-x64@0.25.5': optional: true + '@esbuild/linux-x64@0.25.6': + optional: true + '@esbuild/netbsd-arm64@0.25.4': optional: true '@esbuild/netbsd-arm64@0.25.5': optional: true + '@esbuild/netbsd-arm64@0.25.6': + optional: true + '@esbuild/netbsd-x64@0.25.4': optional: true '@esbuild/netbsd-x64@0.25.5': optional: true + '@esbuild/netbsd-x64@0.25.6': + optional: true + '@esbuild/openbsd-arm64@0.25.4': optional: true '@esbuild/openbsd-arm64@0.25.5': optional: true + '@esbuild/openbsd-arm64@0.25.6': + optional: true + '@esbuild/openbsd-x64@0.25.4': optional: true '@esbuild/openbsd-x64@0.25.5': optional: true + '@esbuild/openbsd-x64@0.25.6': + optional: true + + '@esbuild/openharmony-arm64@0.25.6': + optional: true + '@esbuild/sunos-x64@0.25.4': optional: true '@esbuild/sunos-x64@0.25.5': optional: true + '@esbuild/sunos-x64@0.25.6': + optional: true + '@esbuild/win32-arm64@0.25.4': optional: true '@esbuild/win32-arm64@0.25.5': optional: true + '@esbuild/win32-arm64@0.25.6': + optional: true + '@esbuild/win32-ia32@0.25.4': optional: true '@esbuild/win32-ia32@0.25.5': optional: true + '@esbuild/win32-ia32@0.25.6': + optional: true + '@esbuild/win32-x64@0.25.4': optional: true '@esbuild/win32-x64@0.25.5': optional: true + '@esbuild/win32-x64@0.25.6': + optional: true + '@eslint-community/eslint-utils@4.7.0(eslint@9.28.0)': dependencies: eslint: 9.28.0 @@ -12520,6 +12898,12 @@ snapshots: '@ioredis/commands@1.2.0': {} + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -12546,7 +12930,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -12559,14 +12943,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 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@22.15.31) + jest-config: 29.7.0(@types/node@22.16.4) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -12591,11 +12975,15 @@ snapshots: dependencies: '@jest/types': 29.6.3 + '@jest/create-cache-key-function@30.0.2': + dependencies: + '@jest/types': 30.0.1 + '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -12613,7 +13001,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.15.31 + '@types/node': 22.16.4 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -12627,6 +13015,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 22.16.4 + jest-regex-util: 30.0.1 + '@jest/reporters@29.7.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -12635,7 +13028,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.15.31 + '@types/node': 22.16.4 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -12660,6 +13053,10 @@ snapshots: dependencies: '@sinclair/typebox': 0.27.8 + '@jest/schemas@30.0.1': + dependencies: + '@sinclair/typebox': 0.34.37 + '@jest/source-map@29.6.3': dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -12705,16 +13102,26 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/yargs': 17.0.19 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))': + '@jest/types@30.0.1': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.1 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.16.4 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': dependencies: glob: 10.4.5 magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.8.3) - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) optionalDependencies: typescript: 5.8.3 @@ -12778,23 +13185,23 @@ snapshots: '@types/react': 18.0.28 react: 19.1.0 - '@microsoft/api-extractor-model@7.30.6(@types/node@22.15.31)': + '@microsoft/api-extractor-model@7.30.6(@types/node@22.16.4)': dependencies: '@microsoft/tsdoc': 0.15.1 '@microsoft/tsdoc-config': 0.17.1 - '@rushstack/node-core-library': 5.13.1(@types/node@22.15.31) + '@rushstack/node-core-library': 5.13.1(@types/node@22.16.4) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.52.8(@types/node@22.15.31)': + '@microsoft/api-extractor@7.52.8(@types/node@22.16.4)': dependencies: - '@microsoft/api-extractor-model': 7.30.6(@types/node@22.15.31) + '@microsoft/api-extractor-model': 7.30.6(@types/node@22.16.4) '@microsoft/tsdoc': 0.15.1 '@microsoft/tsdoc-config': 0.17.1 - '@rushstack/node-core-library': 5.13.1(@types/node@22.15.31) + '@rushstack/node-core-library': 5.13.1(@types/node@22.16.4) '@rushstack/rig-package': 0.5.3 - '@rushstack/terminal': 0.15.3(@types/node@22.15.31) - '@rushstack/ts-command-line': 5.0.1(@types/node@22.15.31) + '@rushstack/terminal': 0.15.3(@types/node@22.16.4) + '@rushstack/ts-command-line': 5.0.1(@types/node@22.16.4) lodash: 4.17.21 minimatch: 3.0.8 resolve: 1.22.8 @@ -13544,7 +13951,7 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@rushstack/node-core-library@5.13.1(@types/node@22.15.31)': + '@rushstack/node-core-library@5.13.1(@types/node@22.16.4)': dependencies: ajv: 8.13.0 ajv-draft-04: 1.0.0(ajv@8.13.0) @@ -13555,23 +13962,23 @@ snapshots: resolve: 1.22.8 semver: 7.5.4 optionalDependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@rushstack/rig-package@0.5.3': dependencies: resolve: 1.22.8 strip-json-comments: 3.1.1 - '@rushstack/terminal@0.15.3(@types/node@22.15.31)': + '@rushstack/terminal@0.15.3(@types/node@22.16.4)': dependencies: - '@rushstack/node-core-library': 5.13.1(@types/node@22.15.31) + '@rushstack/node-core-library': 5.13.1(@types/node@22.16.4) supports-color: 8.1.1 optionalDependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 - '@rushstack/ts-command-line@5.0.1(@types/node@22.15.31)': + '@rushstack/ts-command-line@5.0.1(@types/node@22.16.4)': dependencies: - '@rushstack/terminal': 0.15.3(@types/node@22.15.31) + '@rushstack/terminal': 0.15.3(@types/node@22.16.4) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.1 @@ -13768,6 +14175,8 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@sinclair/typebox@0.34.37': {} + '@sindresorhus/is@5.6.0': {} '@sindresorhus/is@7.0.1': {} @@ -14292,13 +14701,13 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@storybook/builder-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))': + '@storybook/builder-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': dependencies: '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) browser-assert: 1.2.1 storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) '@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: @@ -14313,8 +14722,8 @@ snapshots: '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) better-opn: 3.0.2 browser-assert: 1.2.1 - esbuild: 0.25.5 - esbuild-register: 3.5.0(esbuild@0.25.5) + esbuild: 0.25.6 + esbuild-register: 3.5.0(esbuild@0.25.6) jsdoc-type-pratt-parser: 4.1.0 process: 0.11.10 recast: 0.23.6 @@ -14361,11 +14770,11 @@ snapshots: react-dom: 19.1.0(react@19.1.0) storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))': + '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) '@rollup/pluginutils': 5.1.4(rollup@4.42.0) - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) find-up: 5.0.0 magic-string: 0.30.17 @@ -14375,7 +14784,7 @@ snapshots: resolve: 1.22.8 storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tsconfig-paths: 4.2.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) optionalDependencies: '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) transitivePeerDependencies: @@ -14424,15 +14833,15 @@ snapshots: dependencies: storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/vue3-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))(vue@3.5.16(typescript@5.8.3))': + '@storybook/vue3-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': dependencies: - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) '@storybook/vue3': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.16(typescript@5.8.3)) find-package-json: 1.2.0 magic-string: 0.30.17 storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) typescript: 5.8.3 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) vue-component-meta: 2.0.16(typescript@5.8.3) vue-docgen-api: 4.75.1(vue@3.5.16(typescript@5.8.3)) transitivePeerDependencies: @@ -14450,7 +14859,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.16(typescript@5.8.3) - vue-component-type-helpers: 2.2.10 + vue-component-type-helpers: 3.0.1 '@stylistic/eslint-plugin@2.13.0(eslint@9.28.0)(typescript@5.8.3)': dependencies: @@ -14544,6 +14953,13 @@ snapshots: '@swc/counter': 0.1.3 jsonc-parser: 3.2.0 + '@swc/jest@0.2.39(@swc/core@1.12.0)': + dependencies: + '@jest/create-cache-key-function': 30.0.2 + '@swc/core': 1.12.0 + '@swc/counter': 0.1.3 + jsonc-parser: 3.2.0 + '@swc/types@0.1.22': dependencies: '@swc/counter': 0.1.3 @@ -14768,7 +15184,7 @@ snapshots: '@types/connect@3.4.36': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/content-disposition@0.5.9': {} @@ -14799,7 +15215,7 @@ snapshots: '@types/express-serve-static-core@4.17.33': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 @@ -14816,7 +15232,7 @@ snapshots: '@types/graceful-fs@4.1.6': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/hammerjs@2.0.46': {} @@ -14834,6 +15250,8 @@ snapshots: '@types/istanbul-lib-coverage@2.0.4': {} + '@types/istanbul-lib-coverage@2.0.6': {} + '@types/istanbul-lib-report@3.0.0': dependencies: '@types/istanbul-lib-coverage': 2.0.4 @@ -14842,6 +15260,10 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.0 + '@types/jest@29.5.14': dependencies: expect: 29.7.0 @@ -14889,17 +15311,21 @@ snapshots: '@types/mysql@2.15.26': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/node-fetch@2.6.11': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 form-data: 4.0.3 '@types/node@22.15.31': dependencies: undici-types: 6.21.0 + '@types/node@22.16.4': + dependencies: + undici-types: 6.21.0 + '@types/nodemailer@6.4.17': dependencies: '@types/node': 22.15.31 @@ -14935,7 +15361,7 @@ snapshots: '@types/pg@8.6.1': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 pg-protocol: 1.10.0 pg-types: 2.2.0 @@ -14965,7 +15391,7 @@ snapshots: '@types/readdir-glob@1.1.1': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/redis-info@3.0.3': {} @@ -14988,7 +15414,7 @@ snapshots: '@types/serve-static@1.15.1': dependencies: '@types/mime': 3.0.1 - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/serviceworker@0.0.74': {} @@ -15014,7 +15440,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.15.31 + '@types/node': 22.16.4 form-data: 4.0.3 '@types/supertest@6.0.3': @@ -15024,7 +15450,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/throttle-debounce@5.0.2': {} @@ -15060,9 +15486,13 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.0 + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.0 + '@types/yauzl@2.10.0': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 optional: true '@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3)': @@ -15082,6 +15512,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.37.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.37.0 + '@typescript-eslint/type-utils': 8.37.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.37.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.37.0 + eslint: 9.28.0 + graphemer: 1.4.0 + ignore: 7.0.4 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.34.0 @@ -15094,6 +15541,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.37.0 + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.37.0 + debug: 4.4.1(supports-color@10.0.0) + eslint: 9.28.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/project-service@8.34.0(typescript@5.8.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.3) @@ -15103,15 +15562,33 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.37.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) + '@typescript-eslint/types': 8.37.0 + debug: 4.4.1(supports-color@10.0.0) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.34.0': dependencies: '@typescript-eslint/types': 8.34.0 '@typescript-eslint/visitor-keys': 8.34.0 + '@typescript-eslint/scope-manager@8.37.0': + dependencies: + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/visitor-keys': 8.37.0 + '@typescript-eslint/tsconfig-utils@8.34.0(typescript@5.8.3)': dependencies: typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + '@typescript-eslint/type-utils@8.34.0(eslint@9.28.0)(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.3) @@ -15123,8 +15600,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@8.37.0(eslint@9.28.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.37.0(eslint@9.28.0)(typescript@5.8.3) + debug: 4.4.1(supports-color@10.0.0) + eslint: 9.28.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@8.34.0': {} + '@typescript-eslint/types@8.37.0': {} + '@typescript-eslint/typescript-estree@8.34.0(typescript@5.8.3)': dependencies: '@typescript-eslint/project-service': 8.34.0(typescript@5.8.3) @@ -15141,6 +15632,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.37.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.37.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/visitor-keys': 8.37.0 + debug: 4.4.1(supports-color@10.0.0) + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.34.0(eslint@9.28.0)(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) @@ -15152,19 +15659,35 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.37.0(eslint@9.28.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) + '@typescript-eslint/scope-manager': 8.37.0 + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) + eslint: 9.28.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.34.0': dependencies: '@typescript-eslint/types': 8.34.0 eslint-visitor-keys: 4.2.0 + '@typescript-eslint/visitor-keys@8.37.0': + dependencies: + '@typescript-eslint/types': 8.37.0 + eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))(vue@3.5.16(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': dependencies: - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) vue: 3.5.16(typescript@5.8.3) - '@vitest/coverage-v8@3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))': + '@vitest/coverage-v8@3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -15179,7 +15702,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) transitivePeerDependencies: - supports-color @@ -15198,14 +15721,14 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4))': + '@vitest/mocker@3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': dependencies: '@vitest/spy': 3.2.3 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: msw: 2.10.2(@types/node@22.15.31)(typescript@5.8.3) - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) '@vitest/pretty-format@2.0.5': dependencies: @@ -16435,6 +16958,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(@types/node@22.16.4): + 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@22.16.4) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + cron-parser@4.9.0: dependencies: luxon: 3.3.0 @@ -17017,10 +17555,10 @@ snapshots: es6-promise: 4.2.8 optional: true - esbuild-register@3.5.0(esbuild@0.25.5): + esbuild-register@3.5.0(esbuild@0.25.6): dependencies: debug: 4.4.1(supports-color@10.0.0) - esbuild: 0.25.5 + esbuild: 0.25.6 transitivePeerDependencies: - supports-color @@ -17080,6 +17618,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.5 '@esbuild/win32-x64': 0.25.5 + esbuild@0.25.6: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.6 + '@esbuild/android-arm': 0.25.6 + '@esbuild/android-arm64': 0.25.6 + '@esbuild/android-x64': 0.25.6 + '@esbuild/darwin-arm64': 0.25.6 + '@esbuild/darwin-x64': 0.25.6 + '@esbuild/freebsd-arm64': 0.25.6 + '@esbuild/freebsd-x64': 0.25.6 + '@esbuild/linux-arm': 0.25.6 + '@esbuild/linux-arm64': 0.25.6 + '@esbuild/linux-ia32': 0.25.6 + '@esbuild/linux-loong64': 0.25.6 + '@esbuild/linux-mips64el': 0.25.6 + '@esbuild/linux-ppc64': 0.25.6 + '@esbuild/linux-riscv64': 0.25.6 + '@esbuild/linux-s390x': 0.25.6 + '@esbuild/linux-x64': 0.25.6 + '@esbuild/netbsd-arm64': 0.25.6 + '@esbuild/netbsd-x64': 0.25.6 + '@esbuild/openbsd-arm64': 0.25.6 + '@esbuild/openbsd-x64': 0.25.6 + '@esbuild/openharmony-arm64': 0.25.6 + '@esbuild/sunos-x64': 0.25.6 + '@esbuild/win32-arm64': 0.25.6 + '@esbuild/win32-ia32': 0.25.6 + '@esbuild/win32-x64': 0.25.6 + escalade@3.2.0: {} escape-goat@3.0.0: {} @@ -17176,6 +17743,8 @@ snapshots: eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} + eslint@9.28.0: dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) @@ -17618,6 +18187,11 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + forever-agent@0.6.1: {} form-data-encoder@2.1.4: {} @@ -17798,6 +18372,15 @@ snapshots: package-json-from-dist: 1.0.0 path-scurry: 2.0.0 + glob@11.0.3: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + 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 @@ -18381,6 +18964,10 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -18393,7 +18980,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.6.0 @@ -18432,6 +19019,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(@types/node@22.16.4): + 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@22.16.4) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@22.16.4) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-config@29.7.0(@types/node@22.15.31): dependencies: '@babel/core': 7.24.7 @@ -18462,6 +19068,36 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@22.16.4): + dependencies: + '@babel/core': 7.24.7 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.24.7) + chalk: 4.1.2 + ci-info: 3.7.1 + deepmerge: 4.2.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.16.4 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -18486,7 +19122,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -18503,7 +19139,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 22.15.31 + '@types/node': 22.16.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -18551,6 +19187,8 @@ snapshots: jest-regex-util@29.6.3: {} + jest-regex-util@30.0.1: {} + jest-resolve-dependencies@29.7.0: dependencies: jest-regex-util: 29.6.3 @@ -18577,7 +19215,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -18605,7 +19243,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -18651,7 +19289,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.11 @@ -18670,7 +19308,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -18684,7 +19322,7 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -18701,6 +19339,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(@types/node@22.16.4): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@22.16.4) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jju@1.4.0: {} joi@17.13.3: @@ -19429,6 +20079,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 @@ -21775,6 +22429,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.20.3: + dependencies: + esbuild: 0.25.6 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 @@ -22042,13 +22703,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4): + vite-node@3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3): dependencies: cac: 6.7.14 debug: 4.4.1(supports-color@10.0.0) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) transitivePeerDependencies: - '@types/node' - jiti @@ -22065,7 +22726,7 @@ snapshots: vite-plugin-turbosnap@1.0.3: {} - vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4): + vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3): dependencies: esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) @@ -22078,17 +22739,17 @@ snapshots: fsevents: 2.3.3 sass: 1.89.2 terser: 5.42.0 - tsx: 4.19.4 + tsx: 4.20.3 - vitest-fetch-mock@0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)): + vitest-fetch-mock@0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)): dependencies: - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) - vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4): + vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.3 - '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4)) + '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) '@vitest/pretty-format': 3.2.3 '@vitest/runner': 3.2.3 '@vitest/snapshot': 3.2.3 @@ -22106,8 +22767,8 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.0 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) - vite-node: 3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.19.4) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite-node: 3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -22162,6 +22823,8 @@ snapshots: vue-component-type-helpers@2.2.10: {} + vue-component-type-helpers@3.0.1: {} + vue-demi@0.14.7(vue@3.5.16(typescript@5.8.3)): dependencies: vue: 3.5.16(typescript@5.8.3) From 49cac2f72b8f4a014ec29e8fa799a0dfb1621d80 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:56:55 +0900 Subject: [PATCH 42/85] fix(deps): update [root] update dependencies (#16200) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 26 +- pnpm-lock.yaml | 812 +++++++++++++++++++++++++++++-------------------- 2 files changed, 496 insertions(+), 342 deletions(-) diff --git a/package.json b/package.json index 182ea74cff..8cbc5a6396 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/misskey-dev/misskey.git" }, - "packageManager": "pnpm@10.12.1", + "packageManager": "pnpm@10.13.1", "workspaces": [ "packages/frontend-shared", "packages/frontend", @@ -52,29 +52,29 @@ "lodash": "4.17.21" }, "dependencies": { - "cssnano": "7.0.7", - "esbuild": "0.25.5", + "cssnano": "7.1.0", + "esbuild": "0.25.6", "execa": "9.6.0", "fast-glob": "3.3.3", - "glob": "11.0.2", + "glob": "11.0.3", "ignore-walk": "7.0.0", "js-yaml": "4.1.0", - "postcss": "8.5.4", + "postcss": "8.5.6", "tar": "7.4.3", - "terser": "5.42.0", + "terser": "5.43.1", "typescript": "5.8.3" }, "devDependencies": { "@misskey-dev/eslint-plugin": "2.1.0", - "@types/node": "22.15.31", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", + "@types/node": "22.16.4", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", "cross-env": "7.0.3", - "cypress": "14.4.1", - "eslint": "9.28.0", - "globals": "16.2.0", + "cypress": "14.5.2", + "eslint": "9.31.0", + "globals": "16.3.0", "ncp": "2.0.0", - "pnpm": "10.12.1", + "pnpm": "10.13.1", "start-server-and-test": "2.0.12" }, "optionalDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3c2456f9a..b127a8e667 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,11 +19,11 @@ importers: .: dependencies: cssnano: - specifier: 7.0.7 - version: 7.0.7(postcss@8.5.4) + specifier: 7.1.0 + version: 7.1.0(postcss@8.5.6) esbuild: - specifier: 0.25.5 - version: 0.25.5 + specifier: 0.25.6 + version: 0.25.6 execa: specifier: 9.6.0 version: 9.6.0 @@ -31,8 +31,8 @@ importers: specifier: 3.3.3 version: 3.3.3 glob: - specifier: 11.0.2 - version: 11.0.2 + specifier: 11.0.3 + version: 11.0.3 ignore-walk: specifier: 7.0.0 version: 7.0.0 @@ -40,48 +40,48 @@ importers: specifier: 4.1.0 version: 4.1.0 postcss: - specifier: 8.5.4 - version: 8.5.4 + specifier: 8.5.6 + version: 8.5.6 tar: specifier: 7.4.3 version: 7.4.3 terser: - specifier: 5.42.0 - version: 5.42.0 + specifier: 5.43.1 + version: 5.43.1 typescript: specifier: 5.8.3 version: 5.8.3 devDependencies: '@misskey-dev/eslint-plugin': specifier: 2.1.0 - version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.28.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3))(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0))(eslint@9.28.0)(globals@16.2.0) + version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0) '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) cross-env: specifier: 7.0.3 version: 7.0.3 cypress: - specifier: 14.4.1 - version: 14.4.1 + specifier: 14.5.2 + version: 14.5.2 eslint: - specifier: 9.28.0 - version: 9.28.0 + specifier: 9.31.0 + version: 9.31.0 globals: - specifier: 16.2.0 - version: 16.2.0 + specifier: 16.3.0 + version: 16.3.0 ncp: specifier: 2.0.0 version: 2.0.0 pnpm: - specifier: 10.12.1 - version: 10.12.1 + specifier: 10.13.1 + version: 10.13.1 start-server-and-test: specifier: 2.0.12 version: 2.0.12 @@ -575,10 +575,10 @@ importers: version: 8.18.1 '@typescript-eslint/eslint-plugin': specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) aws-sdk-client-mock: specifier: 4.1.0 version: 4.1.0 @@ -587,7 +587,7 @@ importers: version: 7.0.3 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0) + version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) execa: specifier: 8.0.1 version: 8.0.1 @@ -738,7 +738,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.4 - version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) + version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) '@vue/compiler-sfc': specifier: 3.5.16 version: 3.5.16 @@ -876,7 +876,7 @@ importers: version: 1.13.1(vue@3.5.16(typescript@5.8.3)) vite: specifier: 6.3.5 - version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vue: specifier: 3.5.16 version: 3.5.16(typescript@5.8.3) @@ -928,7 +928,7 @@ importers: version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) '@storybook/react-vite': specifier: 8.6.14 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@storybook/test': specifier: 8.6.14 version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) @@ -943,7 +943,7 @@ importers: version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.16(typescript@5.8.3)) '@storybook/vue3-vite': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) '@tabler/icons-webfont': specifier: 3.34.0 version: 3.34.0 @@ -985,13 +985,13 @@ importers: version: 8.18.1 '@typescript-eslint/eslint-plugin': specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) '@vitest/coverage-v8': specifier: 3.2.3 - version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@vue/compiler-core': specifier: 3.5.16 version: 3.5.16 @@ -1009,10 +1009,10 @@ importers: version: 14.4.1 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0) + version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) eslint-plugin-vue: specifier: 10.2.0 - version: 10.2.0(eslint@9.28.0)(vue-eslint-parser@10.1.3(eslint@9.28.0)) + version: 10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -1063,16 +1063,16 @@ importers: version: 1.0.3 vitest: specifier: 3.2.3 - version: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + version: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vitest-fetch-mock: specifier: 0.4.5 - version: 0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + version: 0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) vue-component-type-helpers: specifier: 2.2.10 version: 2.2.10 vue-eslint-parser: specifier: 10.1.3 - version: 10.1.3(eslint@9.28.0) + version: 10.1.3(eslint@9.31.0) vue-tsc: specifier: 2.2.10 version: 2.2.10(typescript@5.8.3) @@ -1096,7 +1096,7 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.4 - version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) + version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) '@vue/compiler-sfc': specifier: 3.5.16 version: 3.5.16 @@ -1153,7 +1153,7 @@ importers: version: 11.1.0 vite: specifier: 6.3.5 - version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vue: specifier: 3.5.16 version: 3.5.16(typescript@5.8.3) @@ -1187,13 +1187,13 @@ importers: version: 8.18.1 '@typescript-eslint/eslint-plugin': specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) '@vitest/coverage-v8': specifier: 3.2.3 - version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@vue/runtime-core': specifier: 3.5.16 version: 3.5.16 @@ -1205,10 +1205,10 @@ importers: version: 7.0.3 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0) + version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) eslint-plugin-vue: specifier: 10.2.0 - version: 10.2.0(eslint@9.28.0)(vue-eslint-parser@10.1.3(eslint@9.28.0)) + version: 10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -1241,7 +1241,7 @@ importers: version: 2.2.10 vue-eslint-parser: specifier: 10.1.3 - version: 10.1.3(eslint@9.28.0) + version: 10.1.3(eslint@9.31.0) vue-tsc: specifier: 2.2.10 version: 2.2.10(typescript@5.8.3) @@ -1260,16 +1260,16 @@ importers: version: 22.15.31 '@typescript-eslint/eslint-plugin': specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) esbuild: specifier: 0.25.5 version: 0.25.5 eslint-plugin-vue: specifier: 10.2.0 - version: 10.2.0(eslint@9.28.0)(vue-eslint-parser@10.1.3(eslint@9.28.0)) + version: 10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)) nodemon: specifier: 3.1.10 version: 3.1.10 @@ -1278,7 +1278,7 @@ importers: version: 5.8.3 vue-eslint-parser: specifier: 10.1.3 - version: 10.1.3(eslint@9.28.0) + version: 10.1.3(eslint@9.31.0) packages/icons-subsetter: dependencies: @@ -1309,10 +1309,10 @@ importers: version: 1.0.2 '@typescript-eslint/eslint-plugin': specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) packages/misskey-bubble-game: dependencies: @@ -1337,10 +1337,10 @@ importers: version: 3.0.8 '@typescript-eslint/eslint-plugin': specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) esbuild: specifier: 0.25.5 version: 0.25.5 @@ -1383,10 +1383,10 @@ importers: version: 22.16.4 '@typescript-eslint/eslint-plugin': specifier: 8.37.0 - version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.37.0 - version: 8.37.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) esbuild: specifier: 0.25.6 version: 0.25.6 @@ -1431,10 +1431,10 @@ importers: version: 22.16.4 '@typescript-eslint/eslint-plugin': specifier: 8.37.0 - version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.37.0 - version: 8.37.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) openapi-types: specifier: 12.1.3 version: 12.1.3 @@ -1462,10 +1462,10 @@ importers: version: 22.15.31 '@typescript-eslint/eslint-plugin': specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) esbuild: specifier: 0.25.5 version: 0.25.5 @@ -1496,13 +1496,13 @@ importers: devDependencies: '@typescript-eslint/parser': specifier: 8.34.0 - version: 8.34.0(eslint@9.28.0)(typescript@5.8.3) + version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.74 version: '@types/serviceworker@0.0.74' eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0) + version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) nodemon: specifier: 3.1.10 version: 3.1.10 @@ -2468,24 +2468,28 @@ packages: resolution: {integrity: sha512-lpHyRyplhGPL5mGEh6M9O5nnKk0Gz4bFI+Zu6tKlPpDUN7XshWvH9C/px4UVm87IAANE0W81CEsNGbS1KlzXpA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-array@0.20.0': - resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.2.1': - resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} + '@eslint/config-helpers@0.3.0': + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.14.0': resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.28.0': - resolution: {integrity: sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==} + '@eslint/js@9.31.0': + resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': @@ -4587,10 +4591,6 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@trysound/sax@0.2.0': - resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} - engines: {node: '>=10.13.0'} - '@tsd/typescript@5.8.3': resolution: {integrity: sha512-oKarNCN1QUhG148M88mtZdOlBZWWGcInquef+U8QL7gwJkRuNo5WS45Fjsd+3hM9cDJWGpqSZ4Oo097KDx4IWA==} engines: {node: '>=14.17'} @@ -5689,6 +5689,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -5793,6 +5798,9 @@ packages: caniuse-lite@1.0.30001718: resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} + caniuse-lite@1.0.30001727: + resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} + canonicalize@1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} @@ -6032,6 +6040,10 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -6043,10 +6055,6 @@ packages: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} engines: {node: '>= 6'} - commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} @@ -6187,8 +6195,8 @@ packages: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - css-tree@2.3.1: - resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css-what@6.1.0: @@ -6203,8 +6211,8 @@ packages: engines: {node: '>=4'} hasBin: true - cssnano-preset-default@7.0.7: - resolution: {integrity: sha512-jW6CG/7PNB6MufOrlovs1TvBTEVmhY45yz+bd0h6nw3h6d+1e+/TX+0fflZ+LzvZombbT5f+KC063w9VoHeHow==} + cssnano-preset-default@7.0.8: + resolution: {integrity: sha512-d+3R2qwrUV3g4LEMOjnndognKirBZISylDZAF/TPeCWVjEwlXS2e4eN4ICkoobRe7pD3H6lltinKVyS1AJhdjQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 @@ -6215,8 +6223,8 @@ packages: peerDependencies: postcss: ^8.4.32 - cssnano@7.0.7: - resolution: {integrity: sha512-evKu7yiDIF7oS+EIpwFlMF730ijRyLFaM2o5cTxRGJR9OKHKkc+qP443ZEVR9kZG0syaAJJCPJyfv5pbrxlSng==} + cssnano@7.1.0: + resolution: {integrity: sha512-Pu3rlKkd0ZtlCUzBrKL1Z4YmhKppjC1H9jo7u1o4qaKqyhvixFgu5qLyNIAOjSTg9DjVPtUqdROq2EfpVMEe+w==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 @@ -6237,6 +6245,11 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + cypress@14.5.2: + resolution: {integrity: sha512-O4E4CEBqDHLDrJD/dfStHPcM+8qFgVVZ89Li7xDU0yL/JxO/V0PEcfF2I8aGa7uA2MGNLkNUAnghPM83UcHOJw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + dashdash@1.14.1: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} @@ -6515,6 +6528,9 @@ packages: electron-to-chromium@1.5.155: resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==} + electron-to-chromium@1.5.186: + resolution: {integrity: sha512-lur7L4BFklgepaJxj4DqPk7vKbTEl0pajNlg2QjE5shefmlmBLm2HvQ7PMf1R/GvlevT/581cop33/quQcfX3A==} + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -6711,6 +6727,10 @@ packages: resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + 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} @@ -6723,8 +6743,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.28.0: - resolution: {integrity: sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==} + eslint@9.31.0: + resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -6737,6 +6757,10 @@ packages: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -7217,8 +7241,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@16.2.0: - resolution: {integrity: sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==} + globals@16.3.0: + resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} engines: {node: '>=18'} globalthis@1.0.3: @@ -7311,6 +7335,10 @@ packages: hash-sum@2.0.0: resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} + hasha@5.2.2: + resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} + engines: {node: '>=8'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -8303,8 +8331,8 @@ packages: mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} - mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} @@ -9184,8 +9212,8 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - pnpm@10.12.1: - resolution: {integrity: sha512-8N2oWA8O6UgcXHmh2Se5Fk8sR46QmSrSaLuyRlpzaYQ5HWMz0sMnkTV4soBK8zR0ylVLopwEqLEwYKcXZ1rjrA==} + pnpm@10.13.1: + resolution: {integrity: sha512-N+vxpcejDV+r4MXfRO6NpMllygxa89urKMOhaBtwolYhjQXIHJwNz3Z+9rhVHrW5YAQrntQwDFkkIzY3fgHPrQ==} engines: {node: '>=18.12'} hasBin: true @@ -9203,14 +9231,14 @@ packages: peerDependencies: postcss: ^8.4.38 - postcss-colormin@7.0.3: - resolution: {integrity: sha512-xZxQcSyIVZbSsl1vjoqZAcMYYdnJsIyG8OvqShuuqf12S88qQboxxEy0ohNCOLwVPXTU+hFHvJPACRL2B5ohTA==} + postcss-colormin@7.0.4: + resolution: {integrity: sha512-ziQuVzQZBROpKpfeDwmrG+Vvlr0YWmY/ZAk99XD+mGEBuEojoFekL41NCsdhyNUtZI7DPOoIWIR7vQQK9xwluw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 - postcss-convert-values@7.0.5: - resolution: {integrity: sha512-0VFhH8nElpIs3uXKnVtotDJJNX0OGYSZmdt4XfSfvOMrFw1jKfpwpZxfC4iN73CTM/MWakDEmsHQXkISYj4BXw==} + postcss-convert-values@7.0.6: + resolution: {integrity: sha512-MD/eb39Mr60hvgrqpXsgbiqluawYg/8K4nKsqRsuDX9f+xN1j6awZCUv/5tLH8ak3vYp/EMXwdcnXvfZYiejCQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 @@ -9245,8 +9273,8 @@ packages: peerDependencies: postcss: ^8.4.32 - postcss-merge-rules@7.0.5: - resolution: {integrity: sha512-ZonhuSwEaWA3+xYbOdJoEReKIBs5eDiBVLAGpYZpNFPzXZcEE5VKR7/qBEQvTZpiwjqhhqEQ+ax5O3VShBj9Wg==} + postcss-merge-rules@7.0.6: + resolution: {integrity: sha512-2jIPT4Tzs8K87tvgCpSukRQ2jjd+hH6Bb8rEEOUDmmhOeTcqDg5fEFK8uKIu+Pvc3//sm3Uu6FRqfyv7YF7+BQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 @@ -9263,8 +9291,8 @@ packages: peerDependencies: postcss: ^8.4.32 - postcss-minify-params@7.0.3: - resolution: {integrity: sha512-vUKV2+f5mtjewYieanLX0xemxIp1t0W0H/D11u+kQV/MWdygOO7xPMkbK+r9P6Lhms8MgzKARF/g5OPXhb8tgg==} + postcss-minify-params@7.0.4: + resolution: {integrity: sha512-3OqqUddfH8c2e7M35W6zIwv7jssM/3miF9cbCSb1iJiWvtguQjlxZGIHK9JRmc8XAKmE2PFGtHSM7g/VcW97sw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 @@ -9311,8 +9339,8 @@ packages: peerDependencies: postcss: ^8.4.32 - postcss-normalize-unicode@7.0.3: - resolution: {integrity: sha512-EcoA29LvG3F+EpOh03iqu+tJY3uYYKzArqKJHxDhUYLa2u58aqGq16K6/AOsXD9yqLN8O6y9mmePKN5cx6krOw==} + postcss-normalize-unicode@7.0.4: + resolution: {integrity: sha512-LvIURTi1sQoZqj8mEIE8R15yvM+OhbR1avynMtI9bUzj5gGKR/gfZFd8O7VMj0QgJaIFzxDwxGl/ASMYAkqO8g==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 @@ -9335,8 +9363,8 @@ packages: peerDependencies: postcss: ^8.4.32 - postcss-reduce-initial@7.0.3: - resolution: {integrity: sha512-RFvkZaqiWtGMlVjlUHpaxGqEL27lgt+Q2Ixjf83CRAzqdo+TsDyGPtJUbPx2MuYIJ+sCQc2TrOvRnhcXQfgIVA==} + postcss-reduce-initial@7.0.4: + resolution: {integrity: sha512-rdIC9IlMBn7zJo6puim58Xd++0HdbvHeHaPgXsimMfG1ijC5A9ULvNLSE0rUKVJOvNMcwewW4Ga21ngyJjY/+Q==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.4.32 @@ -9355,8 +9383,8 @@ packages: resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} engines: {node: '>=4'} - postcss-svgo@7.0.2: - resolution: {integrity: sha512-5Dzy66JlnRM6pkdOTF8+cGsB1fnERTE8Nc+Eed++fOWo1hdsBptCsbG8UuJkgtZt75bRtMJIrPeZmtfANixdFA==} + postcss-svgo@7.1.0: + resolution: {integrity: sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==} engines: {node: ^18.12.0 || ^20.9.0 || >= 18} peerDependencies: postcss: ^8.4.32 @@ -9374,8 +9402,8 @@ packages: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.4: - resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -9880,6 +9908,9 @@ packages: sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -10428,9 +10459,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svgo@3.3.2: - resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} - engines: {node: '>=14.0.0'} + svgo@4.0.0: + resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==} + engines: {node: '>=16'} hasBin: true symbol-tree@3.2.4: @@ -10464,8 +10495,8 @@ packages: resolution: {integrity: sha512-+HRtZ40Vc+6YfCDWCeAsixwxJgMbPY4HHuTgzPYH3JXvqHWUlsCfy+ylXlAKhFNcuLp4xVeWeFBUhDk+7KYUvQ==} engines: {node: '>=14.16'} - terser@5.42.0: - resolution: {integrity: sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==} + terser@5.43.1: + resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} engines: {node: '>=10'} hasBin: true @@ -12526,16 +12557,16 @@ snapshots: '@esbuild/win32-x64@0.25.6': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.28.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0)': dependencies: - eslint: 9.28.0 + eslint: 9.31.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} '@eslint/compat@1.1.1': {} - '@eslint/config-array@0.20.0': + '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 debug: 4.4.1(supports-color@10.0.0) @@ -12543,17 +12574,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.2.1': {} + '@eslint/config-helpers@0.3.0': {} '@eslint/core@0.14.0': dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@0.15.1': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 debug: 4.4.1(supports-color@10.0.0) - espree: 10.3.0 + espree: 10.4.0 globals: 14.0.0 ignore: 5.3.1 import-fresh: 3.3.0 @@ -12563,7 +12598,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.28.0': {} + '@eslint/js@9.31.0': {} '@eslint/object-schema@2.1.6': {} @@ -13116,12 +13151,12 @@ snapshots: '@types/yargs': 17.0.33 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: glob: 10.4.5 magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.8.3) - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) optionalDependencies: typescript: 5.8.3 @@ -13222,15 +13257,15 @@ snapshots: '@misskey-dev/browser-image-resizer@2024.1.0': {} - '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.28.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3))(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0))(eslint@9.28.0)(globals@16.2.0)': + '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0)': dependencies: '@eslint/compat': 1.1.1 - '@stylistic/eslint-plugin': 2.13.0(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/eslint-plugin': 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.34.0(eslint@9.28.0)(typescript@5.8.3) - eslint: 9.28.0 - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0) - globals: 16.2.0 + '@stylistic/eslint-plugin': 2.13.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) + eslint: 9.31.0 + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) + globals: 16.3.0 '@misskey-dev/sharp-read-bmp@1.2.0': dependencies: @@ -14701,13 +14736,13 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@storybook/builder-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': + '@storybook/builder-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) browser-assert: 1.2.1 storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) '@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': dependencies: @@ -14770,11 +14805,11 @@ snapshots: react-dom: 19.1.0(react@19.1.0) storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': + '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@rollup/pluginutils': 5.1.4(rollup@4.42.0) - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) find-up: 5.0.0 magic-string: 0.30.17 @@ -14784,7 +14819,7 @@ snapshots: resolve: 1.22.8 storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) tsconfig-paths: 4.2.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) optionalDependencies: '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) transitivePeerDependencies: @@ -14833,15 +14868,15 @@ snapshots: dependencies: storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) - '@storybook/vue3-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': + '@storybook/vue3-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': dependencies: - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@storybook/vue3': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.16(typescript@5.8.3)) find-package-json: 1.2.0 magic-string: 0.30.17 storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) typescript: 5.8.3 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vue-component-meta: 2.0.16(typescript@5.8.3) vue-docgen-api: 4.75.1(vue@3.5.16(typescript@5.8.3)) transitivePeerDependencies: @@ -14861,11 +14896,11 @@ snapshots: vue: 3.5.16(typescript@5.8.3) vue-component-type-helpers: 3.0.1 - '@stylistic/eslint-plugin@2.13.0(eslint@9.28.0)(typescript@5.8.3)': + '@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/utils': 8.34.0(eslint@9.28.0)(typescript@5.8.3) - eslint: 9.28.0 - eslint-visitor-keys: 4.2.0 + '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) + eslint: 9.31.0 + eslint-visitor-keys: 4.2.1 espree: 10.3.0 estraverse: 5.3.0 picomatch: 4.0.2 @@ -15118,8 +15153,6 @@ snapshots: '@tokenizer/token@0.3.0': {} - '@trysound/sax@0.2.0': {} - '@tsd/typescript@5.8.3': {} '@twemoji/parser@15.0.0': {} @@ -15495,15 +15528,15 @@ snapshots: '@types/node': 22.16.4 optional: true - '@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.34.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.34.0(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.34.0 - '@typescript-eslint/type-utils': 8.34.0(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.34.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.34.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.34.0(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.34.0 - eslint: 9.28.0 + eslint: 9.31.0 graphemer: 1.4.0 ignore: 7.0.4 natural-compare: 1.4.0 @@ -15512,15 +15545,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.37.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.37.0 - '@typescript-eslint/type-utils': 8.37.0(eslint@9.28.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.37.0 - eslint: 9.28.0 + eslint: 9.31.0 graphemer: 1.4.0 ignore: 7.0.4 natural-compare: 1.4.0 @@ -15529,26 +15562,26 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.34.0 '@typescript-eslint/types': 8.34.0 '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.34.0 debug: 4.4.1(supports-color@10.0.0) - eslint: 9.28.0 + eslint: 9.31.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.37.0(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.37.0 '@typescript-eslint/types': 8.37.0 '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.37.0 debug: 4.4.1(supports-color@10.0.0) - eslint: 9.28.0 + eslint: 9.31.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -15589,24 +15622,24 @@ snapshots: dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.34.0(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.34.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.34.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.34.0(eslint@9.31.0)(typescript@5.8.3) debug: 4.4.1(supports-color@10.0.0) - eslint: 9.28.0 + eslint: 9.31.0 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.37.0(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 8.37.0 '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.37.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) debug: 4.4.1(supports-color@10.0.0) - eslint: 9.28.0 + eslint: 9.31.0 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -15648,24 +15681,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.34.0(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.34.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) '@typescript-eslint/scope-manager': 8.34.0 '@typescript-eslint/types': 8.34.0 '@typescript-eslint/typescript-estree': 8.34.0(typescript@5.8.3) - eslint: 9.28.0 + eslint: 9.31.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.37.0(eslint@9.28.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.37.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) '@typescript-eslint/scope-manager': 8.37.0 '@typescript-eslint/types': 8.37.0 '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) - eslint: 9.28.0 + eslint: 9.31.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -15682,12 +15715,12 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': dependencies: - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vue: 3.5.16(typescript@5.8.3) - '@vitest/coverage-v8@3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': + '@vitest/coverage-v8@3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -15702,7 +15735,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) transitivePeerDependencies: - supports-color @@ -15721,14 +15754,14 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3))': + '@vitest/mocker@3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: '@vitest/spy': 3.2.3 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: msw: 2.10.2(@types/node@22.15.31)(typescript@5.8.3) - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) '@vitest/pretty-format@2.0.5': dependencies: @@ -16489,6 +16522,13 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.5) + browserslist@4.25.1: + dependencies: + caniuse-lite: 1.0.30001727 + electron-to-chromium: 1.5.186 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.1) + bser@2.1.1: dependencies: node-int64: 0.4.0 @@ -16616,13 +16656,15 @@ snapshots: caniuse-api@3.0.0: dependencies: - browserslist: 4.24.5 + browserslist: 4.25.1 caniuse-lite: 1.0.30001718 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 caniuse-lite@1.0.30001718: {} + caniuse-lite@1.0.30001727: {} + canonicalize@1.0.8: {} canvas-confetti@1.9.3: {} @@ -16853,14 +16895,14 @@ snapshots: commander@10.0.1: {} + commander@11.1.0: {} + commander@12.1.0: {} commander@2.20.3: {} commander@6.2.1: {} - commander@7.2.0: {} - commander@8.3.0: {} commander@9.5.0: {} @@ -17004,9 +17046,9 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-declaration-sorter@7.2.0(postcss@8.5.4): + css-declaration-sorter@7.2.0(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 css-select@5.1.0: dependencies: @@ -17021,9 +17063,9 @@ snapshots: mdn-data: 2.0.28 source-map-js: 1.2.1 - css-tree@2.3.1: + css-tree@3.1.0: dependencies: - mdn-data: 2.0.30 + mdn-data: 2.12.2 source-map-js: 1.2.1 css-what@6.1.0: {} @@ -17032,49 +17074,49 @@ snapshots: cssesc@3.0.0: {} - cssnano-preset-default@7.0.7(postcss@8.5.4): + cssnano-preset-default@7.0.8(postcss@8.5.6): dependencies: - browserslist: 4.24.5 - css-declaration-sorter: 7.2.0(postcss@8.5.4) - cssnano-utils: 5.0.1(postcss@8.5.4) - postcss: 8.5.4 - postcss-calc: 10.1.1(postcss@8.5.4) - postcss-colormin: 7.0.3(postcss@8.5.4) - postcss-convert-values: 7.0.5(postcss@8.5.4) - postcss-discard-comments: 7.0.4(postcss@8.5.4) - postcss-discard-duplicates: 7.0.2(postcss@8.5.4) - postcss-discard-empty: 7.0.1(postcss@8.5.4) - postcss-discard-overridden: 7.0.1(postcss@8.5.4) - postcss-merge-longhand: 7.0.5(postcss@8.5.4) - postcss-merge-rules: 7.0.5(postcss@8.5.4) - postcss-minify-font-values: 7.0.1(postcss@8.5.4) - postcss-minify-gradients: 7.0.1(postcss@8.5.4) - postcss-minify-params: 7.0.3(postcss@8.5.4) - postcss-minify-selectors: 7.0.5(postcss@8.5.4) - postcss-normalize-charset: 7.0.1(postcss@8.5.4) - postcss-normalize-display-values: 7.0.1(postcss@8.5.4) - postcss-normalize-positions: 7.0.1(postcss@8.5.4) - postcss-normalize-repeat-style: 7.0.1(postcss@8.5.4) - postcss-normalize-string: 7.0.1(postcss@8.5.4) - postcss-normalize-timing-functions: 7.0.1(postcss@8.5.4) - postcss-normalize-unicode: 7.0.3(postcss@8.5.4) - postcss-normalize-url: 7.0.1(postcss@8.5.4) - postcss-normalize-whitespace: 7.0.1(postcss@8.5.4) - postcss-ordered-values: 7.0.2(postcss@8.5.4) - postcss-reduce-initial: 7.0.3(postcss@8.5.4) - postcss-reduce-transforms: 7.0.1(postcss@8.5.4) - postcss-svgo: 7.0.2(postcss@8.5.4) - postcss-unique-selectors: 7.0.4(postcss@8.5.4) + browserslist: 4.25.1 + css-declaration-sorter: 7.2.0(postcss@8.5.6) + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 10.1.1(postcss@8.5.6) + postcss-colormin: 7.0.4(postcss@8.5.6) + postcss-convert-values: 7.0.6(postcss@8.5.6) + postcss-discard-comments: 7.0.4(postcss@8.5.6) + postcss-discard-duplicates: 7.0.2(postcss@8.5.6) + postcss-discard-empty: 7.0.1(postcss@8.5.6) + postcss-discard-overridden: 7.0.1(postcss@8.5.6) + postcss-merge-longhand: 7.0.5(postcss@8.5.6) + postcss-merge-rules: 7.0.6(postcss@8.5.6) + postcss-minify-font-values: 7.0.1(postcss@8.5.6) + postcss-minify-gradients: 7.0.1(postcss@8.5.6) + postcss-minify-params: 7.0.4(postcss@8.5.6) + postcss-minify-selectors: 7.0.5(postcss@8.5.6) + postcss-normalize-charset: 7.0.1(postcss@8.5.6) + postcss-normalize-display-values: 7.0.1(postcss@8.5.6) + postcss-normalize-positions: 7.0.1(postcss@8.5.6) + postcss-normalize-repeat-style: 7.0.1(postcss@8.5.6) + postcss-normalize-string: 7.0.1(postcss@8.5.6) + postcss-normalize-timing-functions: 7.0.1(postcss@8.5.6) + postcss-normalize-unicode: 7.0.4(postcss@8.5.6) + postcss-normalize-url: 7.0.1(postcss@8.5.6) + postcss-normalize-whitespace: 7.0.1(postcss@8.5.6) + postcss-ordered-values: 7.0.2(postcss@8.5.6) + postcss-reduce-initial: 7.0.4(postcss@8.5.6) + postcss-reduce-transforms: 7.0.1(postcss@8.5.6) + postcss-svgo: 7.1.0(postcss@8.5.6) + postcss-unique-selectors: 7.0.4(postcss@8.5.6) - cssnano-utils@5.0.1(postcss@8.5.4): + cssnano-utils@5.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 - cssnano@7.0.7(postcss@8.5.4): + cssnano@7.1.0(postcss@8.5.6): dependencies: - cssnano-preset-default: 7.0.7(postcss@8.5.4) + cssnano-preset-default: 7.0.8(postcss@8.5.6) lilconfig: 3.1.3 - postcss: 8.5.4 + postcss: 8.5.6 csso@5.0.5: dependencies: @@ -17133,6 +17175,53 @@ snapshots: untildify: 4.0.0 yauzl: 2.10.0 + cypress@14.5.2: + dependencies: + '@cypress/request': 3.0.8 + '@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 + ci-info: 4.1.0 + cli-cursor: 3.1.0 + cli-table3: 0.6.1 + commander: 6.2.1 + common-tags: 1.8.2 + dayjs: 1.11.13 + debug: 4.4.1(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 + hasha: 5.2.2 + 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.7.2 + supports-color: 8.1.1 + tmp: 0.2.3 + tree-kill: 1.2.2 + untildify: 4.0.0 + yauzl: 2.10.0 + dashdash@1.14.1: dependencies: assert-plus: 1.0.0 @@ -17415,6 +17504,8 @@ snapshots: electron-to-chromium@1.5.155: {} + electron-to-chromium@1.5.186: {} + emittery@0.13.1: {} emoji-regex@8.0.0: {} @@ -17682,17 +17773,27 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.28.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: - '@typescript-eslint/parser': 8.34.0(eslint@9.28.0)(typescript@5.8.3) - eslint: 9.28.0 + '@typescript-eslint/parser': 8.34.0(eslint@9.31.0)(typescript@5.8.3) + eslint: 9.31.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint@9.28.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0): + dependencies: + debug: 3.2.7(supports-color@8.1.1) + optionalDependencies: + '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) + eslint: 9.31.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -17701,9 +17802,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 - eslint: 9.28.0 + eslint: 9.31.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.28.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -17715,21 +17816,50 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.34.0(eslint@9.28.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.34.0(eslint@9.31.0)(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-vue@10.2.0(eslint@9.28.0)(vue-eslint-parser@10.1.3(eslint@9.28.0)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) - eslint: 9.28.0 + '@rtsao/scc': 1.1.0 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + 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.31.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0) + hasown: 2.0.2 + is-core-module: 2.15.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + string.prototype.trimend: 1.0.8 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-vue@10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) + eslint: 9.31.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.1.2 semver: 7.7.2 - vue-eslint-parser: 10.1.3(eslint@9.28.0) + vue-eslint-parser: 10.1.3(eslint@9.31.0) xml-name-validator: 4.0.0 eslint-rule-docs@1.1.235: {} @@ -17739,21 +17869,26 @@ snapshots: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-visitor-keys@3.4.3: {} eslint-visitor-keys@4.2.0: {} eslint-visitor-keys@4.2.1: {} - eslint@9.28.0: + eslint@9.31.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.0 - '@eslint/config-helpers': 0.2.1 - '@eslint/core': 0.14.0 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.0 + '@eslint/core': 0.15.1 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.28.0 + '@eslint/js': 9.31.0 '@eslint/plugin-kit': 0.3.1 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 @@ -17765,9 +17900,9 @@ snapshots: cross-spawn: 7.0.6 debug: 4.4.1(supports-color@10.0.0) escape-string-regexp: 4.0.0 - eslint-scope: 8.3.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -17791,6 +17926,12 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.0 + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + esprima@4.0.1: {} esquery@1.6.0: @@ -18406,7 +18547,7 @@ snapshots: globals@14.0.0: {} - globals@16.2.0: {} + globals@16.3.0: {} globalthis@1.0.3: dependencies: @@ -18503,6 +18644,11 @@ snapshots: hash-sum@2.0.0: {} + hasha@5.2.2: + dependencies: + is-stream: 2.0.1 + type-fest: 0.8.1 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -19812,7 +19958,7 @@ snapshots: mdn-data@2.0.28: {} - mdn-data@2.0.30: {} + mdn-data@2.12.2: {} media-typer@0.3.0: {} @@ -20785,7 +20931,7 @@ snapshots: pngjs@5.0.0: {} - pnpm@10.12.1: {} + pnpm@10.13.1: {} polished@4.2.2: dependencies: @@ -20793,142 +20939,142 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-calc@10.1.1(postcss@8.5.4): + postcss-calc@10.1.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-selector-parser: 7.1.0 postcss-value-parser: 4.2.0 - postcss-colormin@7.0.3(postcss@8.5.4): + postcss-colormin@7.0.4(postcss@8.5.6): dependencies: - browserslist: 4.24.5 + browserslist: 4.25.1 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-convert-values@7.0.5(postcss@8.5.4): + postcss-convert-values@7.0.6(postcss@8.5.6): dependencies: - browserslist: 4.24.5 - postcss: 8.5.4 + browserslist: 4.25.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-discard-comments@7.0.4(postcss@8.5.4): + postcss-discard-comments@7.0.4(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-selector-parser: 7.1.0 - postcss-discard-duplicates@7.0.2(postcss@8.5.4): + postcss-discard-duplicates@7.0.2(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 - postcss-discard-empty@7.0.1(postcss@8.5.4): + postcss-discard-empty@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 - postcss-discard-overridden@7.0.1(postcss@8.5.4): + postcss-discard-overridden@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 - postcss-merge-longhand@7.0.5(postcss@8.5.4): + postcss-merge-longhand@7.0.5(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - stylehacks: 7.0.5(postcss@8.5.4) + stylehacks: 7.0.5(postcss@8.5.6) - postcss-merge-rules@7.0.5(postcss@8.5.4): + postcss-merge-rules@7.0.6(postcss@8.5.6): dependencies: - browserslist: 4.24.5 + browserslist: 4.25.1 caniuse-api: 3.0.0 - cssnano-utils: 5.0.1(postcss@8.5.4) - postcss: 8.5.4 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 postcss-selector-parser: 7.1.0 - postcss-minify-font-values@7.0.1(postcss@8.5.4): + postcss-minify-font-values@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-minify-gradients@7.0.1(postcss@8.5.4): + postcss-minify-gradients@7.0.1(postcss@8.5.6): dependencies: colord: 2.9.3 - cssnano-utils: 5.0.1(postcss@8.5.4) - postcss: 8.5.4 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-minify-params@7.0.3(postcss@8.5.4): + postcss-minify-params@7.0.4(postcss@8.5.6): dependencies: - browserslist: 4.24.5 - cssnano-utils: 5.0.1(postcss@8.5.4) - postcss: 8.5.4 + browserslist: 4.25.1 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-minify-selectors@7.0.5(postcss@8.5.4): + postcss-minify-selectors@7.0.5(postcss@8.5.6): dependencies: cssesc: 3.0.0 - postcss: 8.5.4 + postcss: 8.5.6 postcss-selector-parser: 7.1.0 - postcss-normalize-charset@7.0.1(postcss@8.5.4): + postcss-normalize-charset@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 - postcss-normalize-display-values@7.0.1(postcss@8.5.4): + postcss-normalize-display-values@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-positions@7.0.1(postcss@8.5.4): + postcss-normalize-positions@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@7.0.1(postcss@8.5.4): + postcss-normalize-repeat-style@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-string@7.0.1(postcss@8.5.4): + postcss-normalize-string@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@7.0.1(postcss@8.5.4): + postcss-normalize-timing-functions@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@7.0.3(postcss@8.5.4): + postcss-normalize-unicode@7.0.4(postcss@8.5.6): dependencies: - browserslist: 4.24.5 - postcss: 8.5.4 + browserslist: 4.25.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-url@7.0.1(postcss@8.5.4): + postcss-normalize-url@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@7.0.1(postcss@8.5.4): + postcss-normalize-whitespace@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-ordered-values@7.0.2(postcss@8.5.4): + postcss-ordered-values@7.0.2(postcss@8.5.6): dependencies: - cssnano-utils: 5.0.1(postcss@8.5.4) - postcss: 8.5.4 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-reduce-initial@7.0.3(postcss@8.5.4): + postcss-reduce-initial@7.0.4(postcss@8.5.6): dependencies: - browserslist: 4.24.5 + browserslist: 4.25.1 caniuse-api: 3.0.0 - postcss: 8.5.4 + postcss: 8.5.6 - postcss-reduce-transforms@7.0.1(postcss@8.5.4): + postcss-reduce-transforms@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 postcss-selector-parser@6.1.2: @@ -20941,15 +21087,15 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@7.0.2(postcss@8.5.4): + postcss-svgo@7.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - svgo: 3.3.2 + svgo: 4.0.0 - postcss-unique-selectors@7.0.4(postcss@8.5.4): + postcss-unique-selectors@7.0.4(postcss@8.5.6): dependencies: - postcss: 8.5.4 + postcss: 8.5.6 postcss-selector-parser: 7.1.0 postcss-value-parser@4.2.0: {} @@ -20960,7 +21106,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.4: + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -21557,6 +21703,8 @@ snapshots: sax@1.2.4: {} + sax@1.4.1: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -22137,10 +22285,10 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 5.3.1 - stylehacks@7.0.5(postcss@8.5.4): + stylehacks@7.0.5(postcss@8.5.6): dependencies: - browserslist: 4.24.5 - postcss: 8.5.4 + browserslist: 4.25.1 + postcss: 8.5.6 postcss-selector-parser: 7.1.0 superagent@10.2.1: @@ -22185,15 +22333,15 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svgo@3.3.2: + svgo@4.0.0: dependencies: - '@trysound/sax': 0.2.0 - commander: 7.2.0 + commander: 11.1.0 css-select: 5.1.0 - css-tree: 2.3.1 + css-tree: 3.1.0 css-what: 6.1.0 csso: 5.0.5 picocolors: 1.1.1 + sax: 1.4.1 symbol-tree@3.2.4: {} @@ -22245,7 +22393,7 @@ snapshots: dependencies: execa: 6.1.0 - terser@5.42.0: + terser@5.43.1: dependencies: '@jridgewell/source-map': 0.3.6 acorn: 8.15.0 @@ -22629,6 +22777,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.1.3(browserslist@4.25.1): + dependencies: + browserslist: 4.25.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js-replace@1.0.1: {} uri-js@4.4.1: @@ -22703,13 +22857,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3): + vite-node@3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): dependencies: cac: 6.7.14 debug: 4.4.1(supports-color@10.0.0) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) transitivePeerDependencies: - '@types/node' - jiti @@ -22726,7 +22880,7 @@ snapshots: vite-plugin-turbosnap@1.0.3: {} - vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3): + vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): dependencies: esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) @@ -22738,18 +22892,18 @@ snapshots: '@types/node': 22.15.31 fsevents: 2.3.3 sass: 1.89.2 - terser: 5.42.0 + terser: 5.43.1 tsx: 4.20.3 - vitest-fetch-mock@0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)): + vitest-fetch-mock@0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)): dependencies: - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) - vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3): + vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.3 - '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3)) + '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@vitest/pretty-format': 3.2.3 '@vitest/runner': 3.2.3 '@vitest/snapshot': 3.2.3 @@ -22767,8 +22921,8 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.0 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) - vite-node: 3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.42.0)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite-node: 3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -22844,10 +22998,10 @@ snapshots: vue: 3.5.16(typescript@5.8.3) vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.16(typescript@5.8.3)) - vue-eslint-parser@10.1.3(eslint@9.28.0): + vue-eslint-parser@10.1.3(eslint@9.31.0): dependencies: debug: 4.4.1(supports-color@10.0.0) - eslint: 9.28.0 + eslint: 9.31.0 eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 espree: 10.3.0 From 1b791258ce3f2a693f8f6e9c195ea02826583258 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 12:14:19 +0900 Subject: [PATCH 43/85] fix(deps): update [frontend] update dependencies (#16202) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/frontend-embed/package.json | 36 +- packages/frontend-shared/package.json | 14 +- packages/frontend/package.json | 50 +- packages/icons-subsetter/package.json | 8 +- packages/misskey-bubble-game/package.json | 10 +- packages/misskey-reversi/package.json | 10 +- packages/sw/package.json | 6 +- pnpm-lock.yaml | 2636 ++++++++++++--------- 8 files changed, 1602 insertions(+), 1168 deletions(-) diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json index 5e73c26daf..bae5b0ebd3 100644 --- a/packages/frontend-embed/package.json +++ b/packages/frontend-embed/package.json @@ -13,10 +13,10 @@ "@discordapp/twemoji": "15.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "6.0.2", - "@rollup/pluginutils": "5.1.4", + "@rollup/pluginutils": "5.2.0", "@twemoji/parser": "15.1.1", "@vitejs/plugin-vue": "5.2.4", - "@vue/compiler-sfc": "3.5.16", + "@vue/compiler-sfc": "3.5.17", "astring": "1.9.0", "buraha": "0.0.1", "estree-walker": "3.0.3", @@ -26,46 +26,46 @@ "mfm-js": "0.24.0", "misskey-js": "workspace:*", "punycode.js": "2.3.1", - "rollup": "4.42.0", + "rollup": "4.45.1", "sass": "1.89.2", - "shiki": "3.6.0", + "shiki": "3.8.0", "tinycolor2": "1.6.0", "tsc-alias": "1.8.16", "tsconfig-paths": "4.2.0", "typescript": "5.8.3", "uuid": "11.1.0", "vite": "6.3.5", - "vue": "3.5.16" + "vue": "3.5.17" }, "devDependencies": { - "@misskey-dev/summaly": "5.2.1", + "@misskey-dev/summaly": "5.2.2", "@tabler/icons-webfont": "3.34.0", "@testing-library/vue": "8.1.0", "@types/estree": "1.0.8", "@types/micromatch": "4.0.9", - "@types/node": "22.15.31", + "@types/node": "22.16.4", "@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/tinycolor2": "1.4.6", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", - "@vitest/coverage-v8": "3.2.3", - "@vue/runtime-core": "3.5.16", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@vitest/coverage-v8": "3.2.4", + "@vue/runtime-core": "3.5.17", "acorn": "8.15.0", "cross-env": "7.0.3", - "eslint-plugin-import": "2.31.0", - "eslint-plugin-vue": "10.2.0", + "eslint-plugin-import": "2.32.0", + "eslint-plugin-vue": "10.3.0", "fast-glob": "3.3.3", "happy-dom": "17.6.3", "intersection-observer": "0.12.2", "micromatch": "4.0.8", - "msw": "2.10.2", + "msw": "2.10.4", "nodemon": "3.1.10", - "prettier": "3.5.3", + "prettier": "3.6.2", "start-server-and-test": "2.0.12", "vite-plugin-turbosnap": "1.0.3", - "vue-component-type-helpers": "2.2.10", - "vue-eslint-parser": "10.1.3", - "vue-tsc": "2.2.10" + "vue-component-type-helpers": "2.2.12", + "vue-eslint-parser": "10.2.0", + "vue-tsc": "2.2.12" } } diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index bd9bbbe7b6..7caa7fd283 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -21,20 +21,20 @@ "lint": "pnpm typecheck && pnpm eslint" }, "devDependencies": { - "@types/node": "22.15.31", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", - "esbuild": "0.25.5", - "eslint-plugin-vue": "10.2.0", + "@types/node": "22.16.4", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "esbuild": "0.25.6", + "eslint-plugin-vue": "10.3.0", "nodemon": "3.1.10", "typescript": "5.8.3", - "vue-eslint-parser": "10.1.3" + "vue-eslint-parser": "10.2.0" }, "files": [ "js-built" ], "dependencies": { "misskey-js": "workspace:*", - "vue": "3.5.16" + "vue": "3.5.17" } } diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 0dbd94362b..b8964549f4 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -23,19 +23,19 @@ "@misskey-dev/browser-image-resizer": "2024.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "6.0.2", - "@rollup/pluginutils": "5.1.4", - "@sentry/vue": "9.27.0", + "@rollup/pluginutils": "5.2.0", + "@sentry/vue": "9.39.0", "@syuilo/aiscript": "0.19.0", "@twemoji/parser": "15.1.1", "@vitejs/plugin-vue": "5.2.4", - "@vue/compiler-sfc": "3.5.16", + "@vue/compiler-sfc": "3.5.17", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15", "analytics": "0.8.16", "astring": "1.9.0", "broadcast-channel": "7.1.0", "buraha": "0.0.1", "canvas-confetti": "1.9.3", - "chart.js": "4.4.9", + "chart.js": "4.5.0", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "2.1.1", "chartjs-plugin-gradient": "0.6.1", @@ -60,13 +60,13 @@ "misskey-reversi": "workspace:*", "photoswipe": "5.4.4", "punycode.js": "2.3.1", - "rollup": "4.42.0", + "rollup": "4.45.1", "sanitize-html": "2.17.0", "sass": "1.89.2", - "shiki": "3.6.0", + "shiki": "3.8.0", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", - "three": "0.177.0", + "three": "0.178.0", "throttle-debounce": "5.0.2", "tinycolor2": "1.6.0", "tsc-alias": "1.8.16", @@ -74,12 +74,12 @@ "typescript": "5.8.3", "v-code-diff": "1.13.1", "vite": "6.3.5", - "vue": "3.5.16", + "vue": "3.5.17", "vuedraggable": "next", "wanakana": "5.3.1" }, "devDependencies": { - "@misskey-dev/summaly": "5.2.1", + "@misskey-dev/summaly": "5.2.2", "@storybook/addon-actions": "8.6.14", "@storybook/addon-essentials": "8.6.14", "@storybook/addon-interactions": "8.6.14", @@ -104,32 +104,32 @@ "@types/estree": "1.0.8", "@types/matter-js": "0.19.8", "@types/micromatch": "4.0.9", - "@types/node": "22.15.31", + "@types/node": "22.16.4", "@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/sanitize-html": "2.16.0", "@types/seedrandom": "3.0.8", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/ws": "8.18.1", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", - "@vitest/coverage-v8": "3.2.3", - "@vue/compiler-core": "3.5.16", - "@vue/runtime-core": "3.5.16", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@vitest/coverage-v8": "3.2.4", + "@vue/compiler-core": "3.5.17", + "@vue/runtime-core": "3.5.17", "acorn": "8.15.0", "cross-env": "7.0.3", - "cypress": "14.4.1", - "eslint-plugin-import": "2.31.0", - "eslint-plugin-vue": "10.2.0", + "cypress": "14.5.2", + "eslint-plugin-import": "2.32.0", + "eslint-plugin-vue": "10.3.0", "fast-glob": "3.3.3", "happy-dom": "17.6.3", "intersection-observer": "0.12.2", "micromatch": "4.0.8", - "minimatch": "10.0.1", - "msw": "2.10.2", + "minimatch": "10.0.3", + "msw": "2.10.4", "msw-storybook-addon": "2.0.5", "nodemon": "3.1.10", - "prettier": "3.5.3", + "prettier": "3.6.2", "react": "19.1.0", "react-dom": "19.1.0", "seedrandom": "3.0.5", @@ -137,10 +137,10 @@ "storybook": "8.6.14", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "vite-plugin-turbosnap": "1.0.3", - "vitest": "3.2.3", + "vitest": "3.2.4", "vitest-fetch-mock": "0.4.5", - "vue-component-type-helpers": "2.2.10", - "vue-eslint-parser": "10.1.3", - "vue-tsc": "2.2.10" + "vue-component-type-helpers": "2.2.12", + "vue-eslint-parser": "10.2.0", + "vue-tsc": "2.2.12" } } diff --git a/packages/icons-subsetter/package.json b/packages/icons-subsetter/package.json index 9581ad4206..a0ac7b3435 100644 --- a/packages/icons-subsetter/package.json +++ b/packages/icons-subsetter/package.json @@ -11,16 +11,16 @@ "lint": "pnpm typecheck && pnpm eslint" }, "devDependencies": { - "@types/node": "22.15.31", + "@types/node": "22.16.4", "@types/wawoff2": "1.0.2", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0" }, "dependencies": { "@tabler/icons-webfont": "3.34.0", "harfbuzzjs": "0.4.7", "tiny-glob": "0.2.9", - "tsx": "4.19.4", + "tsx": "4.20.3", "typescript": "5.8.3", "wawoff2": "2.0.1" }, diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json index 7593a33bc1..73208be12f 100644 --- a/packages/misskey-bubble-game/package.json +++ b/packages/misskey-bubble-game/package.json @@ -24,14 +24,14 @@ "devDependencies": { "@types/matter-js": "0.19.8", "@types/seedrandom": "3.0.8", - "@types/node": "22.15.31", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", + "@types/node": "22.16.4", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", "nodemon": "3.1.10", "execa": "9.6.0", "typescript": "5.8.3", - "esbuild": "0.25.5", - "glob": "11.0.2" + "esbuild": "0.25.6", + "glob": "11.0.3" }, "files": [ "built" diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json index c4a4b8bc75..e7158fa4f4 100644 --- a/packages/misskey-reversi/package.json +++ b/packages/misskey-reversi/package.json @@ -22,14 +22,14 @@ "lint": "pnpm typecheck && pnpm eslint" }, "devDependencies": { - "@types/node": "22.15.31", - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", + "@types/node": "22.16.4", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", "execa": "9.6.0", "nodemon": "3.1.10", "typescript": "5.8.3", - "esbuild": "0.25.5", - "glob": "11.0.2" + "esbuild": "0.25.6", + "glob": "11.0.3" }, "files": [ "built" diff --git a/packages/sw/package.json b/packages/sw/package.json index 8981242acc..8ebd8f44ae 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -9,14 +9,14 @@ "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { - "esbuild": "0.25.5", + "esbuild": "0.25.6", "idb-keyval": "6.2.2", "misskey-js": "workspace:*" }, "devDependencies": { - "@typescript-eslint/parser": "8.34.0", + "@typescript-eslint/parser": "8.37.0", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74", - "eslint-plugin-import": "2.31.0", + "eslint-plugin-import": "2.32.0", "nodemon": "3.1.10", "typescript": "5.8.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b127a8e667..08711cac71 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,7 +54,7 @@ importers: devDependencies: '@misskey-dev/eslint-plugin': specifier: 2.1.0 - version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0) + version: 2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0) '@types/node': specifier: 22.16.4 version: 22.16.4 @@ -455,7 +455,7 @@ importers: version: 10.4.19(@nestjs/common@11.1.3(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.3) '@sentry/vue': specifier: 9.28.0 - version: 9.28.0(vue@3.5.16(typescript@5.8.3)) + version: 9.28.0(vue@3.5.17(typescript@5.8.3)) '@simplewebauthn/types': specifier: 12.0.0 version: 12.0.0 @@ -720,16 +720,16 @@ importers: version: 2024.1.0 '@rollup/plugin-json': specifier: 6.1.0 - version: 6.1.0(rollup@4.42.0) + version: 6.1.0(rollup@4.45.1) '@rollup/plugin-replace': specifier: 6.0.2 - version: 6.0.2(rollup@4.42.0) + version: 6.0.2(rollup@4.45.1) '@rollup/pluginutils': - specifier: 5.1.4 - version: 5.1.4(rollup@4.42.0) + specifier: 5.2.0 + version: 5.2.0(rollup@4.45.1) '@sentry/vue': - specifier: 9.27.0 - version: 9.27.0(vue@3.5.16(typescript@5.8.3)) + specifier: 9.39.0 + version: 9.39.0(vue@3.5.17(typescript@5.8.3)) '@syuilo/aiscript': specifier: 0.19.0 version: 0.19.0 @@ -738,10 +738,10 @@ importers: version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.4 - version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) + version: 5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3)) '@vue/compiler-sfc': - specifier: 3.5.16 - version: 3.5.16 + specifier: 3.5.17 + version: 3.5.17 aiscript-vscode: specifier: github:aiscript-dev/aiscript-vscode#v0.1.15 version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/c3cde89e79a41d93540cf8a48cd619c3f2dcb1b7 @@ -761,20 +761,20 @@ importers: specifier: 1.9.3 version: 1.9.3 chart.js: - specifier: 4.4.9 - version: 4.4.9 + specifier: 4.5.0 + version: 4.5.0 chartjs-adapter-date-fns: specifier: 3.0.0 - version: 3.0.0(chart.js@4.4.9)(date-fns@4.1.0) + version: 3.0.0(chart.js@4.5.0)(date-fns@4.1.0) chartjs-chart-matrix: specifier: 2.1.1 - version: 2.1.1(chart.js@4.4.9) + version: 2.1.1(chart.js@4.5.0) chartjs-plugin-gradient: specifier: 0.6.1 - version: 0.6.1(chart.js@4.4.9) + version: 0.6.1(chart.js@4.5.0) chartjs-plugin-zoom: specifier: 2.2.0 - version: 2.2.0(chart.js@4.4.9) + version: 2.2.0(chart.js@4.5.0) chromatic: specifier: 11.29.0 version: 11.29.0 @@ -836,8 +836,8 @@ importers: specifier: 2.3.1 version: 2.3.1 rollup: - specifier: 4.42.0 - version: 4.42.0 + specifier: 4.45.1 + version: 4.45.1 sanitize-html: specifier: 2.17.0 version: 2.17.0 @@ -845,8 +845,8 @@ importers: specifier: 1.89.2 version: 1.89.2 shiki: - specifier: 3.6.0 - version: 3.6.0 + specifier: 3.8.0 + version: 3.8.0 strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 @@ -854,8 +854,8 @@ importers: specifier: 3.1.0 version: 3.1.0 three: - specifier: 0.177.0 - version: 0.177.0 + specifier: 0.178.0 + version: 0.178.0 throttle-debounce: specifier: 5.0.2 version: 5.0.2 @@ -873,83 +873,83 @@ importers: version: 5.8.3 v-code-diff: specifier: 1.13.1 - version: 1.13.1(vue@3.5.16(typescript@5.8.3)) + version: 1.13.1(vue@3.5.17(typescript@5.8.3)) vite: specifier: 6.3.5 - version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + version: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vue: - specifier: 3.5.16 - version: 3.5.16(typescript@5.8.3) + specifier: 3.5.17 + version: 3.5.17(typescript@5.8.3) vuedraggable: specifier: next - version: 4.1.0(vue@3.5.16(typescript@5.8.3)) + version: 4.1.0(vue@3.5.17(typescript@5.8.3)) wanakana: specifier: 5.3.1 version: 5.3.1 devDependencies: '@misskey-dev/summaly': - specifier: 5.2.1 - version: 5.2.1 + specifier: 5.2.2 + version: 5.2.2 '@storybook/addon-actions': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/addon-essentials': specifier: 8.6.14 - version: 8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/addon-interactions': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/addon-links': specifier: 8.6.14 - version: 8.6.14(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/addon-mdx-gfm': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/addon-storysource': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/blocks': specifier: 8.6.14 - version: 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/components': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/core-events': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/manager-api': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/preview-api': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/react': specifier: 8.6.14 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3) '@storybook/react-vite': specifier: 8.6.14 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.45.1)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@storybook/test': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/theming': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/types': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/vue3': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.16(typescript@5.8.3)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vue@3.5.17(typescript@5.8.3)) '@storybook/vue3-vite': specifier: 8.6.14 - version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) + version: 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3)) '@tabler/icons-webfont': specifier: 3.34.0 version: 3.34.0 '@testing-library/vue': specifier: 8.1.0 - version: 8.1.0(@vue/compiler-sfc@3.5.16)(@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3)) + version: 8.1.0(@vue/compiler-sfc@3.5.17)(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) '@types/canvas-confetti': specifier: 1.9.0 version: 1.9.0 @@ -963,8 +963,8 @@ importers: specifier: 4.0.9 version: 4.0.9 '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@types/punycode.js': specifier: npm:@types/punycode@2.1.4 version: '@types/punycode@2.1.4' @@ -984,20 +984,20 @@ importers: specifier: 8.18.1 version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) '@vitest/coverage-v8': - specifier: 3.2.3 - version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + specifier: 3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@vue/compiler-core': - specifier: 3.5.16 - version: 3.5.16 + specifier: 3.5.17 + version: 3.5.17 '@vue/runtime-core': - specifier: 3.5.16 - version: 3.5.16 + specifier: 3.5.17 + version: 3.5.17 acorn: specifier: 8.15.0 version: 8.15.0 @@ -1005,14 +1005,14 @@ importers: specifier: 7.0.3 version: 7.0.3 cypress: - specifier: 14.4.1 - version: 14.4.1 + specifier: 14.5.2 + version: 14.5.2 eslint-plugin-import: - specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) + specifier: 2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) eslint-plugin-vue: - specifier: 10.2.0 - version: 10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)) + specifier: 10.3.0 + version: 10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -1026,20 +1026,20 @@ importers: specifier: 4.0.8 version: 4.0.8 minimatch: - specifier: 10.0.1 - version: 10.0.1 + specifier: 10.0.3 + version: 10.0.3 msw: - specifier: 2.10.2 - version: 2.10.2(@types/node@22.15.31)(typescript@5.8.3) + specifier: 2.10.4 + version: 2.10.4(@types/node@22.16.4)(typescript@5.8.3) msw-storybook-addon: specifier: 2.0.5 - version: 2.0.5(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3)) + version: 2.0.5(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3)) nodemon: specifier: 3.1.10 version: 3.1.10 prettier: - specifier: 3.5.3 - version: 3.5.3 + specifier: 3.6.2 + version: 3.6.2 react: specifier: 19.1.0 version: 19.1.0 @@ -1054,28 +1054,28 @@ importers: version: 2.0.12 storybook: specifier: 8.6.14 - version: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + version: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) 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.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) vite-plugin-turbosnap: specifier: 1.0.3 version: 1.0.3 vitest: - specifier: 3.2.3 - version: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + specifier: 3.2.4 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vitest-fetch-mock: specifier: 0.4.5 - version: 0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) vue-component-type-helpers: - specifier: 2.2.10 - version: 2.2.10 + specifier: 2.2.12 + version: 2.2.12 vue-eslint-parser: - specifier: 10.1.3 - version: 10.1.3(eslint@9.31.0) + specifier: 10.2.0 + version: 10.2.0(eslint@9.31.0) vue-tsc: - specifier: 2.2.10 - version: 2.2.10(typescript@5.8.3) + specifier: 2.2.12 + version: 2.2.12(typescript@5.8.3) packages/frontend-embed: dependencies: @@ -1084,22 +1084,22 @@ importers: version: 15.1.0 '@rollup/plugin-json': specifier: 6.1.0 - version: 6.1.0(rollup@4.42.0) + version: 6.1.0(rollup@4.45.1) '@rollup/plugin-replace': specifier: 6.0.2 - version: 6.0.2(rollup@4.42.0) + version: 6.0.2(rollup@4.45.1) '@rollup/pluginutils': - specifier: 5.1.4 - version: 5.1.4(rollup@4.42.0) + specifier: 5.2.0 + version: 5.2.0(rollup@4.45.1) '@twemoji/parser': specifier: 15.1.1 version: 15.1.1 '@vitejs/plugin-vue': specifier: 5.2.4 - version: 5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3)) + version: 5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3)) '@vue/compiler-sfc': - specifier: 3.5.16 - version: 3.5.16 + specifier: 3.5.17 + version: 3.5.17 astring: specifier: 1.9.0 version: 1.9.0 @@ -1128,14 +1128,14 @@ importers: specifier: 2.3.1 version: 2.3.1 rollup: - specifier: 4.42.0 - version: 4.42.0 + specifier: 4.45.1 + version: 4.45.1 sass: specifier: 1.89.2 version: 1.89.2 shiki: - specifier: 3.6.0 - version: 3.6.0 + specifier: 3.8.0 + version: 3.8.0 tinycolor2: specifier: 1.6.0 version: 1.6.0 @@ -1153,20 +1153,20 @@ importers: version: 11.1.0 vite: specifier: 6.3.5 - version: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + version: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vue: - specifier: 3.5.16 - version: 3.5.16(typescript@5.8.3) + specifier: 3.5.17 + version: 3.5.17(typescript@5.8.3) devDependencies: '@misskey-dev/summaly': - specifier: 5.2.1 - version: 5.2.1 + specifier: 5.2.2 + version: 5.2.2 '@tabler/icons-webfont': specifier: 3.34.0 version: 3.34.0 '@testing-library/vue': specifier: 8.1.0 - version: 8.1.0(@vue/compiler-sfc@3.5.16)(@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3)) + version: 8.1.0(@vue/compiler-sfc@3.5.17)(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) '@types/estree': specifier: 1.0.8 version: 1.0.8 @@ -1174,8 +1174,8 @@ importers: specifier: 4.0.9 version: 4.0.9 '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@types/punycode.js': specifier: npm:@types/punycode@2.1.4 version: '@types/punycode@2.1.4' @@ -1186,17 +1186,17 @@ importers: specifier: 8.18.1 version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) '@vitest/coverage-v8': - specifier: 3.2.3 - version: 3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + specifier: 3.2.4 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) '@vue/runtime-core': - specifier: 3.5.16 - version: 3.5.16 + specifier: 3.5.17 + version: 3.5.17 acorn: specifier: 8.15.0 version: 8.15.0 @@ -1204,11 +1204,11 @@ importers: specifier: 7.0.3 version: 7.0.3 eslint-plugin-import: - specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) + specifier: 2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) eslint-plugin-vue: - specifier: 10.2.0 - version: 10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)) + specifier: 10.3.0 + version: 10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0)) fast-glob: specifier: 3.3.3 version: 3.3.3 @@ -1222,14 +1222,14 @@ importers: specifier: 4.0.8 version: 4.0.8 msw: - specifier: 2.10.2 - version: 2.10.2(@types/node@22.15.31)(typescript@5.8.3) + specifier: 2.10.4 + version: 2.10.4(@types/node@22.16.4)(typescript@5.8.3) nodemon: specifier: 3.1.10 version: 3.1.10 prettier: - specifier: 3.5.3 - version: 3.5.3 + specifier: 3.6.2 + version: 3.6.2 start-server-and-test: specifier: 2.0.12 version: 2.0.12 @@ -1237,14 +1237,14 @@ importers: specifier: 1.0.3 version: 1.0.3 vue-component-type-helpers: - specifier: 2.2.10 - version: 2.2.10 + specifier: 2.2.12 + version: 2.2.12 vue-eslint-parser: - specifier: 10.1.3 - version: 10.1.3(eslint@9.31.0) + specifier: 10.2.0 + version: 10.2.0(eslint@9.31.0) vue-tsc: - specifier: 2.2.10 - version: 2.2.10(typescript@5.8.3) + specifier: 2.2.12 + version: 2.2.12(typescript@5.8.3) packages/frontend-shared: dependencies: @@ -1252,24 +1252,24 @@ importers: specifier: workspace:* version: link:../misskey-js vue: - specifier: 3.5.16 - version: 3.5.16(typescript@5.8.3) + specifier: 3.5.17 + version: 3.5.17(typescript@5.8.3) devDependencies: '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) esbuild: - specifier: 0.25.5 - version: 0.25.5 + specifier: 0.25.6 + version: 0.25.6 eslint-plugin-vue: - specifier: 10.2.0 - version: 10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)) + specifier: 10.3.0 + version: 10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0)) nodemon: specifier: 3.1.10 version: 3.1.10 @@ -1277,8 +1277,8 @@ importers: specifier: 5.8.3 version: 5.8.3 vue-eslint-parser: - specifier: 10.1.3 - version: 10.1.3(eslint@9.31.0) + specifier: 10.2.0 + version: 10.2.0(eslint@9.31.0) packages/icons-subsetter: dependencies: @@ -1292,8 +1292,8 @@ importers: specifier: 0.2.9 version: 0.2.9 tsx: - specifier: 4.19.4 - version: 4.19.4 + specifier: 4.20.3 + version: 4.20.3 typescript: specifier: 5.8.3 version: 5.8.3 @@ -1302,17 +1302,17 @@ importers: version: 2.0.1 devDependencies: '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@types/wawoff2': specifier: 1.0.2 version: 1.0.2 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) packages/misskey-bubble-game: dependencies: @@ -1330,26 +1330,26 @@ importers: specifier: 0.19.8 version: 0.19.8 '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@types/seedrandom': specifier: 3.0.8 version: 3.0.8 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) esbuild: - specifier: 0.25.5 - version: 0.25.5 + specifier: 0.25.6 + version: 0.25.6 execa: specifier: 9.6.0 version: 9.6.0 glob: - specifier: 11.0.2 - version: 11.0.2 + specifier: 11.0.3 + version: 11.0.3 nodemon: specifier: 3.1.10 version: 3.1.10 @@ -1458,23 +1458,23 @@ importers: version: 1.2.2 devDependencies: '@types/node': - specifier: 22.15.31 - version: 22.15.31 + specifier: 22.16.4 + version: 22.16.4 '@typescript-eslint/eslint-plugin': - specifier: 8.34.0 - version: 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) esbuild: - specifier: 0.25.5 - version: 0.25.5 + specifier: 0.25.6 + version: 0.25.6 execa: specifier: 9.6.0 version: 9.6.0 glob: - specifier: 11.0.2 - version: 11.0.2 + specifier: 11.0.3 + version: 11.0.3 nodemon: specifier: 3.1.10 version: 3.1.10 @@ -1485,8 +1485,8 @@ importers: packages/sw: dependencies: esbuild: - specifier: 0.25.5 - version: 0.25.5 + specifier: 0.25.6 + version: 0.25.6 idb-keyval: specifier: 6.2.2 version: 6.2.2 @@ -1495,14 +1495,14 @@ importers: version: link:../misskey-js devDependencies: '@typescript-eslint/parser': - specifier: 8.34.0 - version: 8.34.0(eslint@9.31.0)(typescript@5.8.3) + specifier: 8.37.0 + version: 8.37.0(eslint@9.31.0)(typescript@5.8.3) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.74 version: '@types/serviceworker@0.0.74' eslint-plugin-import: - specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) + specifier: 2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) nodemon: specifier: 3.1.10 version: 3.1.10 @@ -1792,13 +1792,13 @@ packages: resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.25.6': - resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} + '@babel/parser@7.27.2': + resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true @@ -1895,6 +1895,10 @@ packages: resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.1': + resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -2004,12 +2008,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.5': - resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.6': resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} engines: {node: '>=18'} @@ -2022,12 +2020,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.5': - resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.6': resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} engines: {node: '>=18'} @@ -2040,12 +2032,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.5': - resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.6': resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} engines: {node: '>=18'} @@ -2058,12 +2044,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.5': - resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.6': resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} engines: {node: '>=18'} @@ -2076,12 +2056,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.5': - resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.6': resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} engines: {node: '>=18'} @@ -2094,12 +2068,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.5': - resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.6': resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} engines: {node: '>=18'} @@ -2112,12 +2080,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.5': - resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.6': resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} engines: {node: '>=18'} @@ -2130,12 +2092,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.5': - resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.6': resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} engines: {node: '>=18'} @@ -2148,12 +2104,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.5': - resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.6': resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} engines: {node: '>=18'} @@ -2166,12 +2116,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.5': - resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.6': resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} engines: {node: '>=18'} @@ -2184,12 +2128,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.5': - resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.6': resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} engines: {node: '>=18'} @@ -2202,12 +2140,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.5': - resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.6': resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} engines: {node: '>=18'} @@ -2220,12 +2152,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.5': - resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.6': resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} engines: {node: '>=18'} @@ -2238,12 +2164,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.5': - resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.6': resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} engines: {node: '>=18'} @@ -2256,12 +2176,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.5': - resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.6': resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} engines: {node: '>=18'} @@ -2274,12 +2188,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.5': - resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.6': resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} engines: {node: '>=18'} @@ -2292,12 +2200,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.5': - resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.6': resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} engines: {node: '>=18'} @@ -2310,12 +2212,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.25.5': - resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.25.6': resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} engines: {node: '>=18'} @@ -2328,12 +2224,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.5': - resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.6': resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} engines: {node: '>=18'} @@ -2346,12 +2236,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.5': - resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.25.6': resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} engines: {node: '>=18'} @@ -2364,12 +2248,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.5': - resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.6': resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} engines: {node: '>=18'} @@ -2388,12 +2266,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.5': - resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.6': resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} engines: {node: '>=18'} @@ -2406,12 +2278,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.5': - resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.6': resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} engines: {node: '>=18'} @@ -2424,12 +2290,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.5': - resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.6': resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} engines: {node: '>=18'} @@ -2442,12 +2302,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.5': - resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.6': resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} engines: {node: '>=18'} @@ -3060,6 +2914,9 @@ packages: '@misskey-dev/summaly@5.2.1': resolution: {integrity: sha512-fcFd7ssHAghRntewRROOpRxv+VH18uz85Kzg6pZK1EFyqPOXxf39ErRA9HnJSzPYQT6KJTqBWuKHbCGoFlceXg==} + '@misskey-dev/summaly@5.2.2': + resolution: {integrity: sha512-GIKnosbTJ/me2LgqQbxk35tsbzSJUMRwlTeJGxPylGcOFlDYdHeAuiF/qFfCzlzpVzO+oisKvazZkTMY9cGShA==} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2': resolution: {integrity: sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==} cpu: [arm64] @@ -3628,8 +3485,8 @@ packages: rollup: optional: true - '@rollup/pluginutils@5.1.4': - resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -3637,103 +3494,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.42.0': - resolution: {integrity: sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==} + '@rollup/rollup-android-arm-eabi@4.45.1': + resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.42.0': - resolution: {integrity: sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==} + '@rollup/rollup-android-arm64@4.45.1': + resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.42.0': - resolution: {integrity: sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==} + '@rollup/rollup-darwin-arm64@4.45.1': + resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.42.0': - resolution: {integrity: sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==} + '@rollup/rollup-darwin-x64@4.45.1': + resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.42.0': - resolution: {integrity: sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==} + '@rollup/rollup-freebsd-arm64@4.45.1': + resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.42.0': - resolution: {integrity: sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==} + '@rollup/rollup-freebsd-x64@4.45.1': + resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.42.0': - resolution: {integrity: sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==} + '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.42.0': - resolution: {integrity: sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==} + '@rollup/rollup-linux-arm-musleabihf@4.45.1': + resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.42.0': - resolution: {integrity: sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==} + '@rollup/rollup-linux-arm64-gnu@4.45.1': + resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.42.0': - resolution: {integrity: sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==} + '@rollup/rollup-linux-arm64-musl@4.45.1': + resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.42.0': - resolution: {integrity: sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==} + '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.42.0': - resolution: {integrity: sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.42.0': - resolution: {integrity: sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==} + '@rollup/rollup-linux-riscv64-gnu@4.45.1': + resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.42.0': - resolution: {integrity: sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==} + '@rollup/rollup-linux-riscv64-musl@4.45.1': + resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.42.0': - resolution: {integrity: sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==} + '@rollup/rollup-linux-s390x-gnu@4.45.1': + resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.42.0': - resolution: {integrity: sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==} + '@rollup/rollup-linux-x64-gnu@4.45.1': + resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.42.0': - resolution: {integrity: sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==} + '@rollup/rollup-linux-x64-musl@4.45.1': + resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.42.0': - resolution: {integrity: sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==} + '@rollup/rollup-win32-arm64-msvc@4.45.1': + resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.42.0': - resolution: {integrity: sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==} + '@rollup/rollup-win32-ia32-msvc@4.45.1': + resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.42.0': - resolution: {integrity: sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==} + '@rollup/rollup-win32-x64-msvc@4.45.1': + resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} cpu: [x64] os: [win32] @@ -3765,58 +3622,58 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@sentry-internal/browser-utils@9.27.0': - resolution: {integrity: sha512-SJa7f6Ct1BzP8rWEomnshSGN1CmT+axNKvT+StrbFPD6AyHnYfFLJpKgc2iToIJHB/pmeuOI9dUwqtzVx+5nSw==} - engines: {node: '>=18'} - '@sentry-internal/browser-utils@9.28.0': resolution: {integrity: sha512-SqntPnIXudP3FoKj4mQ1BVPC1RNzo4CGtAxJnLpbIUpdT/khJVM6Q59zrGl2MgZ7URZCI986L5jXihQeferf6g==} engines: {node: '>=18'} - '@sentry-internal/feedback@9.27.0': - resolution: {integrity: sha512-e7L8eG0y63RulN352lmafoCCfQGg4jLVT8YLx6096eWu/YKLkgmVpgi8livsT5WREnH+HB+iFSrejOwK7cRkhw==} + '@sentry-internal/browser-utils@9.39.0': + resolution: {integrity: sha512-f1qUAftQ/aMTJHJDCG9jDJIwLEkSVZ6xWGn2xQMj7PfD1uuYlTy3KHn+wPy1VlPJTZQ44WclTPnY2U/OBIPmsg==} engines: {node: '>=18'} '@sentry-internal/feedback@9.28.0': resolution: {integrity: sha512-z2jShmVENsesmDnShEOv841Saw0zXe1tX6GHNgkK9f6NrUMbL970JvGKByBFTffhQH6uQ0WeNPnXJ5L/YKnfDg==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@9.27.0': - resolution: {integrity: sha512-44rVSt3LCH6qePYRQrl4WUBwnkOk9dzinmnKmuwRksEdDOkVq5KBRhi/IDr7omwSpX8C+KrX5alfKhOx1cP0gQ==} + '@sentry-internal/feedback@9.39.0': + resolution: {integrity: sha512-IvD2LDKFderPn6pXN6N8cFJWWIXUADys9WRtfh1CK8DIuU1fXYi61tO23Z+sCsvXReQgr/mdJg5dHa0qY/vDww==} engines: {node: '>=18'} '@sentry-internal/replay-canvas@9.28.0': resolution: {integrity: sha512-Bv4mbtUrRV3p6PpFQPseLv3+Uaen+3AlfX02Z6QHY1sMa4lpt+U8OHfRGLprnzb6Rarw6fK2LNVL5rnV9LNMwA==} engines: {node: '>=18'} - '@sentry-internal/replay@9.27.0': - resolution: {integrity: sha512-n2kO1wOfCG7GxkMAqbYYkpgTqJM5tuVLdp0JuNCqTOLTXWvw6svWGaYKlYpKUgsK9X/GDzJYSXZmfe+Dbg+FJQ==} + '@sentry-internal/replay-canvas@9.39.0': + resolution: {integrity: sha512-rTw0WZuAwzo36/hXRSuftu7e3qtwXc+bSs1q7M+w7Tb98aoOyXs3GlnYmgP6q3t2v/h7zoIiQcfjnPqo6+DVtQ==} engines: {node: '>=18'} '@sentry-internal/replay@9.28.0': resolution: {integrity: sha512-BVGVBlmcpJdT55d/vywjfK1u6zMC5ycjJBxU1wUCNgCU3cSKRDBnvmYgk/+Ay23bFryT28Q4hM1p5qBBAOfxjQ==} engines: {node: '>=18'} - '@sentry/browser@9.27.0': - resolution: {integrity: sha512-geR3lhRJOmUQqi1WgovLSYcD/f66zYnctdnDEa7j1BW2XIB1nlTJn0mpYyAHghXKkUN/pBpp1Z+Jk0XlVwFYVg==} + '@sentry-internal/replay@9.39.0': + resolution: {integrity: sha512-6mweqQtfcVG2+FEPjmgjrpCThgs9bR2cmQM2Ew/SlINj8ETq5P/7Z3rdIp+Zs7LArXFIJUA7KX9ZW+noVPI8Gg==} engines: {node: '>=18'} '@sentry/browser@9.28.0': resolution: {integrity: sha512-ttqiv3D9sIB43nZnJTTln1nXw1p4C5BDSh+sHmGUOiqdCH6ND3HByDITYMYIOz1lACSISTT4V+MEpqx0V25Tlw==} engines: {node: '>=18'} + '@sentry/browser@9.39.0': + resolution: {integrity: sha512-RZ4Zp22ohFLwkR3y6pmSWDrPVP7ErX0ClNOh2Kal3TNWlOGb4WPBPaWj2tiOvc4n/l6EDYaFvQ0Hh8RhesB98A==} + engines: {node: '>=18'} + '@sentry/core@8.55.0': resolution: {integrity: sha512-6g7jpbefjHYs821Z+EBJ8r4Z7LT5h80YSWRJaylGS4nW5W5Z2KXzpdnyFarv37O7QjauzVC2E+PABmpkw5/JGA==} engines: {node: '>=14.18'} - '@sentry/core@9.27.0': - resolution: {integrity: sha512-Zb2SSAdWXQjTem+sVWrrAq9L6YYfxyoTwtapaE6C6qZBR5C8Uak0wcYww8StaCFH7dDA/PSW+VxOwjNXocrQHQ==} - engines: {node: '>=18'} - '@sentry/core@9.28.0': resolution: {integrity: sha512-vzD9xhg9S864jxfCpq77feCE4y7iP2cZYsNMoTupl1vTUlmXlhp7XgF832fEMjEZq4vrPhaqCNsde7Sc3PAbaQ==} engines: {node: '>=18'} + '@sentry/core@9.39.0': + resolution: {integrity: sha512-9Z32C64rUGEI0ROxXXOtpJDaldPYtkzOQyGzToVgq5LefXkdgQInd8BhCt6PIBALfj7n25lLOYllTrHlboFJug==} + engines: {node: '>=18'} + '@sentry/node@8.55.0': resolution: {integrity: sha512-h10LJLDTRAzYgay60Oy7moMookqqSZSviCWkkmHZyaDn+4WURnPp5SKhhfrzPRQcXKrweiOwDSHBgn1tweDssg==} engines: {node: '>=14.18'} @@ -3837,16 +3694,6 @@ packages: engines: {node: '>=14.18'} hasBin: true - '@sentry/vue@9.27.0': - resolution: {integrity: sha512-LeoxSCDtynAA89tcUD3r1JblCwFjzByID9O0y1JkMU1UlVL08vbV9fNSfEhJ66WdvBo5KPTRn5ScHLUOoae/WA==} - engines: {node: '>=18'} - peerDependencies: - pinia: 2.x || 3.x - vue: 2.x || 3.x - peerDependenciesMeta: - pinia: - optional: true - '@sentry/vue@9.28.0': resolution: {integrity: sha512-k3TG9yuta5pbmX5If5BhaL+GCs1hKrepYf0XdDW+XSMB5gXJ7BzeCNrAGbGWfRlr+pioiRz4V9nxTTBY1yX5mQ==} engines: {node: '>=18'} @@ -3857,23 +3704,33 @@ packages: pinia: optional: true - '@shikijs/core@3.6.0': - resolution: {integrity: sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw==} + '@sentry/vue@9.39.0': + resolution: {integrity: sha512-BRCdfj4qsaJyoqqEPUB3DIcJOTTzGJd73dBGGwc3N2HAwWI5TyTLP3lHhbhE43ZowBO0aE/ogVT1N6dzQYt7Yg==} + engines: {node: '>=18'} + peerDependencies: + pinia: 2.x || 3.x + vue: 2.x || 3.x + peerDependenciesMeta: + pinia: + optional: true - '@shikijs/engine-javascript@3.6.0': - resolution: {integrity: sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA==} + '@shikijs/core@3.8.0': + resolution: {integrity: sha512-gWt8NNZFurL6FMESO4lEsmspDh0H1fyUibhx1NnEH/S3kOXgYiWa6ZFqy+dcjBLhZqCXsepuUaL1QFXk6PrpsQ==} - '@shikijs/engine-oniguruma@3.6.0': - resolution: {integrity: sha512-nmOhIZ9yT3Grd+2plmW/d8+vZ2pcQmo/UnVwXMUXAKTXdi+LK0S08Ancrz5tQQPkxvjBalpMW2aKvwXfelauvA==} + '@shikijs/engine-javascript@3.8.0': + resolution: {integrity: sha512-IBULFFpQ1N5Cg/C7jPCGnjIKz72CcRtD0BIbNhSuXPUOxLG0bF1URsP/uLfxQFQ9ORfunCQwL7UuSX1RSRBwUQ==} - '@shikijs/langs@3.6.0': - resolution: {integrity: sha512-IdZkQJaLBu1LCYCwkr30hNuSDfllOT8RWYVZK1tD2J03DkiagYKRxj/pDSl8Didml3xxuyzUjgtioInwEQM/TA==} + '@shikijs/engine-oniguruma@3.8.0': + resolution: {integrity: sha512-Tx7kR0oFzqa+rY7t80LjN8ZVtHO3a4+33EUnBVx2qYP3fGxoI9H0bvnln5ySelz9SIUTsS0/Qn+9dg5zcUMsUw==} - '@shikijs/themes@3.6.0': - resolution: {integrity: sha512-Fq2j4nWr1DF4drvmhqKq8x5vVQ27VncF8XZMBuHuQMZvUSS3NBgpqfwz/FoGe36+W6PvniZ1yDlg2d4kmYDU6w==} + '@shikijs/langs@3.8.0': + resolution: {integrity: sha512-mfGYuUgjQ5GgXinB5spjGlBVhG2crKRpKkfADlp8r9k/XvZhtNXxyOToSnCEnF0QNiZnJjlt5MmU9PmhRdwAbg==} - '@shikijs/types@3.6.0': - resolution: {integrity: sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg==} + '@shikijs/themes@3.8.0': + resolution: {integrity: sha512-yaZiLuyO23sXe16JFU76KyUMTZCJi4EMQKIrdQt7okoTzI4yAaJhVXT2Uy4k8yBIEFRiia5dtD7gC1t8m6y3oQ==} + + '@shikijs/types@3.8.0': + resolution: {integrity: sha512-I/b/aNg0rP+kznVDo7s3UK8jMcqEGTtoPDdQ+JlQ2bcJIyu/e2iRvl42GLIDMK03/W1YOHOuhlhQ7aM+XbKUeg==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -4679,9 +4536,6 @@ packages: '@types/eslint@7.29.0': resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==} - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -5083,11 +4937,11 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/coverage-v8@3.2.3': - resolution: {integrity: sha512-D1QKzngg8PcDoCE8FHSZhREDuEy+zcKmMiMafYse41RZpBE5EDJyKOTdqK3RQfsV2S2nyKor5KCs8PyPRFqKPg==} + '@vitest/coverage-v8@3.2.4': + resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} peerDependencies: - '@vitest/browser': 3.2.3 - vitest: 3.2.3 + '@vitest/browser': 3.2.4 + vitest: 3.2.4 peerDependenciesMeta: '@vitest/browser': optional: true @@ -5095,11 +4949,11 @@ packages: '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} - '@vitest/expect@3.2.3': - resolution: {integrity: sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/mocker@3.2.3': - resolution: {integrity: sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==} + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 @@ -5115,20 +4969,20 @@ packages: '@vitest/pretty-format@2.1.1': resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} - '@vitest/pretty-format@3.2.3': - resolution: {integrity: sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/runner@3.2.3': - resolution: {integrity: sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==} + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@vitest/snapshot@3.2.3': - resolution: {integrity: sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==} + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} '@vitest/spy@2.0.5': resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} - '@vitest/spy@3.2.3': - resolution: {integrity: sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} '@vitest/utils@2.0.5': resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} @@ -5136,50 +4990,50 @@ packages: '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} - '@vitest/utils@3.2.3': - resolution: {integrity: sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} '@volar/language-core@2.2.0': resolution: {integrity: sha512-a8WG9+4OdeNDW4ywABZIM6S6UN7em8uIlM/BZ2pWQUYrVmX+m8sj/X+QadvO+Li/t/LjAqbWJQtVgxdpEWLALQ==} - '@volar/language-core@2.4.11': - resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} + '@volar/language-core@2.4.15': + resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} '@volar/source-map@2.2.0': resolution: {integrity: sha512-HQlPRlHOVqCCHK8wI76ZldHkEwKsjp7E6idUc36Ekni+KJDNrqgSqPvyHQixybXPHNU7CI9Uxd9/IkxO7LuNBw==} - '@volar/source-map@2.4.11': - resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==} + '@volar/source-map@2.4.15': + resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} '@volar/typescript@2.2.0': resolution: {integrity: sha512-wC6l4zLiiCLxF+FGaHCbWlQYf4vMsnRxYhcI6WgvaNppOD6r1g+Ef1RKRJUApALWU46Yy/JDU/TbdV6w/X6Liw==} - '@volar/typescript@2.4.11': - resolution: {integrity: sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==} + '@volar/typescript@2.4.15': + resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} '@vue/compiler-core@3.5.13': resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} - '@vue/compiler-core@3.5.14': - resolution: {integrity: sha512-k7qMHMbKvoCXIxPhquKQVw3Twid3Kg4s7+oYURxLGRd56LiuHJVrvFKI4fm2AM3c8apqODPfVJGoh8nePbXMRA==} - '@vue/compiler-core@3.5.16': resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} + '@vue/compiler-core@3.5.17': + resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==} + '@vue/compiler-dom@3.5.13': resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} - '@vue/compiler-dom@3.5.14': - resolution: {integrity: sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==} - '@vue/compiler-dom@3.5.16': resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} - '@vue/compiler-sfc@3.5.16': - resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==} + '@vue/compiler-dom@3.5.17': + resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==} - '@vue/compiler-ssr@3.5.16': - resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==} + '@vue/compiler-sfc@3.5.17': + resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==} + + '@vue/compiler-ssr@3.5.17': + resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==} '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -5192,37 +5046,37 @@ packages: typescript: optional: true - '@vue/language-core@2.2.10': - resolution: {integrity: sha512-+yNoYx6XIKuAO8Mqh1vGytu8jkFEOH5C8iOv3i8Z/65A7x9iAOXA97Q+PqZ3nlm2lxf5rOJuIGI/wDtx/riNYw==} + '@vue/language-core@2.2.12': + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - '@vue/reactivity@3.5.16': - resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==} + '@vue/reactivity@3.5.17': + resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==} - '@vue/runtime-core@3.5.16': - resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==} + '@vue/runtime-core@3.5.17': + resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==} - '@vue/runtime-dom@3.5.16': - resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==} + '@vue/runtime-dom@3.5.17': + resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==} - '@vue/server-renderer@3.5.16': - resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==} + '@vue/server-renderer@3.5.17': + resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==} peerDependencies: - vue: 3.5.16 + vue: 3.5.17 '@vue/shared@3.5.13': resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} - '@vue/shared@3.5.14': - resolution: {integrity: sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==} - '@vue/shared@3.5.16': resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} + '@vue/shared@3.5.17': + resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==} + '@vue/test-utils@2.4.1': resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==} peerDependencies: @@ -5472,6 +5326,10 @@ packages: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} @@ -5479,6 +5337,10 @@ packages: resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -5487,18 +5349,34 @@ packages: resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.2: resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.3: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -5542,6 +5420,10 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + async-mutex@0.5.0: resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} @@ -5769,6 +5651,10 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -5864,8 +5750,8 @@ packages: character-parser@2.2.0: resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} - chart.js@4.4.9: - resolution: {integrity: sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==} + chart.js@4.5.0: + resolution: {integrity: sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==} engines: {pnpm: '>=8'} chartjs-adapter-date-fns@3.0.0: @@ -5904,6 +5790,10 @@ packages: resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} engines: {node: '>=18.17'} + cheerio@1.1.0: + resolution: {integrity: sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==} + engines: {node: '>=18.17'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -6240,11 +6130,6 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - cypress@14.4.1: - resolution: {integrity: sha512-YSGvVXtTqSGRTyHbaxHI5dHU/9xc5ymaTIM4BU85GKhj980y6XgA3fShSpj5DatS8knXMsAvYItQxVQFHGpUtw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - cypress@14.5.2: resolution: {integrity: sha512-O4E4CEBqDHLDrJD/dfStHPcM+8qFgVVZ89Li7xDU0yL/JxO/V0PEcfF2I8aGa7uA2MGNLkNUAnghPM83UcHOJw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -6266,14 +6151,26 @@ packages: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} engines: {node: '>= 0.4'} + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} @@ -6497,6 +6394,9 @@ packages: domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dotenv@16.4.7: resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} engines: {node: '>=12'} @@ -6587,6 +6487,10 @@ packages: resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} engines: {node: '>= 0.4'} + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -6612,10 +6516,18 @@ packages: es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + es-toolkit@1.27.0: resolution: {integrity: sha512-ETSFA+ZJArcuSCpzD2TjAy6UHpx4E4uqFsoDg9F/nTLogrLmVVZQ+zNxco5h7cWnA1nNak07IXsLcaSMih+ZPQ==} @@ -6635,11 +6547,6 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.25.5: - resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.25.6: resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} engines: {node: '>=18'} @@ -6703,6 +6610,27 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + eslint-plugin-import@2.31.0: resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} engines: {node: '>=4'} @@ -6713,20 +6641,30 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-vue@10.2.0: - resolution: {integrity: sha512-tl9s+KN3z0hN2b8fV2xSs5ytGl7Esk1oSCxULLwFcdaElhZ8btYYZFrWxvh4En+czrSDtuLCeCOGa8HhEZuBdQ==} + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-vue@10.3.0: + resolution: {integrity: sha512-A0u9snqjCfYaPnqqOaH6MBLVWDUIN4trXn8J3x67uDcXvR7X6Ut8p16N+nYhMCQ9Y7edg2BIRGzfyZsY0IdqoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 vue-eslint-parser: ^10.0.0 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true eslint-rule-docs@1.1.235: resolution: {integrity: sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==} - eslint-scope@8.3.0: - resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6753,10 +6691,6 @@ packages: jiti: optional: true - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -7052,6 +6986,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -7135,6 +7073,10 @@ packages: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} @@ -7183,6 +7125,10 @@ packages: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + get-tsconfig@4.10.0: resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} @@ -7249,6 +7195,10 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + globalyzer@0.1.0: resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} @@ -7321,6 +7271,10 @@ packages: resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -7381,6 +7335,9 @@ packages: html-entities@2.5.2: resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -7391,6 +7348,9 @@ packages: resolution: {integrity: sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg==} engines: {node: '>=0.10'} + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + htmlparser2@5.0.1: resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} @@ -7551,6 +7511,10 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + intersection-observer@0.12.2: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} @@ -7590,19 +7554,35 @@ packages: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -7611,14 +7591,26 @@ packages: resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -7634,6 +7626,10 @@ packages: is-file-animated@1.0.2: resolution: {integrity: sha512-TAYDUkvyBmxqneRU26zzpeHLAgtzEOIsRQWrtDidPT/tFK3Yc0WKgtF3u4oOEAiN0kAuVfl7MTgbD0vXdFDztA==} + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -7657,6 +7653,10 @@ packages: is-map@2.0.2: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + is-negative-zero@2.0.3: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} @@ -7668,6 +7668,10 @@ packages: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -7698,13 +7702,25 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + is-set@2.0.2: resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -7721,6 +7737,10 @@ packages: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + is-svg@5.1.0: resolution: {integrity: sha512-uVg5yifaTxHoefNf5Jcx+i9RZe2OBYd/UStp1umx+EERa4xGRa3LLGXjoEph43qUORC0qkafUgrXZ6zzK89yGA==} engines: {node: '>=14.16'} @@ -7729,10 +7749,18 @@ packages: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + is-typed-array@1.1.13: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -7747,12 +7775,24 @@ packages: is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + is-weakset@2.0.2: resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -8210,6 +8250,9 @@ packages: loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} + lowercase-keys@3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -8506,10 +8549,6 @@ 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@10.0.3: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} @@ -8520,10 +8559,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.2: - resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==} - engines: {node: '>=10'} - minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -8632,8 +8667,8 @@ packages: peerDependencies: msw: ^2.0.0 - msw@2.10.2: - resolution: {integrity: sha512-RCKM6IZseZQCWcSWlutdf590M8nVfRHG1ImwzOtwz8IYxgT4zhUO0rfTcTvDGiaFE0Rhcc+h43lcF3Jc9gFtwQ==} + msw@2.10.4: + resolution: {integrity: sha512-6R1or/qyele7q3RyPwNuvc0IxO8L8/Aim6Sz5ncXEgcWUNxSKE+udriTOWHtpMwmfkLYlacA2y7TIx4cL5lgHA==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -8883,6 +8918,10 @@ packages: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + object.fromentries@2.0.8: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} @@ -8895,6 +8934,10 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + obliterator@2.0.4: resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} @@ -8955,6 +8998,10 @@ packages: outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -9031,6 +9078,9 @@ packages: parse5-htmlparser2-tree-adapter@7.0.0: resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + parse5-parser-stream@7.1.2: resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} @@ -9431,8 +9481,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -9741,6 +9791,10 @@ packages: reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + regenerator-runtime@0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} @@ -9760,6 +9814,10 @@ packages: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + remark-gfm@4.0.0: resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} @@ -9856,8 +9914,8 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rollup@4.42.0: - resolution: {integrity: sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==} + rollup@4.45.1: + resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -9877,16 +9935,28 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + safe-regex2@4.0.0: resolution: {integrity: sha512-Hvjfv25jPDVr3U+4LDzBuZPPOymELG3PYcSk5hcevooo1yxxamQL/bHs/GrEPGmMoMEwRrHVGiCA1pXi97B8Ew==} @@ -9982,6 +10052,10 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -10008,8 +10082,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@3.6.0: - resolution: {integrity: sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w==} + shiki@3.8.0: + resolution: {integrity: sha512-yPqK0y68t20aakv+3aMTpUMJZd6UHaBY2/SBUDowh9M70gVUwqT0bf7Kz5CWG0AXfHtFvXCHhBBHVAzdp0ILoQ==} shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -10269,6 +10343,10 @@ packages: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640: resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640} version: 0.0.0 @@ -10335,6 +10413,10 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} engines: {node: '>= 0.4'} @@ -10342,6 +10424,10 @@ packages: string.prototype.trimend@1.0.8: resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} @@ -10521,8 +10607,8 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - three@0.177.0: - resolution: {integrity: sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg==} + three@0.178.0: + resolution: {integrity: sha512-ybFIB0+x8mz0wnZgSGy2MO/WCO6xZhQSZnmfytSPyNpM0sBafGRVhdaj+erYh5U+RhQOAg/eXqw5uVDiM2BjhQ==} throttle-debounce@5.0.2: resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} @@ -10557,8 +10643,8 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} - tinypool@1.1.0: - resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@1.2.0: @@ -10686,11 +10772,6 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.19.4: - resolution: {integrity: sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==} - engines: {node: '>=18.0.0'} - hasBin: true - tsx@4.20.3: resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} @@ -10746,18 +10827,34 @@ packages: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + typed-array-length@1.0.6: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} @@ -10848,6 +10945,10 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} @@ -10865,6 +10966,10 @@ packages: resolution: {integrity: sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==} engines: {node: '>=18.17'} + undici@7.11.0: + resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==} + engines: {node: '>=20.18.1'} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -10996,8 +11101,8 @@ packages: vfile@6.0.1: resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==} - vite-node@3.2.3: - resolution: {integrity: sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -11050,16 +11155,16 @@ packages: peerDependencies: vitest: '>=2.0.0' - vitest@3.2.3: - resolution: {integrity: sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==} + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.3 - '@vitest/ui': 3.2.3 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -11113,8 +11218,8 @@ packages: vue-component-type-helpers@2.0.16: resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==} - vue-component-type-helpers@2.2.10: - resolution: {integrity: sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==} + vue-component-type-helpers@2.2.12: + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} vue-component-type-helpers@3.0.1: resolution: {integrity: sha512-j23mCB5iEbGsyIhnVdXdWUOg+UdwmVxpKnYYf2j+4ppCt5VSFXKjwu9YFt0QYxUaf5G99PuHsVfRScjHCRSsGQ==} @@ -11135,8 +11240,8 @@ packages: peerDependencies: vue: '>=2' - vue-eslint-parser@10.1.3: - resolution: {integrity: sha512-dbCBnd2e02dYWsXoqX5yKUZlOt+ExIpq7hmHKPb5ZqKcjf++Eo0hMseFTZMLKThrUk61m+Uv6A2YSBve6ZvuDQ==} + vue-eslint-parser@10.2.0: + resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -11149,14 +11254,14 @@ packages: vue-template-compiler@2.7.14: resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==} - vue-tsc@2.2.10: - resolution: {integrity: sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==} + vue-tsc@2.2.12: + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} hasBin: true peerDependencies: typescript: '>=5.0.0' - vue@3.5.16: - resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==} + vue@3.5.17: + resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -11241,9 +11346,21 @@ packages: which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + which-collection@1.0.1: resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + which-module@2.0.0: resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} @@ -11251,6 +11368,10 @@ packages: resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -11962,10 +12083,10 @@ snapshots: '@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.25.6 + '@babel/parser': 7.27.2 '@babel/template': 7.24.7 '@babel/traverse': 7.24.7 - '@babel/types': 7.25.6 + '@babel/types': 7.27.1 convert-source-map: 2.0.0 debug: 4.4.1(supports-color@10.0.0) gensync: 1.0.0-beta.2 @@ -11976,7 +12097,7 @@ snapshots: '@babel/generator@7.24.7': dependencies: - '@babel/types': 7.25.6 + '@babel/types': 7.27.1 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -12016,7 +12137,7 @@ snapshots: '@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 + '@babel/helper-validator-identifier': 7.27.1 transitivePeerDependencies: - supports-color @@ -12055,14 +12176,14 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/parser@7.25.6': - dependencies: - '@babel/types': 7.25.6 - '@babel/parser@7.27.2': dependencies: '@babel/types': 7.27.1 + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.1 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 @@ -12151,8 +12272,8 @@ snapshots: '@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.25.6 - '@babel/types': 7.25.6 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 debug: 4.4.1(supports-color@10.0.0) globals: 11.12.0 transitivePeerDependencies: @@ -12169,6 +12290,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.1': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@bcoe/v8-coverage@0.2.3': {} '@bcoe/v8-coverage@1.0.2': {} @@ -12332,189 +12458,126 @@ snapshots: '@esbuild/aix-ppc64@0.25.4': optional: true - '@esbuild/aix-ppc64@0.25.5': - optional: true - '@esbuild/aix-ppc64@0.25.6': optional: true '@esbuild/android-arm64@0.25.4': optional: true - '@esbuild/android-arm64@0.25.5': - optional: true - '@esbuild/android-arm64@0.25.6': optional: true '@esbuild/android-arm@0.25.4': optional: true - '@esbuild/android-arm@0.25.5': - optional: true - '@esbuild/android-arm@0.25.6': optional: true '@esbuild/android-x64@0.25.4': optional: true - '@esbuild/android-x64@0.25.5': - optional: true - '@esbuild/android-x64@0.25.6': optional: true '@esbuild/darwin-arm64@0.25.4': optional: true - '@esbuild/darwin-arm64@0.25.5': - optional: true - '@esbuild/darwin-arm64@0.25.6': optional: true '@esbuild/darwin-x64@0.25.4': optional: true - '@esbuild/darwin-x64@0.25.5': - optional: true - '@esbuild/darwin-x64@0.25.6': optional: true '@esbuild/freebsd-arm64@0.25.4': optional: true - '@esbuild/freebsd-arm64@0.25.5': - optional: true - '@esbuild/freebsd-arm64@0.25.6': optional: true '@esbuild/freebsd-x64@0.25.4': optional: true - '@esbuild/freebsd-x64@0.25.5': - optional: true - '@esbuild/freebsd-x64@0.25.6': optional: true '@esbuild/linux-arm64@0.25.4': optional: true - '@esbuild/linux-arm64@0.25.5': - optional: true - '@esbuild/linux-arm64@0.25.6': optional: true '@esbuild/linux-arm@0.25.4': optional: true - '@esbuild/linux-arm@0.25.5': - optional: true - '@esbuild/linux-arm@0.25.6': optional: true '@esbuild/linux-ia32@0.25.4': optional: true - '@esbuild/linux-ia32@0.25.5': - optional: true - '@esbuild/linux-ia32@0.25.6': optional: true '@esbuild/linux-loong64@0.25.4': optional: true - '@esbuild/linux-loong64@0.25.5': - optional: true - '@esbuild/linux-loong64@0.25.6': optional: true '@esbuild/linux-mips64el@0.25.4': optional: true - '@esbuild/linux-mips64el@0.25.5': - optional: true - '@esbuild/linux-mips64el@0.25.6': optional: true '@esbuild/linux-ppc64@0.25.4': optional: true - '@esbuild/linux-ppc64@0.25.5': - optional: true - '@esbuild/linux-ppc64@0.25.6': optional: true '@esbuild/linux-riscv64@0.25.4': optional: true - '@esbuild/linux-riscv64@0.25.5': - optional: true - '@esbuild/linux-riscv64@0.25.6': optional: true '@esbuild/linux-s390x@0.25.4': optional: true - '@esbuild/linux-s390x@0.25.5': - optional: true - '@esbuild/linux-s390x@0.25.6': optional: true '@esbuild/linux-x64@0.25.4': optional: true - '@esbuild/linux-x64@0.25.5': - optional: true - '@esbuild/linux-x64@0.25.6': optional: true '@esbuild/netbsd-arm64@0.25.4': optional: true - '@esbuild/netbsd-arm64@0.25.5': - optional: true - '@esbuild/netbsd-arm64@0.25.6': optional: true '@esbuild/netbsd-x64@0.25.4': optional: true - '@esbuild/netbsd-x64@0.25.5': - optional: true - '@esbuild/netbsd-x64@0.25.6': optional: true '@esbuild/openbsd-arm64@0.25.4': optional: true - '@esbuild/openbsd-arm64@0.25.5': - optional: true - '@esbuild/openbsd-arm64@0.25.6': optional: true '@esbuild/openbsd-x64@0.25.4': optional: true - '@esbuild/openbsd-x64@0.25.5': - optional: true - '@esbuild/openbsd-x64@0.25.6': optional: true @@ -12524,36 +12587,24 @@ snapshots: '@esbuild/sunos-x64@0.25.4': optional: true - '@esbuild/sunos-x64@0.25.5': - optional: true - '@esbuild/sunos-x64@0.25.6': optional: true '@esbuild/win32-arm64@0.25.4': optional: true - '@esbuild/win32-arm64@0.25.5': - optional: true - '@esbuild/win32-arm64@0.25.6': optional: true '@esbuild/win32-ia32@0.25.4': optional: true - '@esbuild/win32-ia32@0.25.5': - optional: true - '@esbuild/win32-ia32@0.25.6': optional: true '@esbuild/win32-x64@0.25.4': optional: true - '@esbuild/win32-x64@0.25.5': - optional: true - '@esbuild/win32-x64@0.25.6': optional: true @@ -12905,16 +12956,16 @@ snapshots: '@img/sharp-win32-x64@0.34.2': optional: true - '@inquirer/confirm@5.0.2(@types/node@22.15.31)': + '@inquirer/confirm@5.0.2(@types/node@22.16.4)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.15.31) - '@inquirer/type': 3.0.1(@types/node@22.15.31) - '@types/node': 22.15.31 + '@inquirer/core': 10.1.0(@types/node@22.16.4) + '@inquirer/type': 3.0.1(@types/node@22.16.4) + '@types/node': 22.16.4 - '@inquirer/core@10.1.0(@types/node@22.15.31)': + '@inquirer/core@10.1.0(@types/node@22.16.4)': dependencies: '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@22.15.31) + '@inquirer/type': 3.0.1(@types/node@22.16.4) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -12927,9 +12978,9 @@ snapshots: '@inquirer/figures@1.0.8': {} - '@inquirer/type@3.0.1(@types/node@22.15.31)': + '@inquirer/type@3.0.1(@types/node@22.16.4)': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@ioredis/commands@1.2.0': {} @@ -13151,12 +13202,12 @@ snapshots: '@types/yargs': 17.0.33 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: glob: 10.4.5 magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.8.3) - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) optionalDependencies: typescript: 5.8.3 @@ -13257,14 +13308,14 @@ snapshots: '@misskey-dev/browser-image-resizer@2024.1.0': {} - '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0)': + '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.1.1)(@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3))(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0))(eslint@9.31.0)(globals@16.3.0)': dependencies: '@eslint/compat': 1.1.1 '@stylistic/eslint-plugin': 2.13.0(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) eslint: 9.31.0 - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0) globals: 16.3.0 '@misskey-dev/sharp-read-bmp@1.2.0': @@ -13283,6 +13334,16 @@ snapshots: jschardet: 3.1.4 private-ip: 3.0.2 + '@misskey-dev/summaly@5.2.2': + dependencies: + cheerio: 1.1.0 + escape-regexp: 0.0.1 + got: 14.4.7 + html-entities: 2.6.0 + iconv-lite: 0.6.3 + jschardet: 3.1.4 + private-ip: 3.0.2 + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2': optional: true @@ -13903,85 +13964,85 @@ snapshots: transitivePeerDependencies: - supports-color - '@rollup/plugin-json@6.1.0(rollup@4.42.0)': + '@rollup/plugin-json@6.1.0(rollup@4.45.1)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.42.0) + '@rollup/pluginutils': 5.2.0(rollup@4.45.1) optionalDependencies: - rollup: 4.42.0 + rollup: 4.45.1 - '@rollup/plugin-replace@6.0.2(rollup@4.42.0)': + '@rollup/plugin-replace@6.0.2(rollup@4.45.1)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.42.0) + '@rollup/pluginutils': 5.2.0(rollup@4.45.1) magic-string: 0.30.17 optionalDependencies: - rollup: 4.42.0 + rollup: 4.45.1 - '@rollup/pluginutils@5.1.4(rollup@4.42.0)': + '@rollup/pluginutils@5.2.0(rollup@4.45.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.42.0 + rollup: 4.45.1 - '@rollup/rollup-android-arm-eabi@4.42.0': + '@rollup/rollup-android-arm-eabi@4.45.1': optional: true - '@rollup/rollup-android-arm64@4.42.0': + '@rollup/rollup-android-arm64@4.45.1': optional: true - '@rollup/rollup-darwin-arm64@4.42.0': + '@rollup/rollup-darwin-arm64@4.45.1': optional: true - '@rollup/rollup-darwin-x64@4.42.0': + '@rollup/rollup-darwin-x64@4.45.1': optional: true - '@rollup/rollup-freebsd-arm64@4.42.0': + '@rollup/rollup-freebsd-arm64@4.45.1': optional: true - '@rollup/rollup-freebsd-x64@4.42.0': + '@rollup/rollup-freebsd-x64@4.45.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.42.0': + '@rollup/rollup-linux-arm-gnueabihf@4.45.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.42.0': + '@rollup/rollup-linux-arm-musleabihf@4.45.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.42.0': + '@rollup/rollup-linux-arm64-gnu@4.45.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.42.0': + '@rollup/rollup-linux-arm64-musl@4.45.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.42.0': + '@rollup/rollup-linux-loongarch64-gnu@4.45.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.42.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.42.0': + '@rollup/rollup-linux-riscv64-gnu@4.45.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.42.0': + '@rollup/rollup-linux-riscv64-musl@4.45.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.42.0': + '@rollup/rollup-linux-s390x-gnu@4.45.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.42.0': + '@rollup/rollup-linux-x64-gnu@4.45.1': optional: true - '@rollup/rollup-linux-x64-musl@4.42.0': + '@rollup/rollup-linux-x64-musl@4.45.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.42.0': + '@rollup/rollup-win32-arm64-msvc@4.45.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.42.0': + '@rollup/rollup-win32-ia32-msvc@4.45.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.42.0': + '@rollup/rollup-win32-x64-msvc@4.45.1': optional: true '@rtsao/scc@1.1.0': {} @@ -14022,49 +14083,41 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@sentry-internal/browser-utils@9.27.0': - dependencies: - '@sentry/core': 9.27.0 - '@sentry-internal/browser-utils@9.28.0': dependencies: '@sentry/core': 9.28.0 - '@sentry-internal/feedback@9.27.0': + '@sentry-internal/browser-utils@9.39.0': dependencies: - '@sentry/core': 9.27.0 + '@sentry/core': 9.39.0 '@sentry-internal/feedback@9.28.0': dependencies: '@sentry/core': 9.28.0 - '@sentry-internal/replay-canvas@9.27.0': + '@sentry-internal/feedback@9.39.0': dependencies: - '@sentry-internal/replay': 9.27.0 - '@sentry/core': 9.27.0 + '@sentry/core': 9.39.0 '@sentry-internal/replay-canvas@9.28.0': dependencies: '@sentry-internal/replay': 9.28.0 '@sentry/core': 9.28.0 - '@sentry-internal/replay@9.27.0': + '@sentry-internal/replay-canvas@9.39.0': dependencies: - '@sentry-internal/browser-utils': 9.27.0 - '@sentry/core': 9.27.0 + '@sentry-internal/replay': 9.39.0 + '@sentry/core': 9.39.0 '@sentry-internal/replay@9.28.0': dependencies: '@sentry-internal/browser-utils': 9.28.0 '@sentry/core': 9.28.0 - '@sentry/browser@9.27.0': + '@sentry-internal/replay@9.39.0': dependencies: - '@sentry-internal/browser-utils': 9.27.0 - '@sentry-internal/feedback': 9.27.0 - '@sentry-internal/replay': 9.27.0 - '@sentry-internal/replay-canvas': 9.27.0 - '@sentry/core': 9.27.0 + '@sentry-internal/browser-utils': 9.39.0 + '@sentry/core': 9.39.0 '@sentry/browser@9.28.0': dependencies: @@ -14074,12 +14127,20 @@ snapshots: '@sentry-internal/replay-canvas': 9.28.0 '@sentry/core': 9.28.0 + '@sentry/browser@9.39.0': + dependencies: + '@sentry-internal/browser-utils': 9.39.0 + '@sentry-internal/feedback': 9.39.0 + '@sentry-internal/replay': 9.39.0 + '@sentry-internal/replay-canvas': 9.39.0 + '@sentry/core': 9.39.0 + '@sentry/core@8.55.0': {} - '@sentry/core@9.27.0': {} - '@sentry/core@9.28.0': {} + '@sentry/core@9.39.0': {} + '@sentry/node@8.55.0': dependencies: '@opentelemetry/api': 1.9.0 @@ -14139,45 +14200,45 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/vue@9.27.0(vue@3.5.16(typescript@5.8.3))': - dependencies: - '@sentry/browser': 9.27.0 - '@sentry/core': 9.27.0 - vue: 3.5.16(typescript@5.8.3) - - '@sentry/vue@9.28.0(vue@3.5.16(typescript@5.8.3))': + '@sentry/vue@9.28.0(vue@3.5.17(typescript@5.8.3))': dependencies: '@sentry/browser': 9.28.0 '@sentry/core': 9.28.0 - vue: 3.5.16(typescript@5.8.3) + vue: 3.5.17(typescript@5.8.3) - '@shikijs/core@3.6.0': + '@sentry/vue@9.39.0(vue@3.5.17(typescript@5.8.3))': dependencies: - '@shikijs/types': 3.6.0 + '@sentry/browser': 9.39.0 + '@sentry/core': 9.39.0 + vue: 3.5.17(typescript@5.8.3) + + '@shikijs/core@3.8.0': + dependencies: + '@shikijs/types': 3.8.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@3.6.0': + '@shikijs/engine-javascript@3.8.0': dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.8.0 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.3 - '@shikijs/engine-oniguruma@3.6.0': + '@shikijs/engine-oniguruma@3.8.0': dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.8.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.6.0': + '@shikijs/langs@3.8.0': dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.8.0 - '@shikijs/themes@3.6.0': + '@shikijs/themes@3.8.0': dependencies: - '@shikijs/types': 3.6.0 + '@shikijs/types': 3.8.0 - '@shikijs/types@3.6.0': + '@shikijs/types@3.8.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -14617,144 +14678,144 @@ snapshots: '@sqltools/formatter@1.2.5': {} - '@storybook/addon-actions@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-actions@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.2.2 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) uuid: 9.0.1 - '@storybook/addon-backgrounds@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-backgrounds@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-controls@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-controls@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-docs@8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@mdx-js/react': 3.0.1(@types/react@18.0.28)(react@19.1.0) - '@storybook/blocks': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/blocks': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-essentials@8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - '@storybook/addon-actions': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-backgrounds': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-controls': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-docs': 8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-highlight': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-measure': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-outline': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-toolbars': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/addon-viewport': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + '@storybook/addon-actions': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-backgrounds': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-controls': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-docs': 8.6.14(@types/react@18.0.28)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-highlight': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-measure': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-outline': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-toolbars': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/addon-viewport': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-highlight@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/addon-interactions@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-interactions@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/instrumenter': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) polished: 4.2.2 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-links@8.6.14(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-links@8.6.14(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 optionalDependencies: react: 19.1.0 - '@storybook/addon-mdx-gfm@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-mdx-gfm@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: remark-gfm: 4.0.0 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color - '@storybook/addon-measure@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-measure@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) tiny-invariant: 1.3.3 - '@storybook/addon-outline@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-outline@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - '@storybook/addon-storysource@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-storysource@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - '@storybook/source-loader': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/source-loader': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) estraverse: 5.3.0 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) tiny-invariant: 1.3.3 - '@storybook/addon-toolbars@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-toolbars@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/addon-viewport@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/addon-viewport@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: memoizerific: 1.11.3 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/icons': 1.2.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 optionalDependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@storybook/builder-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': + '@storybook/builder-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: - '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) browser-assert: 1.2.1 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) - '@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/core@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)': + '@storybook/core@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5)': dependencies: - '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) better-opn: 3.0.2 browser-assert: 1.2.1 esbuild: 0.25.6 @@ -14766,16 +14827,16 @@ snapshots: util: 0.12.5 ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5) optionalDependencies: - prettier: 3.5.3 + prettier: 3.6.2 transitivePeerDependencies: - bufferutil - storybook - supports-color - utf-8-validate - '@storybook/csf-plugin@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/csf-plugin@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) unplugin: 1.4.0 '@storybook/global@5.0.0': {} @@ -14785,115 +14846,115 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@storybook/instrumenter@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/instrumenter@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 '@vitest/utils': 2.1.1 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/react-dom-shim@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/react-dom-shim@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.42.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': + '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.45.1)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) - '@rollup/pluginutils': 5.1.4(rollup@4.42.0) - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) - '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + '@rollup/pluginutils': 5.2.0(rollup@4.45.1) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3) find-up: 5.0.0 magic-string: 0.30.17 react: 19.1.0 react-docgen: 7.0.1 react-dom: 19.1.0(react@19.1.0) resolve: 1.22.8 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) tsconfig-paths: 4.2.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) optionalDependencies: - '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) transitivePeerDependencies: - rollup - supports-color - typescript - '@storybook/react@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.3)': + '@storybook/react@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(typescript@5.8.3)': dependencies: - '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/react-dom-shim': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) optionalDependencies: - '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/test': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) typescript: 5.8.3 - '@storybook/source-loader@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/source-loader@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: es-toolkit: 1.27.0 estraverse: 5.3.0 - prettier: 3.5.3 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + prettier: 3.6.2 + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/test@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/instrumenter': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@testing-library/dom': 10.4.0 '@testing-library/jest-dom': 6.5.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) '@vitest/expect': 2.0.5 '@vitest/spy': 2.0.5 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))': + '@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))': dependencies: - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) - '@storybook/vue3-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': + '@storybook/vue3-vite@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))': dependencies: - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) - '@storybook/vue3': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.16(typescript@5.8.3)) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + '@storybook/vue3': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vue@3.5.17(typescript@5.8.3)) find-package-json: 1.2.0 magic-string: 0.30.17 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) typescript: 5.8.3 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) vue-component-meta: 2.0.16(typescript@5.8.3) - vue-docgen-api: 4.75.1(vue@3.5.16(typescript@5.8.3)) + vue-docgen-api: 4.75.1(vue@3.5.17(typescript@5.8.3)) transitivePeerDependencies: - vue - '@storybook/vue3@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.16(typescript@5.8.3))': + '@storybook/vue3@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(vue@3.5.17(typescript@5.8.3))': dependencies: - '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@vue/compiler-core': 3.5.16 - storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5) + '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@vue/compiler-core': 3.5.17 + storybook: 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5) ts-dedent: 2.2.0 type-fest: 2.19.0 - vue: 3.5.16(typescript@5.8.3) + vue: 3.5.17(typescript@5.8.3) vue-component-type-helpers: 3.0.1 '@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3)': @@ -14901,7 +14962,7 @@ snapshots: '@typescript-eslint/utils': 8.37.0(eslint@9.31.0)(typescript@5.8.3) eslint: 9.31.0 eslint-visitor-keys: 4.2.1 - espree: 10.3.0 + espree: 10.4.0 estraverse: 5.3.0 picomatch: 4.0.2 transitivePeerDependencies: @@ -15132,14 +15193,14 @@ snapshots: dependencies: '@testing-library/dom': 10.4.0 - '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.16)(@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))': + '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.17)(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))': dependencies: '@babel/runtime': 7.27.0 '@testing-library/dom': 9.3.4 - '@vue/test-utils': 2.4.1(@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3)) - vue: 3.5.16(typescript@5.8.3) + '@vue/test-utils': 2.4.1(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)) + vue: 3.5.17(typescript@5.8.3) optionalDependencies: - '@vue/compiler-sfc': 3.5.16 + '@vue/compiler-sfc': 3.5.17 transitivePeerDependencies: - '@vue/server-renderer' @@ -15175,24 +15236,24 @@ snapshots: '@types/babel__core@7.20.0': dependencies: - '@babel/parser': 7.25.6 - '@babel/types': 7.25.6 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 '@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.25.6 + '@babel/types': 7.27.1 '@types/babel__template@7.4.1': dependencies: - '@babel/parser': 7.25.6 - '@babel/types': 7.25.6 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 '@types/babel__traverse@7.20.0': dependencies: - '@babel/types': 7.25.6 + '@babel/types': 7.27.1 '@types/bcryptjs@2.4.6': {} @@ -15242,8 +15303,6 @@ snapshots: '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 - '@types/estree@1.0.7': {} - '@types/estree@1.0.8': {} '@types/express-serve-static-core@4.17.33': @@ -15503,7 +15562,7 @@ snapshots: '@types/wawoff2@1.0.2': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/web-push@3.6.4': dependencies: @@ -15589,7 +15648,7 @@ snapshots: '@typescript-eslint/project-service@8.34.0(typescript@5.8.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.3) - '@typescript-eslint/types': 8.34.0 + '@typescript-eslint/types': 8.37.0 debug: 4.4.1(supports-color@10.0.0) typescript: 5.8.3 transitivePeerDependencies: @@ -15715,12 +15774,12 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.16(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))(vue@3.5.17(typescript@5.8.3))': dependencies: - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) - vue: 3.5.16(typescript@5.8.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vue: 3.5.17(typescript@5.8.3) - '@vitest/coverage-v8@3.2.3(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -15735,7 +15794,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) transitivePeerDependencies: - supports-color @@ -15746,22 +15805,22 @@ snapshots: chai: 5.2.0 tinyrainbow: 1.2.0 - '@vitest/expect@3.2.3': + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.2 - '@vitest/spy': 3.2.3 - '@vitest/utils': 3.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': + '@vitest/mocker@3.2.4(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3))': dependencies: - '@vitest/spy': 3.2.3 + '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.10.2(@types/node@22.15.31)(typescript@5.8.3) - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + msw: 2.10.4(@types/node@22.16.4)(typescript@5.8.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) '@vitest/pretty-format@2.0.5': dependencies: @@ -15771,19 +15830,19 @@ snapshots: dependencies: tinyrainbow: 1.2.0 - '@vitest/pretty-format@3.2.3': + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.2.3': + '@vitest/runner@3.2.4': dependencies: - '@vitest/utils': 3.2.3 + '@vitest/utils': 3.2.4 pathe: 2.0.3 strip-literal: 3.0.0 - '@vitest/snapshot@3.2.3': + '@vitest/snapshot@3.2.4': dependencies: - '@vitest/pretty-format': 3.2.3 + '@vitest/pretty-format': 3.2.4 magic-string: 0.30.17 pathe: 2.0.3 @@ -15791,7 +15850,7 @@ snapshots: dependencies: tinyspy: 3.0.2 - '@vitest/spy@3.2.3': + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.3 @@ -15808,34 +15867,34 @@ snapshots: loupe: 3.1.3 tinyrainbow: 1.2.0 - '@vitest/utils@3.2.3': + '@vitest/utils@3.2.4': dependencies: - '@vitest/pretty-format': 3.2.3 - loupe: 3.1.3 + '@vitest/pretty-format': 3.2.4 + loupe: 3.1.4 tinyrainbow: 2.0.0 '@volar/language-core@2.2.0': dependencies: '@volar/source-map': 2.2.0 - '@volar/language-core@2.4.11': + '@volar/language-core@2.4.15': dependencies: - '@volar/source-map': 2.4.11 + '@volar/source-map': 2.4.15 '@volar/source-map@2.2.0': dependencies: muggle-string: 0.4.1 - '@volar/source-map@2.4.11': {} + '@volar/source-map@2.4.15': {} '@volar/typescript@2.2.0': dependencies: '@volar/language-core': 2.2.0 path-browserify: 1.0.1 - '@volar/typescript@2.4.11': + '@volar/typescript@2.4.15': dependencies: - '@volar/language-core': 2.4.11 + '@volar/language-core': 2.4.15 path-browserify: 1.0.1 vscode-uri: 3.0.8 @@ -15847,14 +15906,6 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-core@3.5.14': - dependencies: - '@babel/parser': 7.27.2 - '@vue/shared': 3.5.14 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - '@vue/compiler-core@3.5.16': dependencies: '@babel/parser': 7.27.2 @@ -15863,37 +15914,45 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 + '@vue/compiler-core@3.5.17': + dependencies: + '@babel/parser': 7.28.0 + '@vue/shared': 3.5.17 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + '@vue/compiler-dom@3.5.13': dependencies: '@vue/compiler-core': 3.5.13 '@vue/shared': 3.5.13 - '@vue/compiler-dom@3.5.14': - dependencies: - '@vue/compiler-core': 3.5.14 - '@vue/shared': 3.5.14 - '@vue/compiler-dom@3.5.16': dependencies: '@vue/compiler-core': 3.5.16 '@vue/shared': 3.5.16 - '@vue/compiler-sfc@3.5.16': + '@vue/compiler-dom@3.5.17': dependencies: - '@babel/parser': 7.27.2 - '@vue/compiler-core': 3.5.16 - '@vue/compiler-dom': 3.5.16 - '@vue/compiler-ssr': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/compiler-core': 3.5.17 + '@vue/shared': 3.5.17 + + '@vue/compiler-sfc@3.5.17': + dependencies: + '@babel/parser': 7.28.0 + '@vue/compiler-core': 3.5.17 + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 estree-walker: 2.0.2 magic-string: 0.30.17 - postcss: 8.5.3 + postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.16': + '@vue/compiler-ssr@3.5.17': dependencies: - '@vue/compiler-dom': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/compiler-dom': 3.5.17 + '@vue/shared': 3.5.17 '@vue/compiler-vue2@2.7.16': dependencies: @@ -15903,8 +15962,8 @@ snapshots: '@vue/language-core@2.0.16(typescript@5.8.3)': dependencies: '@volar/language-core': 2.2.0 - '@vue/compiler-dom': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/compiler-dom': 3.5.16 + '@vue/shared': 3.5.16 computeds: 0.0.1 minimatch: 9.0.5 path-browserify: 1.0.1 @@ -15912,12 +15971,12 @@ snapshots: optionalDependencies: typescript: 5.8.3 - '@vue/language-core@2.2.10(typescript@5.8.3)': + '@vue/language-core@2.2.12(typescript@5.8.3)': dependencies: - '@volar/language-core': 2.4.11 - '@vue/compiler-dom': 3.5.13 + '@volar/language-core': 2.4.15 + '@vue/compiler-dom': 3.5.16 '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.13 + '@vue/shared': 3.5.16 alien-signals: 1.0.3 minimatch: 9.0.5 muggle-string: 0.4.1 @@ -15925,41 +15984,41 @@ snapshots: optionalDependencies: typescript: 5.8.3 - '@vue/reactivity@3.5.16': + '@vue/reactivity@3.5.17': dependencies: - '@vue/shared': 3.5.16 + '@vue/shared': 3.5.17 - '@vue/runtime-core@3.5.16': + '@vue/runtime-core@3.5.17': dependencies: - '@vue/reactivity': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/reactivity': 3.5.17 + '@vue/shared': 3.5.17 - '@vue/runtime-dom@3.5.16': + '@vue/runtime-dom@3.5.17': dependencies: - '@vue/reactivity': 3.5.16 - '@vue/runtime-core': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/reactivity': 3.5.17 + '@vue/runtime-core': 3.5.17 + '@vue/shared': 3.5.17 csstype: 3.1.3 - '@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3))': + '@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3))': dependencies: - '@vue/compiler-ssr': 3.5.16 - '@vue/shared': 3.5.16 - vue: 3.5.16(typescript@5.8.3) + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 + vue: 3.5.17(typescript@5.8.3) '@vue/shared@3.5.13': {} - '@vue/shared@3.5.14': {} - '@vue/shared@3.5.16': {} - '@vue/test-utils@2.4.1(@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))': + '@vue/shared@3.5.17': {} + + '@vue/test-utils@2.4.1(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))': dependencies: js-beautify: 1.14.9 - vue: 3.5.16(typescript@5.8.3) + vue: 3.5.17(typescript@5.8.3) vue-component-type-helpers: 1.8.4 optionalDependencies: - '@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.8.3)) + '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.8.3)) '@webgpu/types@0.1.38': {} @@ -16237,6 +16296,11 @@ snapshots: call-bind: 1.0.7 is-array-buffer: 3.0.4 + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + array-flatten@1.1.1: {} array-includes@3.1.8: @@ -16248,6 +16312,17 @@ snapshots: get-intrinsic: 1.3.0 is-string: 1.0.7 + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + array-union@2.1.0: {} array.prototype.findlastindex@1.2.5: @@ -16259,6 +16334,16 @@ snapshots: es-object-atoms: 1.1.1 es-shim-unscopables: 1.0.2 + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 @@ -16266,6 +16351,13 @@ snapshots: es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.0.2 + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.7 @@ -16273,6 +16365,13 @@ snapshots: es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.0.2 + arraybuffer.prototype.slice@1.0.3: dependencies: array-buffer-byte-length: 1.0.1 @@ -16284,6 +16383,16 @@ snapshots: is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + arrify@1.0.1: {} asap@2.0.6: {} @@ -16325,6 +16434,8 @@ snapshots: astring@1.9.0: {} + async-function@1.0.0: {} + async-mutex@0.5.0: dependencies: tslib: 2.8.1 @@ -16428,7 +16539,7 @@ snapshots: babel-walk@3.0.0-canary-5: dependencies: - '@babel/types': 7.25.6 + '@babel/types': 7.27.1 bail@2.0.2: {} @@ -16635,6 +16746,13 @@ snapshots: get-intrinsic: 1.3.0 set-function-length: 1.2.2 + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -16727,27 +16845,27 @@ snapshots: dependencies: is-regex: 1.1.4 - chart.js@4.4.9: + chart.js@4.5.0: dependencies: '@kurkle/color': 0.3.2 - chartjs-adapter-date-fns@3.0.0(chart.js@4.4.9)(date-fns@4.1.0): + chartjs-adapter-date-fns@3.0.0(chart.js@4.5.0)(date-fns@4.1.0): dependencies: - chart.js: 4.4.9 + chart.js: 4.5.0 date-fns: 4.1.0 - chartjs-chart-matrix@2.1.1(chart.js@4.4.9): + chartjs-chart-matrix@2.1.1(chart.js@4.5.0): dependencies: - chart.js: 4.4.9 + chart.js: 4.5.0 - chartjs-plugin-gradient@0.6.1(chart.js@4.4.9): + chartjs-plugin-gradient@0.6.1(chart.js@4.5.0): dependencies: - chart.js: 4.4.9 + chart.js: 4.5.0 - chartjs-plugin-zoom@2.2.0(chart.js@4.4.9): + chartjs-plugin-zoom@2.2.0(chart.js@4.5.0): dependencies: '@types/hammerjs': 2.0.46 - chart.js: 4.4.9 + chart.js: 4.5.0 hammerjs: 2.0.8 check-error@2.1.1: {} @@ -16777,6 +16895,20 @@ snapshots: undici: 6.19.8 whatwg-mimetype: 4.0.0 + cheerio@1.1.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.0 + htmlparser2: 10.0.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.11.0 + whatwg-mimetype: 4.0.0 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -16944,7 +17076,7 @@ snapshots: constantinople@4.0.1: dependencies: - '@babel/parser': 7.25.6 + '@babel/parser': 7.27.2 '@babel/types': 7.25.6 content-disposition@0.5.4: @@ -17129,52 +17261,6 @@ snapshots: csstype@3.1.3: {} - cypress@14.4.1: - dependencies: - '@cypress/request': 3.0.8 - '@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 - ci-info: 4.1.0 - cli-cursor: 3.1.0 - cli-table3: 0.6.1 - commander: 6.2.1 - common-tags: 1.8.2 - dayjs: 1.11.13 - debug: 4.4.1(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-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.7.2 - supports-color: 8.1.1 - tmp: 0.2.3 - tree-kill: 1.2.2 - untildify: 4.0.0 - yauzl: 2.10.0 - cypress@14.5.2: dependencies: '@cypress/request': 3.0.8 @@ -17239,18 +17325,36 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + date-fns@2.30.0: dependencies: '@babel/runtime': 7.27.0 @@ -17472,6 +17576,12 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dotenv@16.4.7: {} dunder-proto@1.0.1: @@ -17597,6 +17707,63 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.15 + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -17630,12 +17797,22 @@ snapshots: dependencies: hasown: 2.0.2 + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + es-toolkit@1.27.0: {} es6-promise@4.2.8: @@ -17681,34 +17858,6 @@ snapshots: '@esbuild/win32-ia32': 0.25.4 '@esbuild/win32-x64': 0.25.4 - esbuild@0.25.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.5 - '@esbuild/android-arm': 0.25.5 - '@esbuild/android-arm64': 0.25.5 - '@esbuild/android-x64': 0.25.5 - '@esbuild/darwin-arm64': 0.25.5 - '@esbuild/darwin-x64': 0.25.5 - '@esbuild/freebsd-arm64': 0.25.5 - '@esbuild/freebsd-x64': 0.25.5 - '@esbuild/linux-arm': 0.25.5 - '@esbuild/linux-arm64': 0.25.5 - '@esbuild/linux-ia32': 0.25.5 - '@esbuild/linux-loong64': 0.25.5 - '@esbuild/linux-mips64el': 0.25.5 - '@esbuild/linux-ppc64': 0.25.5 - '@esbuild/linux-riscv64': 0.25.5 - '@esbuild/linux-s390x': 0.25.5 - '@esbuild/linux-x64': 0.25.5 - '@esbuild/netbsd-arm64': 0.25.5 - '@esbuild/netbsd-x64': 0.25.5 - '@esbuild/openbsd-arm64': 0.25.5 - '@esbuild/openbsd-x64': 0.25.5 - '@esbuild/sunos-x64': 0.25.5 - '@esbuild/win32-arm64': 0.25.5 - '@esbuild/win32-ia32': 0.25.5 - '@esbuild/win32-x64': 0.25.5 - esbuild@0.25.6: optionalDependencies: '@esbuild/aix-ppc64': 0.25.6 @@ -17783,7 +17932,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: @@ -17822,27 +17971,27 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0): dependencies: '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 9.31.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0) hasown: 2.0.2 - is-core-module: 2.15.1 + is-core-module: 2.16.1 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 object.groupby: 1.0.3 - object.values: 1.2.0 + object.values: 1.2.1 semver: 6.3.1 - string.prototype.trimend: 1.0.8 + string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) @@ -17851,7 +18000,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-vue@10.2.0(eslint@9.31.0)(vue-eslint-parser@10.1.3(eslint@9.31.0)): + eslint-plugin-vue@10.3.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(vue-eslint-parser@10.2.0(eslint@9.31.0)): dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) eslint: 9.31.0 @@ -17859,16 +18008,13 @@ snapshots: nth-check: 2.1.1 postcss-selector-parser: 6.1.2 semver: 7.7.2 - vue-eslint-parser: 10.1.3(eslint@9.31.0) + vue-eslint-parser: 10.2.0(eslint@9.31.0) xml-name-validator: 4.0.0 + optionalDependencies: + '@typescript-eslint/parser': 8.37.0(eslint@9.31.0)(typescript@5.8.3) eslint-rule-docs@1.1.235: {} - eslint-scope@8.3.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -17920,12 +18066,6 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.3.0: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.0 - espree@10.4.0: dependencies: acorn: 8.15.0 @@ -18323,6 +18463,10 @@ snapshots: dependencies: is-callable: 1.2.7 + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + foreground-child@3.1.1: dependencies: cross-spawn: 7.0.6 @@ -18410,6 +18554,15 @@ snapshots: es-abstract: 1.23.3 functions-have-names: 1.2.3 + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + functions-have-names@1.2.3: {} gauge@3.0.2: @@ -18468,6 +18621,12 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + get-tsconfig@4.10.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -18508,7 +18667,7 @@ snapshots: dependencies: foreground-child: 3.1.1 jackspeak: 4.0.1 - minimatch: 10.0.1 + minimatch: 10.0.3 minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 2.0.0 @@ -18553,6 +18712,11 @@ snapshots: dependencies: define-properties: 1.2.1 + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + globalyzer@0.1.0: {} globby@11.1.0: @@ -18633,6 +18797,10 @@ snapshots: has-proto@1.0.3: {} + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -18693,12 +18861,21 @@ snapshots: html-entities@2.5.2: {} + html-entities@2.6.0: {} + html-escaper@2.0.2: {} html-void-elements@3.0.0: {} htmlescape@1.1.1: {} + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.0 + htmlparser2@5.0.1: dependencies: domelementtype: 2.3.0 @@ -18861,6 +19038,12 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + intersection-observer@0.12.2: {} ioredis@5.6.1: @@ -18904,33 +19087,71 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.3.0 + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} is-arrayish@0.3.2: {} + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 + is-bigint@1.1.0: + dependencies: + has-bigints: 1.0.2 + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-callable@1.2.7: {} is-core-module@2.15.1: dependencies: hasown: 2.0.2 + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-docker@2.2.1: {} is-expression@4.0.0: @@ -18942,6 +19163,10 @@ snapshots: is-file-animated@1.0.2: {} + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} is-generator-fn@2.1.0: {} @@ -18961,6 +19186,8 @@ snapshots: is-map@2.0.2: {} + is-map@2.0.3: {} + is-negative-zero@2.0.3: {} is-node-process@1.2.0: {} @@ -18969,6 +19196,11 @@ snapshots: dependencies: has-tostringtag: 1.0.2 + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-number@7.0.0: {} is-path-inside@3.0.3: {} @@ -18988,12 +19220,25 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + is-set@2.0.2: {} + is-set@2.0.3: {} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + is-stream@2.0.1: {} is-stream@3.0.0: {} @@ -19004,6 +19249,11 @@ snapshots: dependencies: has-tostringtag: 1.0.2 + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + is-svg@5.1.0: dependencies: fast-xml-parser: 4.5.0 @@ -19012,10 +19262,20 @@ snapshots: dependencies: has-symbols: 1.1.0 + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + is-typedarray@1.0.0: {} is-unicode-supported@0.1.0: {} @@ -19024,15 +19284,26 @@ snapshots: is-weakmap@2.0.1: {} + is-weakmap@2.0.2: {} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + is-weakset@2.0.2: dependencies: call-bind: 1.0.7 get-intrinsic: 1.3.0 + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -19764,6 +20035,8 @@ snapshots: loupe@3.1.3: {} + loupe@3.1.4: {} + lowercase-keys@3.0.0: {} lru-cache@10.4.3: {} @@ -20221,10 +20494,6 @@ snapshots: minimalistic-assert@1.0.1: {} - minimatch@10.0.1: - dependencies: - brace-expansion: 2.0.1 - minimatch@10.0.3: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -20237,10 +20506,6 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@5.1.2: - dependencies: - brace-expansion: 2.0.1 - minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 @@ -20347,17 +20612,17 @@ snapshots: optionalDependencies: msgpackr-extract: 3.0.2 - msw-storybook-addon@2.0.5(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3)): + msw-storybook-addon@2.0.5(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3)): dependencies: is-node-process: 1.2.0 - msw: 2.10.2(@types/node@22.15.31)(typescript@5.8.3) + msw: 2.10.4(@types/node@22.16.4)(typescript@5.8.3) - msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3): + msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3): dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.0.2(@types/node@22.15.31) + '@inquirer/confirm': 5.0.2(@types/node@22.16.4) '@mswjs/interceptors': 0.39.2 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 @@ -20370,7 +20635,7 @@ snapshots: path-to-regexp: 6.3.0 picocolors: 1.1.1 strict-event-emitter: 0.5.1 - type-fest: 4.26.1 + type-fest: 4.41.0 yargs: 17.7.2 optionalDependencies: typescript: 5.8.3 @@ -20620,6 +20885,15 @@ snapshots: has-symbols: 1.1.0 object-keys: 1.1.1 + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 @@ -20639,6 +20913,13 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + obliterator@2.0.4: {} oblivious-set@1.4.0: {} @@ -20706,6 +20987,12 @@ snapshots: outvariant@1.4.3: {} + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + p-cancelable@3.0.0: {} p-cancelable@4.0.1: {} @@ -20777,6 +21064,11 @@ snapshots: domhandler: 5.0.3 parse5: 7.3.0 + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + parse5-parser-stream@7.1.2: dependencies: parse5: 7.3.0 @@ -21140,7 +21432,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.5.3: {} + prettier@3.6.2: {} pretty-bytes@5.6.0: {} @@ -21459,7 +21751,7 @@ snapshots: readdir-glob@1.1.2: dependencies: - minimatch: 5.1.2 + minimatch: 5.1.6 readdirp@4.1.2: {} @@ -21494,6 +21786,17 @@ snapshots: reflect-metadata@0.2.2: {} + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + regenerator-runtime@0.13.11: {} regenerator-runtime@0.14.0: {} @@ -21515,6 +21818,15 @@ snapshots: es-errors: 1.3.0 set-function-name: 2.0.2 + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + remark-gfm@4.0.0: dependencies: '@types/mdast': 4.0.3 @@ -21618,30 +21930,30 @@ snapshots: dependencies: glob: 10.4.5 - rollup@4.42.0: + rollup@4.45.1: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.42.0 - '@rollup/rollup-android-arm64': 4.42.0 - '@rollup/rollup-darwin-arm64': 4.42.0 - '@rollup/rollup-darwin-x64': 4.42.0 - '@rollup/rollup-freebsd-arm64': 4.42.0 - '@rollup/rollup-freebsd-x64': 4.42.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.42.0 - '@rollup/rollup-linux-arm-musleabihf': 4.42.0 - '@rollup/rollup-linux-arm64-gnu': 4.42.0 - '@rollup/rollup-linux-arm64-musl': 4.42.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.42.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.42.0 - '@rollup/rollup-linux-riscv64-gnu': 4.42.0 - '@rollup/rollup-linux-riscv64-musl': 4.42.0 - '@rollup/rollup-linux-s390x-gnu': 4.42.0 - '@rollup/rollup-linux-x64-gnu': 4.42.0 - '@rollup/rollup-linux-x64-musl': 4.42.0 - '@rollup/rollup-win32-arm64-msvc': 4.42.0 - '@rollup/rollup-win32-ia32-msvc': 4.42.0 - '@rollup/rollup-win32-x64-msvc': 4.42.0 + '@rollup/rollup-android-arm-eabi': 4.45.1 + '@rollup/rollup-android-arm64': 4.45.1 + '@rollup/rollup-darwin-arm64': 4.45.1 + '@rollup/rollup-darwin-x64': 4.45.1 + '@rollup/rollup-freebsd-arm64': 4.45.1 + '@rollup/rollup-freebsd-x64': 4.45.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 + '@rollup/rollup-linux-arm-musleabihf': 4.45.1 + '@rollup/rollup-linux-arm64-gnu': 4.45.1 + '@rollup/rollup-linux-arm64-musl': 4.45.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 + '@rollup/rollup-linux-riscv64-gnu': 4.45.1 + '@rollup/rollup-linux-riscv64-musl': 4.45.1 + '@rollup/rollup-linux-s390x-gnu': 4.45.1 + '@rollup/rollup-linux-x64-gnu': 4.45.1 + '@rollup/rollup-linux-x64-musl': 4.45.1 + '@rollup/rollup-win32-arm64-msvc': 4.45.1 + '@rollup/rollup-win32-ia32-msvc': 4.45.1 + '@rollup/rollup-win32-x64-msvc': 4.45.1 fsevents: 2.3.3 rrweb-cssom@0.8.0: {} @@ -21666,16 +21978,35 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.1.4 + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + safe-regex2@4.0.0: dependencies: ret: 0.5.0 @@ -21786,6 +22117,12 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + setimmediate@1.0.5: {} setprototypeof@1.2.0: {} @@ -21855,14 +22192,14 @@ snapshots: shebang-regex@3.0.0: {} - shiki@3.6.0: + shiki@3.8.0: dependencies: - '@shikijs/core': 3.6.0 - '@shikijs/engine-javascript': 3.6.0 - '@shikijs/engine-oniguruma': 3.6.0 - '@shikijs/langs': 3.6.0 - '@shikijs/themes': 3.6.0 - '@shikijs/types': 3.6.0 + '@shikijs/core': 3.8.0 + '@shikijs/engine-javascript': 3.8.0 + '@shikijs/engine-oniguruma': 3.8.0 + '@shikijs/langs': 3.8.0 + '@shikijs/themes': 3.8.0 + '@shikijs/types': 3.8.0 '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -22129,24 +22466,29 @@ snapshots: dependencies: internal-slot: 1.0.7 - storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + stop-iteration-iterator@1.1.0: dependencies: - '@storybook/blocks': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/core-events': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) - '@storybook/types': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)) + es-errors: 1.3.0 + internal-slot: 1.1.0 + + storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/components@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/core-events@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/manager-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/preview-api@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/theming@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(@storybook/types@8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@storybook/blocks': 8.6.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/components': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/core-events': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/manager-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/preview-api': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/theming': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) + '@storybook/types': 8.6.14(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5)) optionalDependencies: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5): + storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5): dependencies: - '@storybook/core': 8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5) + '@storybook/core': 8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(storybook@8.6.14(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@6.0.5))(utf-8-validate@6.0.5) optionalDependencies: - prettier: 3.5.3 + prettier: 3.6.2 transitivePeerDependencies: - bufferutil - supports-color @@ -22197,6 +22539,16 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 @@ -22210,6 +22562,13 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 @@ -22426,7 +22785,7 @@ snapshots: dependencies: real-require: 0.2.0 - three@0.177.0: {} + three@0.178.0: {} throttle-debounce@5.0.2: {} @@ -22457,7 +22816,7 @@ snapshots: fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 - tinypool@1.1.0: {} + tinypool@1.1.1: {} tinyrainbow@1.2.0: {} @@ -22570,13 +22929,6 @@ snapshots: tslib@2.8.1: {} - tsx@4.19.4: - dependencies: - esbuild: 0.25.5 - get-tsconfig: 4.10.1 - optionalDependencies: - fsevents: 2.3.3 - tsx@4.20.3: dependencies: esbuild: 0.25.6 @@ -22621,6 +22973,12 @@ snapshots: es-errors: 1.3.0 is-typed-array: 1.1.13 + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 @@ -22629,6 +22987,14 @@ snapshots: has-proto: 1.0.3 is-typed-array: 1.1.13 + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 @@ -22638,6 +23004,16 @@ snapshots: has-proto: 1.0.3 is-typed-array: 1.1.13 + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 @@ -22647,6 +23023,15 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.3 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.0.0 + reflect.getprototypeof: 1.0.10 + typedarray@0.0.6: {} typeorm@0.3.24(patch_hash=2677b97a423e157945c154e64183d3ae2eb44dfa9cb0e5ce731a7612f507bb56)(ioredis@5.6.1)(pg@8.16.0)(reflect-metadata@0.2.2): @@ -22694,6 +23079,13 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.0.2 + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.0.2 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + unbzip2-stream@1.4.3: dependencies: buffer: 5.7.1 @@ -22709,6 +23101,8 @@ snapshots: undici@6.19.8: {} + undici@7.11.0: {} + unicorn-magic@0.3.0: {} unified@11.0.4: @@ -22817,13 +23211,13 @@ snapshots: uuid@9.0.1: {} - v-code-diff@1.13.1(vue@3.5.16(typescript@5.8.3)): + v-code-diff@1.13.1(vue@3.5.17(typescript@5.8.3)): dependencies: diff: 5.2.0 diff-match-patch: 1.0.5 highlight.js: 11.10.0 - vue: 3.5.16(typescript@5.8.3) - vue-demi: 0.14.7(vue@3.5.16(typescript@5.8.3)) + vue: 3.5.17(typescript@5.8.3) + vue-demi: 0.14.7(vue@3.5.17(typescript@5.8.3)) v8-to-istanbul@9.2.0: dependencies: @@ -22857,13 +23251,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): + vite-node@3.2.4(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): dependencies: cac: 6.7.14 debug: 4.4.1(supports-color@10.0.0) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) transitivePeerDependencies: - '@types/node' - jiti @@ -22880,35 +23274,35 @@ snapshots: vite-plugin-turbosnap@1.0.3: {} - vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): + vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): dependencies: esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 postcss: 8.5.3 - rollup: 4.42.0 + rollup: 4.45.1 tinyglobby: 0.2.13 optionalDependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 fsevents: 2.3.3 sass: 1.89.2 terser: 5.43.1 tsx: 4.20.3 - vitest-fetch-mock@0.4.5(vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)): + vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)): dependencies: - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) - vitest@3.2.3(@types/debug@4.1.12)(@types/node@22.15.31)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.4)(happy-dom@17.6.3)(jsdom@26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): dependencies: '@types/chai': 5.2.2 - '@vitest/expect': 3.2.3 - '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(vite@6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) - '@vitest/pretty-format': 3.2.3 - '@vitest/runner': 3.2.3 - '@vitest/snapshot': 3.2.3 - '@vitest/spy': 3.2.3 - '@vitest/utils': 3.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(msw@2.10.4(@types/node@22.16.4)(typescript@5.8.3))(vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 chai: 5.2.0 debug: 4.4.1(supports-color@10.0.0) expect-type: 1.2.1 @@ -22919,14 +23313,14 @@ snapshots: tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.14 - tinypool: 1.1.0 + tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) - vite-node: 3.2.3(@types/node@22.15.31)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite: 6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) + vite-node: 3.2.4(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.15.31 + '@types/node': 22.16.4 happy-dom: 17.6.3 jsdom: 26.1.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5) transitivePeerDependencies: @@ -22949,7 +23343,7 @@ snapshots: vscode-languageclient@9.0.1: dependencies: - minimatch: 5.1.2 + minimatch: 5.1.6 semver: 7.7.2 vscode-languageserver-protocol: 3.17.5 @@ -22975,71 +23369,70 @@ snapshots: vue-component-type-helpers@2.0.16: {} - vue-component-type-helpers@2.2.10: {} + vue-component-type-helpers@2.2.12: {} vue-component-type-helpers@3.0.1: {} - vue-demi@0.14.7(vue@3.5.16(typescript@5.8.3)): + vue-demi@0.14.7(vue@3.5.17(typescript@5.8.3)): dependencies: - vue: 3.5.16(typescript@5.8.3) + vue: 3.5.17(typescript@5.8.3) - vue-docgen-api@4.75.1(vue@3.5.16(typescript@5.8.3)): + vue-docgen-api@4.75.1(vue@3.5.17(typescript@5.8.3)): dependencies: - '@babel/parser': 7.25.6 + '@babel/parser': 7.27.2 '@babel/types': 7.25.6 '@vue/compiler-dom': 3.5.13 - '@vue/compiler-sfc': 3.5.16 + '@vue/compiler-sfc': 3.5.17 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.5.16(typescript@5.8.3) - vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.16(typescript@5.8.3)) + vue: 3.5.17(typescript@5.8.3) + vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.17(typescript@5.8.3)) - vue-eslint-parser@10.1.3(eslint@9.31.0): + vue-eslint-parser@10.2.0(eslint@9.31.0): dependencies: debug: 4.4.1(supports-color@10.0.0) eslint: 9.31.0 - eslint-scope: 8.3.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 - lodash: 4.17.21 semver: 7.7.2 transitivePeerDependencies: - supports-color - vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.16(typescript@5.8.3)): + vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.17(typescript@5.8.3)): dependencies: - vue: 3.5.16(typescript@5.8.3) + vue: 3.5.17(typescript@5.8.3) vue-template-compiler@2.7.14: dependencies: de-indent: 1.0.2 he: 1.2.0 - vue-tsc@2.2.10(typescript@5.8.3): + vue-tsc@2.2.12(typescript@5.8.3): dependencies: - '@volar/typescript': 2.4.11 - '@vue/language-core': 2.2.10(typescript@5.8.3) + '@volar/typescript': 2.4.15 + '@vue/language-core': 2.2.12(typescript@5.8.3) typescript: 5.8.3 - vue@3.5.16(typescript@5.8.3): + vue@3.5.17(typescript@5.8.3): dependencies: - '@vue/compiler-dom': 3.5.16 - '@vue/compiler-sfc': 3.5.16 - '@vue/runtime-dom': 3.5.16 - '@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.8.3)) - '@vue/shared': 3.5.16 + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-sfc': 3.5.17 + '@vue/runtime-dom': 3.5.17 + '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.8.3)) + '@vue/shared': 3.5.17 optionalDependencies: typescript: 5.8.3 - vuedraggable@4.1.0(vue@3.5.16(typescript@5.8.3)): + vuedraggable@4.1.0(vue@3.5.17(typescript@5.8.3)): dependencies: sortablejs: 1.14.0 - vue: 3.5.16(typescript@5.8.3) + vue: 3.5.17(typescript@5.8.3) w3c-xmlserializer@5.0.0: dependencies: @@ -23122,6 +23515,30 @@ snapshots: is-string: 1.0.7 is-symbol: 1.0.4 + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.0.10 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + which-collection@1.0.1: dependencies: is-map: 2.0.2 @@ -23129,6 +23546,13 @@ snapshots: is-weakmap: 2.0.1 is-weakset: 2.0.2 + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + which-module@2.0.0: {} which-typed-array@1.1.15: @@ -23139,6 +23563,16 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@1.3.1: dependencies: isexe: 2.0.0 @@ -23163,7 +23597,7 @@ snapshots: with@7.0.2: dependencies: - '@babel/parser': 7.25.6 + '@babel/parser': 7.27.2 '@babel/types': 7.25.6 assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 From e5c2be15f729e5911c9e6d1cb93abc5251ef2f3e 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, 17 Jul 2025 13:20:43 +0900 Subject: [PATCH 44/85] =?UTF-8?q?fix(deps):=20Node.js=E3=81=AE=E6=9C=80?= =?UTF-8?q?=E5=B0=8F=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92?= =?UTF-8?q?=E5=BC=95=E3=81=8D=E4=B8=8A=E3=81=92=20(#16296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update package.json * Update min.node-version * Update CHANGELOG.md --- .github/min.node-version | 2 +- CHANGELOG.md | 4 ++++ packages/backend/package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/min.node-version b/.github/min.node-version index d5a159609d..d4b7699d36 100644 --- a/.github/min.node-version +++ b/.github/min.node-version @@ -1 +1 @@ -20.10.0 +20.18.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 27810a5b72..112b73f331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 2025.7.0 +### Note +- Node.jsの最小バージョンを20.10.0から20.18.1に引き上げました + - なお、特に必要がない限りNode.jsは推奨バージョンであるv22を使用するようにしてください + ### General - Feat: ノートの下書き機能 - Feat: クリップ内でノートを検索できるように diff --git a/packages/backend/package.json b/packages/backend/package.json index 99482e71bc..1a25f63b83 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "engines": { - "node": "^20.10.0 || ^22.0.0" + "node": "^20.18.1 || ^22.0.0" }, "scripts": { "start": "node ./built/boot/entry.js", From a6cdcfb2cfa18bd5e65afcf2439fe8b7fcae1eb3 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 13:29:53 +0900 Subject: [PATCH 45/85] around ApDeliverManagerService --- packages/backend/src/core/QueueService.ts | 3 + .../backend/src/core/UserSuspendService.ts | 2 + .../activitypub/ApDeliverManagerService.ts | 24 +- .../test/unit/ApDeliverManagerService.ts | 620 ++++++++++++++++++ 4 files changed, 638 insertions(+), 11 deletions(-) create mode 100644 packages/backend/test/unit/ApDeliverManagerService.ts diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index 04bbc7e38a..94ee929acd 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -173,6 +173,9 @@ export class QueueService { @bindThis public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map) { if (content == null) return null; + inboxes.delete(null as unknown as string); // remove null inboxes + if (inboxes.size === 0) return null; + const contentBody = JSON.stringify(content); const digest = ApRequestCreator.createDigest(contentBody); diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index d52955ca3a..7fab5178f4 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -86,6 +86,7 @@ export class UserSuspendService { const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user)); const manager = this.apDeliverManagerService.createDeliverManager(user, content); manager.addAllKnowingSharedInboxRecipe(); + manager.addFollowersRecipe(); manager.execute(); } } @@ -98,6 +99,7 @@ export class UserSuspendService { 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(); + manager.addFollowersRecipe(); manager.execute(); } } diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 025c939831..9d22ea6e3a 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -124,7 +124,7 @@ class DeliverManager { * Execute delivers */ @bindThis - public async execute(): Promise { + public async execute(opts: { ignoreSuspend?: boolean } = {}): Promise { //#region collect inboxes by recipes // The value flags whether it is shared or not. // key: inbox URL, value: whether it is sharedInbox @@ -132,17 +132,19 @@ class DeliverManager { 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'], - }); + const followings = await this.followingsRepository.createQueryBuilder('f') + .select([ + 'f.followerSharedInbox', + 'f.followeeSharedInbox', + ]) + .where('f.followerSharedInbox IS NOT NULL') + .orWhere('f.followeeSharedInbox IS NOT NULL') + .distinct() + .getRawMany<{ f_followerSharedInbox: string | null; f_followeeSharedInbox: string | null; }>(); for (const following of followings) { - if (following.followeeSharedInbox) inboxes.set(following.followeeSharedInbox, true); - if (following.followerSharedInbox) inboxes.set(following.followerSharedInbox, true); + if (following.f_followeeSharedInbox) inboxes.set(following.f_followeeSharedInbox, true); + if (following.f_followerSharedInbox) inboxes.set(following.f_followerSharedInbox, true); } } @@ -156,7 +158,7 @@ class DeliverManager { where: { followeeId: this.actor.id, followerHost: Not(IsNull()), - isFollowerSuspended: false, + isFollowerSuspended: opts.ignoreSuspend ? undefined : false, }, select: { followerSharedInbox: true, diff --git a/packages/backend/test/unit/ApDeliverManagerService.ts b/packages/backend/test/unit/ApDeliverManagerService.ts new file mode 100644 index 0000000000..c92fc71ac7 --- /dev/null +++ b/packages/backend/test/unit/ApDeliverManagerService.ts @@ -0,0 +1,620 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +process.env.NODE_ENV = 'test'; + +import { jest } from '@jest/globals'; +import { Test } from '@nestjs/testing'; +import type { TestingModule } from '@nestjs/testing'; +import type { MiLocalUser, MiRemoteUser } from '@/models/User.js'; +import type { IActivity } from '@/core/activitypub/type.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js'; +import { QueueService } from '@/core/QueueService.js'; +import { FollowingsRepository, UsersRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { secureRndstr } from '@/misc/secure-rndstr.js'; + +describe('ApDeliverManagerService', () => { + let service: ApDeliverManagerService; + let followingsRepository: jest.Mocked; + let queueService: jest.Mocked; + let apLoggerService: jest.Mocked; + + const mockLocalUser: MiLocalUser = { + id: 'local-user-id', + host: null, + } as MiLocalUser; + + const mockRemoteUser1: MiRemoteUser & { inbox: string; sharedInbox: string; } = { + id: 'remote-user-1', + host: 'remote.example.com', + inbox: 'https://remote.example.com/inbox', + sharedInbox: 'https://remote.example.com/shared-inbox', + } as MiRemoteUser & { inbox: string; sharedInbox: string; }; + + const mockRemoteUser2: MiRemoteUser & { inbox: string; } = { + id: 'remote-user-2', + host: 'another.example.com', + inbox: 'https://another.example.com/inbox', + sharedInbox: null, + } as MiRemoteUser & { inbox: string; }; + + const mockActivity: IActivity = { + type: 'Create', + id: 'activity-id', + actor: 'https://local.example.com/users/local-user-id', + object: { + type: 'Note', + id: 'note-id', + content: 'Hello, world!', + }, + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + ApDeliverManagerService, + { + provide: DI.followingsRepository, + useValue: { + find: jest.fn(), + createQueryBuilder: jest.fn(), + }, + }, + { + provide: QueueService, + useValue: { + deliverMany: jest.fn(), + }, + }, + { + provide: ApLoggerService, + useValue: { + logger: { + createSubLogger: jest.fn().mockReturnValue({ + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }), + }, + }, + }, + ], + }).compile(); + + service = module.get(ApDeliverManagerService); + followingsRepository = module.get(DI.followingsRepository); + queueService = module.get(QueueService); + apLoggerService = module.get(ApLoggerService); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('deliverToFollowers', () => { + it('should deliver activity to all followers', async () => { + const mockFollowings = [ + { + followerSharedInbox: 'https://remote1.example.com/shared-inbox', + followerInbox: 'https://remote1.example.com/inbox', + }, + { + followerSharedInbox: 'https://remote2.example.com/shared-inbox', + followerInbox: 'https://remote2.example.com/inbox', + }, + { + followerSharedInbox: null, + followerInbox: 'https://remote3.example.com/inbox', + }, + ]; + + followingsRepository.find.mockResolvedValue(mockFollowings as any); + + await service.deliverToFollowers(mockLocalUser, mockActivity); + + expect(followingsRepository.find).toHaveBeenCalledWith({ + where: { + followeeId: mockLocalUser.id, + followerHost: expect.anything(), // Not(IsNull()) + isFollowerSuspended: false, + }, + select: { + followerSharedInbox: true, + followerInbox: true, + }, + }); + + expect(queueService.deliverMany).toHaveBeenCalledWith( + { id: mockLocalUser.id }, + mockActivity, + expect.any(Map), + ); + + // 呼び出されたinboxesを確認 + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(3); + expect(inboxes.has('https://remote1.example.com/shared-inbox')).toBe(true); + expect(inboxes.has('https://remote2.example.com/shared-inbox')).toBe(true); + expect(inboxes.has('https://remote3.example.com/inbox')).toBe(true); + }); + + it('should exclude suspended followers by default', async () => { + followingsRepository.find.mockResolvedValue([]); + + await service.deliverToFollowers(mockLocalUser, mockActivity); + + expect(followingsRepository.find).toHaveBeenCalledWith( + expect.objectContaining({ + where: expect.objectContaining({ + isFollowerSuspended: false, + }), + }), + ); + }); + }); + + describe('deliverToUser', () => { + it('should deliver activity to specific remote user', async () => { + await service.deliverToUser(mockLocalUser, mockActivity, mockRemoteUser1); + + expect(queueService.deliverMany).toHaveBeenCalledWith( + { id: mockLocalUser.id }, + mockActivity, + expect.any(Map), + ); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(1); + expect(inboxes.has(mockRemoteUser1.inbox)).toBe(true); + }); + + it('should handle user without shared inbox', async () => { + await service.deliverToUser(mockLocalUser, mockActivity, mockRemoteUser2); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(1); + expect(inboxes.has(mockRemoteUser2.inbox)).toBe(true); + }); + + it('should skip user with null inbox', async () => { + const userWithoutInbox = { + ...mockRemoteUser1, + inbox: null, + } as MiRemoteUser; + + await service.deliverToUser(mockLocalUser, mockActivity, userWithoutInbox); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(0); + }); + }); + + describe('deliverToUsers', () => { + it('should deliver activity to multiple remote users', async () => { + await service.deliverToUsers(mockLocalUser, mockActivity, [mockRemoteUser1, mockRemoteUser2]); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(2); + expect(inboxes.has(mockRemoteUser1.inbox)).toBe(true); + expect(inboxes.has(mockRemoteUser2.inbox)).toBe(true); + }); + }); + + describe('createDeliverManager', () => { + it('should create a DeliverManager instance', () => { + const manager = service.createDeliverManager(mockLocalUser, mockActivity); + + expect(manager).toBeDefined(); + expect(typeof manager.addFollowersRecipe).toBe('function'); + expect(typeof manager.addDirectRecipe).toBe('function'); + expect(typeof manager.addAllKnowingSharedInboxRecipe).toBe('function'); + expect(typeof manager.execute).toBe('function'); + }); + + it('should allow manual recipe management', async () => { + const manager = service.createDeliverManager(mockLocalUser, mockActivity); + + followingsRepository.find.mockResolvedValue([ + { + followerSharedInbox: null, + followerInbox: 'https://follower.example.com/inbox', + }, + ] as any); + + // フォロワー配信のレシピを追加 + manager.addFollowersRecipe(); + // ダイレクト配信のレシピを追加 + manager.addDirectRecipe(mockRemoteUser1); + + await manager.execute(); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(2); + expect(inboxes.has('https://follower.example.com/inbox')).toBe(true); + expect(inboxes.has(mockRemoteUser1.inbox)).toBe(true); + }); + + it('should support ignoreSuspend option', async () => { + const manager = service.createDeliverManager(mockLocalUser, mockActivity); + + followingsRepository.find.mockResolvedValue([]); + + manager.addFollowersRecipe(); + await manager.execute({ ignoreSuspend: true }); + + expect(followingsRepository.find).toHaveBeenCalledWith( + expect.objectContaining({ + where: expect.objectContaining({ + isFollowerSuspended: undefined, // ignoreSuspend: true なので undefined + }), + }), + ); + }); + + it('followers and directs mixture: 先にfollowersでsharedInboxが追加されていた場合、directsでユーザーがそのsharedInboxを持っていたらinboxを追加しない', async () => { + const manager = service.createDeliverManager(mockLocalUser, mockActivity); + followingsRepository.find.mockResolvedValue([ + { + followerSharedInbox: mockRemoteUser1.sharedInbox, + followerInbox: mockRemoteUser2.inbox, + }, + ] as any); + manager.addFollowersRecipe(); + manager.addDirectRecipe(mockRemoteUser1); + await manager.execute(); + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(1); + expect(inboxes.has(mockRemoteUser1.sharedInbox)).toBe(true); + expect(inboxes.has(mockRemoteUser1.inbox)).toBe(false); + }); + }); + + describe('error handling', () => { + it('should throw error for non-local actor', () => { + const remoteActor = { id: 'remote-id', host: 'remote.example.com' } as any; + + expect(() => { + service.createDeliverManager(remoteActor, mockActivity); + }).toThrow('actor.host must be null'); + }); + + it('should throw error when follower has null inbox', async () => { + const mockFollowings = [ + { + followerSharedInbox: null, + followerInbox: null, // null inbox + }, + ]; + + followingsRepository.find.mockResolvedValue(mockFollowings as any); + + await expect(service.deliverToFollowers(mockLocalUser, mockActivity)).rejects.toThrow('inbox is null'); + }); + }); + + describe('AllKnowingSharedInbox recipe', () => { + it('should collect all shared inboxes when using AllKnowingSharedInbox', async () => { + const mockQueryBuilder = { + select: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + orWhere: jest.fn().mockReturnThis(), + distinct: jest.fn().mockReturnThis(), + getRawMany: jest.fn().mockResolvedValue([ + { f_followerSharedInbox: 'https://shared1.example.com/inbox' }, + { f_followeeSharedInbox: 'https://shared2.example.com/inbox' }, + ]), + }; + + followingsRepository.createQueryBuilder.mockReturnValue(mockQueryBuilder as any); + + const manager = service.createDeliverManager(mockLocalUser, mockActivity); + manager.addAllKnowingSharedInboxRecipe(); + + await manager.execute(); + + expect(followingsRepository.createQueryBuilder).toHaveBeenCalledWith('f'); + expect(mockQueryBuilder.select).toHaveBeenCalledWith([ + 'f.followerSharedInbox', + 'f.followeeSharedInbox', + ]); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(2); + expect(inboxes.has('https://shared1.example.com/inbox')).toBe(true); + expect(inboxes.has('https://shared2.example.com/inbox')).toBe(true); + }); + }); +}); + +describe('ApDeliverManagerService (SQL)', () => { + // followerにデータを挿入して、SQLの動作を確認します + let app: TestingModule; + let service: ApDeliverManagerService; + let followingsRepository: FollowingsRepository; + let usersRepository: UsersRepository; + let queueService: jest.Mocked; + + async function createUser(data: Partial<{ id: string; username: string; host: string | null; inbox: string | null; sharedInbox: string | null; isSuspended: boolean }> = {}): Promise { + const user = { + id: secureRndstr(16), + username: secureRndstr(16), + usernameLower: (data.username ?? secureRndstr(16)).toLowerCase(), + host: data.host ?? null, + inbox: data.inbox ?? null, + sharedInbox: data.sharedInbox ?? null, + isSuspended: data.isSuspended ?? false, + ...data, + }; + + await usersRepository.insert(user); + return user; + } + + async function createFollowing(follower: any, followee: any, data: Partial<{ + followerInbox: string | null; + followerSharedInbox: string | null; + followeeInbox: string | null; + followeeSharedInbox: string | null; + isFollowerSuspended: boolean; + }> = {}): Promise { + const following = { + id: secureRndstr(16), + followerId: follower.id, + followeeId: followee.id, + followerHost: follower.host, + followeeHost: followee.host, + followerInbox: data.followerInbox ?? follower.inbox, + followerSharedInbox: data.followerSharedInbox ?? follower.sharedInbox, + followeeInbox: data.followeeInbox ?? null, + followeeSharedInbox: data.followeeSharedInbox ?? null, + isFollowerSuspended: data.isFollowerSuspended ?? false, + isFollowerHibernated: false, + withReplies: false, + notify: null, + }; + + await followingsRepository.insert(following); + return following; + } + + beforeEach(async () => { + const { Test } = await import('@nestjs/testing'); + const { GlobalModule } = await import('@/GlobalModule.js'); + const { DI } = await import('@/di-symbols.js'); + + app = await Test.createTestingModule({ + imports: [GlobalModule], + providers: [ + ApDeliverManagerService, + { + provide: QueueService, + useFactory: () => ({ + deliverMany: jest.fn(), + }), + }, + { + provide: ApLoggerService, + useValue: { + logger: { + createSubLogger: jest.fn().mockReturnValue({ + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }), + }, + }, + }, + ], + }).compile(); + + app.enableShutdownHooks(); + + service = app.get(ApDeliverManagerService); + followingsRepository = app.get(DI.followingsRepository); + usersRepository = app.get(DI.usersRepository); + queueService = app.get(QueueService) as jest.Mocked; + + // Reset mocks + jest.clearAllMocks(); + }); + + afterEach(async () => { + await app.close(); + }); + + describe('deliverToFollowers with real data', () => { + it('should deliver to followers excluding suspended ones', async () => { + // Create local user (followee) + const localUser = await createUser({ + host: null, + username: 'localuser', + }); + + // Create remote followers + const activeFollower = await createUser({ + host: 'active.example.com', + username: 'activefollower', + inbox: 'https://active.example.com/inbox', + sharedInbox: 'https://active.example.com/shared-inbox', + isSuspended: false, + }); + + const suspendedFollower = await createUser({ + host: 'suspended.example.com', + username: 'suspendedfollower', + inbox: 'https://suspended.example.com/inbox', + sharedInbox: 'https://suspended.example.com/shared-inbox', + isSuspended: true, + }); + + const followerWithoutSharedInbox = await createUser({ + host: 'noshared.example.com', + username: 'noshared', + inbox: 'https://noshared.example.com/inbox', + sharedInbox: null, + isSuspended: false, + }); + + // Create following relationships + await createFollowing(activeFollower, localUser, { + followerInbox: activeFollower.inbox, + followerSharedInbox: activeFollower.sharedInbox, + isFollowerSuspended: false, + }); + + await createFollowing(suspendedFollower, localUser, { + followerInbox: suspendedFollower.inbox, + followerSharedInbox: suspendedFollower.sharedInbox, + isFollowerSuspended: true, // 凍結されたフォロワー + }); + + await createFollowing(followerWithoutSharedInbox, localUser, { + followerInbox: followerWithoutSharedInbox.inbox, + followerSharedInbox: null, + isFollowerSuspended: false, + }); + + const mockActivity = { + type: 'Create', + id: 'test-activity', + actor: `https://local.example.com/users/${localUser.id}`, + object: { type: 'Note', content: 'Hello' }, + } as any; + + // Execute delivery + await service.deliverToFollowers(localUser, mockActivity); + + // Verify delivery was queued + expect(queueService.deliverMany).toHaveBeenCalledTimes(1); + const [actor, activity, inboxes] = queueService.deliverMany.mock.calls[0]; + + expect(actor.id).toBe(localUser.id); + expect(activity).toBe(mockActivity); + + // Check inboxes - should include active followers but exclude suspended ones + expect(inboxes.size).toBe(2); + expect(inboxes.has('https://active.example.com/shared-inbox')).toBe(true); + expect(inboxes.has('https://noshared.example.com/inbox')).toBe(true); + expect(inboxes.has('https://suspended.example.com/shared-inbox')).toBe(false); + }); + + it('should include suspended followers when ignoreSuspend is true', async () => { + const localUser = await createUser({ host: null }); + const suspendedFollower = await createUser({ + host: 'suspended.example.com', + inbox: 'https://suspended.example.com/inbox', + isSuspended: true, + }); + + await createFollowing(suspendedFollower, localUser, { + isFollowerSuspended: true, + }); + + const manager = service.createDeliverManager(localUser, { type: 'Test' } as any); + manager.addFollowersRecipe(); + + // Execute with ignoreSuspend: true + await manager.execute({ ignoreSuspend: true }); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + expect(inboxes.size).toBe(1); + expect(inboxes.has('https://suspended.example.com/inbox')).toBe(true); + }); + + it('should handle mixed follower types correctly', async () => { + const localUser = await createUser({ host: null }); + + // フォロワー1: shared inbox あり + const follower1 = await createUser({ + host: 'server1.example.com', + inbox: 'https://server1.example.com/users/user1/inbox', + sharedInbox: 'https://server1.example.com/inbox', + }); + + // フォロワー2: 同じサーバーの別ユーザー(shared inbox は同じ) + const follower2 = await createUser({ + host: 'server1.example.com', + inbox: 'https://server1.example.com/users/user2/inbox', + sharedInbox: 'https://server1.example.com/inbox', + }); + + // フォロワー3: 別サーバー、shared inbox なし + const follower3 = await createUser({ + host: 'server2.example.com', + inbox: 'https://server2.example.com/users/user3/inbox', + sharedInbox: null, + }); + + await createFollowing(follower1, localUser); + await createFollowing(follower2, localUser); + await createFollowing(follower3, localUser); + + await service.deliverToFollowers(localUser, { type: 'Test' } as any); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + + // shared inbox は重複排除されるので、2つのinboxのみ + expect(inboxes.size).toBe(2); + expect(inboxes.has('https://server1.example.com/inbox')).toBe(true); // shared inbox + expect(inboxes.has('https://server2.example.com/users/user3/inbox')).toBe(true); // individual inbox + + // individual inbox は shared inbox があるので使用されない + expect(inboxes.has('https://server1.example.com/users/user1/inbox')).toBe(false); + expect(inboxes.has('https://server1.example.com/users/user2/inbox')).toBe(false); + }); + }); + + describe('AllKnowingSharedInbox with real data', () => { + it('should collect all unique shared inboxes from database', async () => { + // Create users with various inbox configurations + const user1 = await createUser({ host: null }); + const user2 = await createUser({ host: null }); + + const remoteUser1 = await createUser({ + host: 'server1.example.com', + sharedInbox: 'https://server1.example.com/shared', + }); + + const remoteUser2 = await createUser({ + host: 'server2.example.com', + sharedInbox: 'https://server2.example.com/shared', + }); + + const remoteUser3 = await createUser({ + host: 'server1.example.com', // 同じサーバー + sharedInbox: 'https://server1.example.com/shared', // 同じ shared inbox + }); + + // Create following relationships + await createFollowing(remoteUser1, user1, { + followerSharedInbox: 'https://server1.example.com/shared', + }); + + await createFollowing(user1, remoteUser2, { + followerSharedInbox: null, + followeeSharedInbox: 'https://server2.example.com/shared', + }); + + await createFollowing(remoteUser3, user2, { + followerSharedInbox: 'https://server1.example.com/shared', // 重複 + }); + + const manager = service.createDeliverManager(user1, { type: 'Test' } as any); + manager.addAllKnowingSharedInboxRecipe(); + + await manager.execute(); + + const [, , inboxes] = queueService.deliverMany.mock.calls[0]; + + // 重複は除去されて2つのユニークな shared inbox + expect(inboxes.size).toBe(2); + expect(inboxes.has('https://server1.example.com/shared')).toBe(true); + expect(inboxes.has('https://server2.example.com/shared')).toBe(true); + }); + }); +}); + From e48590f53ebae746526dfb096cc6e51bdd3e430b Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 14:52:03 +0900 Subject: [PATCH 46/85] n --- .../backend/src/core/AccountUpdateService.ts | 33 ++++++++- .../backend/src/core/UserSuspendService.ts | 20 +---- .../backend/test/unit/UserSuspendService.ts | 73 ++++++++++++++----- 3 files changed, 87 insertions(+), 39 deletions(-) diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 69a57b4854..74f7f0fcab 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -6,7 +6,7 @@ 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'; +import type { MiLocalUser, MiUser } from '@/models/User.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { RelayService } from '@/core/RelayService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; @@ -26,16 +26,43 @@ export class AccountUpdateService { ) { } + private async createUpdatePersonActivity(user: MiLocalUser) { + return this.apRendererService.addContext( + this.apRendererService.renderUpdate( + await this.apRendererService.renderPerson(user), user + ) + ); + } + @bindThis public async publishToFollowers(userId: MiUser['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); - // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 + // 投稿者がローカルユーザーならUpdateを配信 if (this.userEntityService.isLocalUser(user)) { - const content = this.apRendererService.addContext(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user)); + const content = await this.createUpdatePersonActivity(user); this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } } + + @bindThis + async publishToFollowersAndSharedInboxAndRelays(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 = await this.createUpdatePersonActivity(user); + const manager = this.apDeliverManagerService.createDeliverManager(user, content); + manager.addAllKnowingSharedInboxRecipe(); + manager.addFollowersRecipe(); + + await Promise.allSettled([ + manager.execute(), + this.relayService.deliverToRelays(user, content), + ]); + } + } } diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 0b62cf2946..61f7731d1b 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -4,14 +4,12 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import { Not, IsNull } from 'typeorm'; import type { FollowingsRepository, FollowRequestsRepository, UsersRepository } from '@/models/_.js'; import type { MiUser } from '@/models/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; -import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { AccountUpdateService } from '@/core/AccountUpdateService.js'; @@ -29,9 +27,7 @@ export class UserSuspendService { private userEntityService: UserEntityService, private globalEventService: GlobalEventService, - private accountUpadateService: AccountUpdateService, - private apRendererService: ApRendererService, - private apDeliverManagerService: ApDeliverManagerService, + private accountUpdateService: AccountUpdateService, private moderationLogService: ModerationLogService, ) { } @@ -84,12 +80,7 @@ export class UserSuspendService { }); if (this.userEntityService.isLocalUser(user)) { - this.accountUpadateService.publishToFollowers(user.id); - const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user)); - const manager = this.apDeliverManagerService.createDeliverManager(user, content); - manager.addAllKnowingSharedInboxRecipe(); - manager.addFollowersRecipe(); - manager.execute(); + this.accountUpdateService.publishToFollowersAndSharedInboxAndRelays(user.id); } } @@ -98,12 +89,7 @@ export class UserSuspendService { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); if (this.userEntityService.isLocalUser(user)) { - this.accountUpadateService.publishToFollowers(user.id); - 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(); - manager.addFollowersRecipe(); - manager.execute(); + this.accountUpdateService.publishToFollowersAndSharedInboxAndRelays(user.id); } } diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index 8d6b01b730..974b2c1903 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -26,6 +26,10 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { randomString } from '../utils.js'; +import { AccountUpdateService } from '@/core/AccountUpdateService.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { RelayService } from '@/core/RelayService.js'; +import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js'; function genHost() { return randomString() + '.example.com'; @@ -42,6 +46,9 @@ describe('UserSuspendService', () => { let globalEventService: jest.Mocked; let apRendererService: jest.Mocked; let moderationLogService: jest.Mocked; + let accountUpdateService: jest.Mocked; + let apDeliverManagerService: jest.Mocked; + let relayService: jest.Mocked; async function createUser(data: Partial = {}): Promise { const user = { @@ -84,6 +91,8 @@ describe('UserSuspendService', () => { imports: [GlobalModule], providers: [ UserSuspendService, + AccountUpdateService, + ApDeliverManagerService, { provide: UserEntityService, useFactory: () => ({ @@ -94,6 +103,7 @@ describe('UserSuspendService', () => { { provide: QueueService, useFactory: () => ({ + deliverMany: jest.fn(), deliver: jest.fn(), }), }, @@ -103,20 +113,38 @@ describe('UserSuspendService', () => { publishInternalEvent: jest.fn(), }), }, - { - provide: ApRendererService, - useFactory: () => ({ - addContext: jest.fn(), - renderDelete: jest.fn(), - renderUndo: jest.fn(), - }), - }, { provide: ModerationLogService, useFactory: () => ({ log: jest.fn(), }), }, + { + provide: RelayService, + useFactory: () => ({ + deliverToRelays: jest.fn(), + }), + }, + { + provide: ApRendererService, + useFactory: () => ({ + renderDelete: jest.fn(), + renderUndo: jest.fn(), + renderPerson: jest.fn(), + renderUpdate: jest.fn(), + addContext: jest.fn(), + }), + }, + { + provide: ApLoggerService, + useFactory: () => ({ + logger: { + createSubLogger: jest.fn().mockReturnValue({ + info: jest.fn(), error: jest.fn(), warn: jest.fn(), debug: jest.fn(), + }), + }, + }), + }, ], }).compile(); @@ -131,6 +159,9 @@ describe('UserSuspendService', () => { globalEventService = app.get(GlobalEventService) as jest.Mocked; apRendererService = app.get(ApRendererService) as jest.Mocked; moderationLogService = app.get(ModerationLogService) as jest.Mocked; + accountUpdateService = app.get(AccountUpdateService) as jest.Mocked; + apDeliverManagerService = app.get(ApDeliverManagerService) as jest.Mocked; + relayService = app.get(RelayService) as jest.Mocked; // Reset mocks jest.clearAllMocks(); @@ -311,42 +342,46 @@ describe('UserSuspendService', () => { }); describe('ActivityPub delivery', () => { - test('should deliver Delete activity on suspend of local user', async () => { + test('should deliver Update Person activity on suspend of local user', async () => { const localUser = await createUser({ host: null }); const moderator = await createUser(); userEntityService.isLocalUser.mockReturnValue(true); userEntityService.genLocalUserUri.mockReturnValue(`https://example.com/users/${localUser.id}`); - apRendererService.renderDelete.mockReturnValue({ type: 'Delete' } as any); - apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Delete' } as any); + apRendererService.renderUpdate.mockReturnValue({ type: 'Update' } as any); + apRendererService.renderPerson.mockReturnValue({ type: 'Person' } as any); + apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Update' } as any); await userSuspendService.suspend(localUser, moderator); await setTimeout(250); // ActivityPub配信が呼ばれているかチェック expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser); - expect(apRendererService.renderDelete).toHaveBeenCalled(); + expect(apRendererService.renderUpdate).toHaveBeenCalled(); + expect(apRendererService.renderPerson).toHaveBeenCalled(); expect(apRendererService.addContext).toHaveBeenCalled(); + expect(queueService.deliverMany).toHaveBeenCalled(); }); - test('should deliver Undo Delete activity on unsuspend of local user', async () => { + test('should deliver Update Person activity on unsuspend of local user', async () => { const localUser = await createUser({ host: null, isSuspended: true }); const moderator = await createUser(); userEntityService.isLocalUser.mockReturnValue(true); userEntityService.genLocalUserUri.mockReturnValue(`https://example.com/users/${localUser.id}`); - apRendererService.renderDelete.mockReturnValue({ type: 'Delete' } as any); - apRendererService.renderUndo.mockReturnValue({ type: 'Undo' } as any); - apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Undo' } as any); + apRendererService.renderUpdate.mockReturnValue({ type: 'Update' } as any); + apRendererService.renderPerson.mockReturnValue({ type: 'Person' } as any); + apRendererService.addContext.mockReturnValue({ '@context': '...', type: 'Update' } as any); - await userSuspendService.unsuspend(localUser, moderator); + await userSuspendService.suspend(localUser, moderator); await setTimeout(250); // ActivityPub配信が呼ばれているかチェック expect(userEntityService.isLocalUser).toHaveBeenCalledWith(localUser); - expect(apRendererService.renderDelete).toHaveBeenCalled(); - expect(apRendererService.renderUndo).toHaveBeenCalled(); + expect(apRendererService.renderUpdate).toHaveBeenCalled(); + expect(apRendererService.renderPerson).toHaveBeenCalled(); expect(apRendererService.addContext).toHaveBeenCalled(); + expect(queueService.deliverMany).toHaveBeenCalled(); }); test('should not deliver any activity on suspend of remote user', async () => { From bbdfb421f51c2cd9beadced2d5605935fc4714c4 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 14:55:52 +0900 Subject: [PATCH 47/85] fix unit test --- .../backend/test/unit/UserSuspendService.ts | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index 8d6b01b730..ce819c1cb8 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -26,6 +26,9 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { randomString } from '../utils.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { RelayService } from '@/core/RelayService.js'; +import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js'; function genHost() { return randomString() + '.example.com'; @@ -42,6 +45,8 @@ describe('UserSuspendService', () => { let globalEventService: jest.Mocked; let apRendererService: jest.Mocked; let moderationLogService: jest.Mocked; + let apDeliverManagerService: jest.Mocked; + let relayService: jest.Mocked; async function createUser(data: Partial = {}): Promise { const user = { @@ -84,6 +89,7 @@ describe('UserSuspendService', () => { imports: [GlobalModule], providers: [ UserSuspendService, + ApDeliverManagerService, { provide: UserEntityService, useFactory: () => ({ @@ -94,6 +100,7 @@ describe('UserSuspendService', () => { { provide: QueueService, useFactory: () => ({ + deliverMany: jest.fn(), deliver: jest.fn(), }), }, @@ -103,20 +110,38 @@ describe('UserSuspendService', () => { publishInternalEvent: jest.fn(), }), }, - { - provide: ApRendererService, - useFactory: () => ({ - addContext: jest.fn(), - renderDelete: jest.fn(), - renderUndo: jest.fn(), - }), - }, { provide: ModerationLogService, useFactory: () => ({ log: jest.fn(), }), }, + { + provide: RelayService, + useFactory: () => ({ + deliverToRelays: jest.fn(), + }), + }, + { + provide: ApRendererService, + useFactory: () => ({ + renderDelete: jest.fn(), + renderUndo: jest.fn(), + renderPerson: jest.fn(), + renderUpdate: jest.fn(), + addContext: jest.fn(), + }), + }, + { + provide: ApLoggerService, + useFactory: () => ({ + logger: { + createSubLogger: jest.fn().mockReturnValue({ + info: jest.fn(), error: jest.fn(), warn: jest.fn(), debug: jest.fn(), + }), + }, + }), + }, ], }).compile(); @@ -131,6 +156,8 @@ describe('UserSuspendService', () => { globalEventService = app.get(GlobalEventService) as jest.Mocked; apRendererService = app.get(ApRendererService) as jest.Mocked; moderationLogService = app.get(ModerationLogService) as jest.Mocked; + apDeliverManagerService = app.get(ApDeliverManagerService) as jest.Mocked; + relayService = app.get(RelayService) as jest.Mocked; // Reset mocks jest.clearAllMocks(); From 026719733996d4fccc930dafcaf308a2d362874c Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 15:08:47 +0900 Subject: [PATCH 48/85] fix migration file --- .../backend/migration/1751848750315-RemoteSuspend.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/backend/migration/1751848750315-RemoteSuspend.js b/packages/backend/migration/1751848750315-RemoteSuspend.js index d49e14dc21..8edc0d88e6 100644 --- a/packages/backend/migration/1751848750315-RemoteSuspend.js +++ b/packages/backend/migration/1751848750315-RemoteSuspend.js @@ -2,16 +2,7 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ - -/** - * @typedef {import('typeorm').MigrationInterface} MigrationInterface - */ - -/** - * @class - * @implements {MigrationInterface} - */ -module.exports = class RemoteSuspend1751848750315 { +export class RemoteSuspend1751848750315 { name = 'RemoteSuspend1751848750315' async up(queryRunner) { From ab61baa3ea7e773b7bc730e4b9e53f4548cbe93a Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 15:44:37 +0900 Subject: [PATCH 49/85] wip --- .../backend/src/core/GlobalEventService.ts | 2 +- .../backend/src/core/UserSuspendService.ts | 57 ++- .../activitypub/models/ApPersonService.ts | 14 + .../test/user-suspension.test.ts | 445 ------------------ .../backend/test/unit/UserSuspendService.ts | 36 +- 5 files changed, 94 insertions(+), 460 deletions(-) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 3215b41c8d..d82a99fbf4 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -225,7 +225,7 @@ type UndefinedAsNullAll = { }; export interface InternalEventTypes { - userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; }; + userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; } | { id: MiUser['id']; isRemoteSuspended: MiUser['isRemoteSuspended']; }; userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; }; userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; remoteUserUpdated: { id: MiUser['id']; }; diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 61f7731d1b..e425b01c9f 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { FollowingsRepository, FollowRequestsRepository, UsersRepository } from '@/models/_.js'; -import type { MiUser } from '@/models/User.js'; +import type { MiRemoteUser, MiUser } from '@/models/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; @@ -45,11 +45,20 @@ export class UserSuspendService { }); (async () => { - await this.postSuspend(user).catch((e: any) => {}); - await this.suspendFollowings(user).catch((e: any) => {}); + await this.postSuspend(user, false).catch((e: any) => { }); + await this.suspendFollowings(user).catch((e: any) => { }); })(); } + @bindThis + public async suspendFromRemote(user: { id: MiRemoteUser['id']; host: MiRemoteUser['host'] }): Promise { + await this.usersRepository.update(user.id, { + isRemoteSuspended: true, + }); + + this.postSuspend(user, true); + } + @bindThis public async unsuspend(user: MiUser, moderator: MiUser): Promise { await this.usersRepository.update(user.id, { @@ -63,14 +72,26 @@ export class UserSuspendService { }); (async () => { - await this.postUnsuspend(user).catch((e: any) => {}); - await this.restoreFollowings(user).catch((e: any) => {}); + await this.postUnsuspend(user, false).catch((e: any) => { }); + await this.restoreFollowings(user).catch((e: any) => { }); })(); } @bindThis - private async postSuspend(user: { id: MiUser['id']; host: MiUser['host'] }): Promise { - this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); + public async unsuspendFromRemote(user: { id: MiRemoteUser['id']; host: MiRemoteUser['host'] }): Promise { + await this.usersRepository.update(user.id, { + isRemoteSuspended: false, + }); + + this.postUnsuspend(user, true); + } + + @bindThis + private async postSuspend(user: { id: MiUser['id']; host: MiUser['host'] }, isFromRemote: boolean): Promise { + this.globalEventService.publishInternalEvent( + 'userChangeSuspendedState', + isFromRemote ? { id: user.id, isRemoteSuspended: true } : { id: user.id, isSuspended: true } + ); this.followRequestsRepository.delete({ followeeId: user.id, @@ -85,8 +106,11 @@ export class UserSuspendService { } @bindThis - private async postUnsuspend(user: MiUser): Promise { - this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); + private async postUnsuspend(user: { id: MiUser['id']; host: MiUser['host'] }, isFromRemote: boolean): Promise { + this.globalEventService.publishInternalEvent( + 'userChangeSuspendedState', + isFromRemote ? { id: user.id, isRemoteSuspended: false } : { id: user.id, isSuspended: false } + ); if (this.userEntityService.isLocalUser(user)) { this.accountUpdateService.publishToFollowersAndSharedInboxAndRelays(user.id); @@ -94,7 +118,7 @@ export class UserSuspendService { } @bindThis - private async suspendFollowings(follower: MiUser) { + private async suspendFollowings(follower: { id: MiUser['id'] }) { await this.followingsRepository.update( { followerId: follower.id, @@ -106,7 +130,18 @@ export class UserSuspendService { } @bindThis - private async restoreFollowings(follower: MiUser) { + private async restoreFollowings(_follower: { id: MiUser['id'] }) { + // 最新の情報を取得 + const follower = await this.usersRepository.findOneBy({ id: _follower.id }); + if (follower == null) { + // ユーザーが削除されている場合は何もしないでおく + return; + } + if (follower.isSuspended || follower.isRemoteSuspended) { + // フォロー関係を復元しない + return; + } + // フォロー関係を復元(isFollowerSuspended: false)に変更 await this.followingsRepository.update( { diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 4673875e1f..4e548347f0 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -48,6 +48,7 @@ import type { ApLoggerService } from '../ApLoggerService.js'; import type { ApImageService } from './ApImageService.js'; import type { IActor, ICollection, IObject, IOrderedCollection } from '../type.js'; +import { UserSuspendService } from '@/core/UserSuspendService.js'; const nameLength = 128; const summaryLength = 2048; @@ -74,6 +75,7 @@ export class ApPersonService implements OnModuleInit { private instanceChart: InstanceChart; private apLoggerService: ApLoggerService; private accountMoveService: AccountMoveService; + private userSuspendService: UserSuspendService; private logger: Logger; constructor( @@ -126,6 +128,7 @@ export class ApPersonService implements OnModuleInit { this.instanceChart = this.moduleRef.get('InstanceChart'); this.apLoggerService = this.moduleRef.get('ApLoggerService'); this.accountMoveService = this.moduleRef.get('AccountMoveService'); + this.userSuspendService = this.moduleRef.get('UserSuspendService'); this.logger = this.apLoggerService.logger; } @@ -600,6 +603,17 @@ export class ApPersonService implements OnModuleInit { return 'skip'; } + //#region suspend + if (exist.isRemoteSuspended === false && person.suspended === true) { + // リモートサーバーでアカウントが凍結された + this.userSuspendService.suspendFromRemote({ id: exist.id, host: exist.host }); + } + if (exist.isRemoteSuspended === true && person.suspended === false) { + // リモートサーバーでアカウントが解凍された + this.userSuspendService.unsuspendFromRemote({ id: exist.id, host: exist.host }); + } + //#endregion + if (person.publicKey) { await this.userPublickeysRepository.update({ userId: exist.id }, { keyId: person.publicKey.id, diff --git a/packages/backend/test-federation/test/user-suspension.test.ts b/packages/backend/test-federation/test/user-suspension.test.ts index ae7f88a6a7..e5d119d564 100644 --- a/packages/backend/test-federation/test/user-suspension.test.ts +++ b/packages/backend/test-federation/test/user-suspension.test.ts @@ -113,449 +113,4 @@ describe('User Suspension', () => { }); }); }); - - describe('Profile', () => { - describe('Consistency of profile', () => { - let alice: LoginUser; - let aliceWatcher: LoginUser; - let aliceWatcherInB: LoginUser; - - beforeAll(async () => { - alice = await createAccount('a.test'); - [ - aliceWatcher, - aliceWatcherInB, - ] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - }); - - test('Check consistency', async () => { - const aliceInA = await aliceWatcher.client.request('users/show', { userId: alice.id }); - const resolved = await resolveRemoteUser('a.test', aliceInA.id, aliceWatcherInB); - const aliceInB = await aliceWatcherInB.client.request('users/show', { userId: resolved.id }); - - // console.log(`a.test: ${JSON.stringify(aliceInA, null, '\t')}`); - // console.log(`b.test: ${JSON.stringify(aliceInB, null, '\t')}`); - - deepStrictEqualWithExcludedFields(aliceInA, aliceInB, [ - 'id', - 'host', - 'avatarUrl', - 'avatarBlurhash', - 'instance', - 'badgeRoles', - 'url', - 'uri', - 'createdAt', - 'lastFetchedAt', - 'publicReactions', - ]); - }); - }); - - describe('ffVisibility is federated', () => { - let alice: LoginUser, bob: LoginUser; - let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInA, aliceInB] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - - // NOTE: follow each other - await Promise.all([ - alice.client.request('following/create', { userId: bobInA.id }), - bob.client.request('following/create', { userId: aliceInB.id }), - ]); - await sleep(); - }); - - test('Visibility set public by default', async () => { - for (const user of await Promise.all([ - alice.client.request('users/show', { userId: bobInA.id }), - bob.client.request('users/show', { userId: aliceInB.id }), - ])) { - strictEqual(user.followersVisibility, 'public'); - strictEqual(user.followingVisibility, 'public'); - } - }); - - /** FIXME: not working */ - test.skip('Setting private for followersVisibility is federated', async () => { - await Promise.all([ - alice.client.request('i/update', { followersVisibility: 'private' }), - bob.client.request('i/update', { followersVisibility: 'private' }), - ]); - await sleep(); - - for (const user of await Promise.all([ - alice.client.request('users/show', { userId: bobInA.id }), - bob.client.request('users/show', { userId: aliceInB.id }), - ])) { - strictEqual(user.followersVisibility, 'private'); - strictEqual(user.followingVisibility, 'public'); - } - }); - - test.skip('Setting private for followingVisibility is federated', async () => { - await Promise.all([ - alice.client.request('i/update', { followingVisibility: 'private' }), - bob.client.request('i/update', { followingVisibility: 'private' }), - ]); - await sleep(); - - for (const user of await Promise.all([ - alice.client.request('users/show', { userId: bobInA.id }), - bob.client.request('users/show', { userId: aliceInB.id }), - ])) { - strictEqual(user.followersVisibility, 'private'); - strictEqual(user.followingVisibility, 'private'); - } - }); - }); - - describe('isCat is federated', () => { - let alice: LoginUser, bob: LoginUser; - let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInA, aliceInB] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - }); - - test('Not isCat for default', () => { - strictEqual(aliceInB.isCat, false); - }); - - test('Becoming a cat is sent to their followers', async () => { - await bob.client.request('following/create', { userId: aliceInB.id }); - await sleep(); - - await alice.client.request('i/update', { isCat: true }); - await sleep(); - - const res = await bob.client.request('users/show', { userId: aliceInB.id }); - strictEqual(res.isCat, true); - }); - }); - - describe('Pinning Notes', () => { - let alice: LoginUser, bob: LoginUser; - let aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - aliceInB = await resolveRemoteUser('a.test', alice.id, bob); - - await bob.client.request('following/create', { userId: aliceInB.id }); - }); - - test('Pinning localOnly Note is not delivered', async () => { - const note = (await alice.client.request('notes/create', { text: 'a', localOnly: true })).createdNote; - await alice.client.request('i/pin', { noteId: note.id }); - await sleep(); - - const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); - strictEqual(_aliceInB.pinnedNoteIds.length, 0); - }); - - test('Pinning followers-only Note is not delivered', async () => { - const note = (await alice.client.request('notes/create', { text: 'a', visibility: 'followers' })).createdNote; - await alice.client.request('i/pin', { noteId: note.id }); - await sleep(); - - const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); - strictEqual(_aliceInB.pinnedNoteIds.length, 0); - }); - - let pinnedNote: Misskey.entities.Note; - - test('Pinning normal Note is delivered', async () => { - pinnedNote = (await alice.client.request('notes/create', { text: 'a' })).createdNote; - await alice.client.request('i/pin', { noteId: pinnedNote.id }); - await sleep(); - - const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); - strictEqual(_aliceInB.pinnedNoteIds.length, 1); - const pinnedNoteInB = await resolveRemoteNote('a.test', pinnedNote.id, bob); - strictEqual(_aliceInB.pinnedNotes[0].id, pinnedNoteInB.id); - }); - - test('Unpinning normal Note is delivered', async () => { - await alice.client.request('i/unpin', { noteId: pinnedNote.id }); - await sleep(); - - const _aliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); - strictEqual(_aliceInB.pinnedNoteIds.length, 0); - }); - }); - }); - - describe('Follow / Unfollow', () => { - let alice: LoginUser, bob: LoginUser; - let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInA, aliceInB] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - }); - - describe('Follow a.test ==> b.test', () => { - beforeAll(async () => { - await alice.client.request('following/create', { userId: bobInA.id }); - - await sleep(); - }); - - test('Check consistency with `users/following` and `users/followers` endpoints', async () => { - await Promise.all([ - strictEqual( - (await alice.client.request('users/following', { userId: alice.id })) - .some(v => v.followeeId === bobInA.id), - true, - ), - strictEqual( - (await bob.client.request('users/followers', { userId: bob.id })) - .some(v => v.followerId === aliceInB.id), - true, - ), - ]); - }); - }); - - describe('Unfollow a.test ==> b.test', () => { - beforeAll(async () => { - await alice.client.request('following/delete', { userId: bobInA.id }); - - await sleep(); - }); - - test('Check consistency with `users/following` and `users/followers` endpoints', async () => { - await Promise.all([ - strictEqual( - (await alice.client.request('users/following', { userId: alice.id })) - .some(v => v.followeeId === bobInA.id), - false, - ), - strictEqual( - (await bob.client.request('users/followers', { userId: bob.id })) - .some(v => v.followerId === aliceInB.id), - false, - ), - ]); - }); - }); - }); - - describe('Follow requests', () => { - let alice: LoginUser, bob: LoginUser; - let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInA, aliceInB] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - - await alice.client.request('i/update', { isLocked: true }); - }); - - describe('Send follow request from Bob to Alice and cancel', () => { - describe('Bob sends follow request to Alice', () => { - beforeAll(async () => { - await bob.client.request('following/create', { userId: aliceInB.id }); - await sleep(); - }); - - test('Alice should have a request', async () => { - const requests = await alice.client.request('following/requests/list', {}); - strictEqual(requests.length, 1); - strictEqual(requests[0].followee.id, alice.id); - strictEqual(requests[0].follower.id, bobInA.id); - }); - }); - - describe('Alice cancels it', () => { - beforeAll(async () => { - await bob.client.request('following/requests/cancel', { userId: aliceInB.id }); - await sleep(); - }); - - test('Alice should have no requests', async () => { - const requests = await alice.client.request('following/requests/list', {}); - strictEqual(requests.length, 0); - }); - }); - }); - - describe('Send follow request from Bob to Alice and reject', () => { - beforeAll(async () => { - await bob.client.request('following/create', { userId: aliceInB.id }); - await sleep(); - - await alice.client.request('following/requests/reject', { userId: bobInA.id }); - await sleep(); - }); - - test('Bob should have no requests', async () => { - await rejects( - async () => await bob.client.request('following/requests/cancel', { userId: aliceInB.id }), - (err: any) => { - strictEqual(err.code, 'FOLLOW_REQUEST_NOT_FOUND'); - return true; - }, - ); - }); - - test('Bob doesn\'t follow Alice', async () => { - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 0); - }); - }); - - describe('Send follow request from Bob to Alice and accept', () => { - beforeAll(async () => { - await bob.client.request('following/create', { userId: aliceInB.id }); - await sleep(); - - await alice.client.request('following/requests/accept', { userId: bobInA.id }); - await sleep(); - }); - - test('Bob follows Alice', async () => { - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 1); - strictEqual(following[0].followeeId, aliceInB.id); - strictEqual(following[0].followerId, bob.id); - }); - }); - }); - - describe('Deletion', () => { - describe('Check Delete consistency', () => { - let alice: LoginUser, bob: LoginUser; - let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInA, aliceInB] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - }); - - test('Bob follows Alice, and Alice deleted themself', async () => { - await bob.client.request('following/create', { userId: aliceInB.id }); - await sleep(); - - const followers = await alice.client.request('users/followers', { userId: alice.id }); - strictEqual(followers.length, 1); // followed by Bob - - await alice.client.request('i/delete-account', { password: alice.password }); - await sleep(); - - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 0); // no following relation - - await rejects( - async () => await bob.client.request('following/create', { userId: aliceInB.id }), - (err: any) => { - strictEqual(err.code, 'NO_SUCH_USER'); - return true; - }, - ); - }); - }); - - describe('Deletion of remote user for moderation', () => { - let alice: LoginUser, bob: LoginUser; - let bobInA: Misskey.entities.UserDetailedNotMe, aliceInB: Misskey.entities.UserDetailedNotMe; - - beforeAll(async () => { - [alice, bob] = await Promise.all([ - createAccount('a.test'), - createAccount('b.test'), - ]); - - [bobInA, aliceInB] = await Promise.all([ - resolveRemoteUser('b.test', bob.id, alice), - resolveRemoteUser('a.test', alice.id, bob), - ]); - }); - - test('Bob follows Alice, then Alice gets deleted in B server', async () => { - await bob.client.request('following/create', { userId: aliceInB.id }); - await sleep(); - - const followers = await alice.client.request('users/followers', { userId: alice.id }); - strictEqual(followers.length, 1); // followed by Bob - - await bAdmin.client.request('admin/delete-account', { userId: aliceInB.id }); - await sleep(); - - /** - * FIXME: remote account is not deleted! - * @see https://github.com/misskey-dev/misskey/issues/14728 - */ - const deletedAlice = await bob.client.request('users/show', { userId: aliceInB.id }); - assert(deletedAlice.id, aliceInB.id); - - // TODO: why still following relation? - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 1); - await rejects( - async () => await bob.client.request('following/create', { userId: aliceInB.id }), - (err: any) => { - strictEqual(err.code, 'ALREADY_FOLLOWING'); - return true; - }, - ); - }); - - test('Alice tries to follow Bob, but it is not processed', async () => { - await alice.client.request('following/create', { userId: bobInA.id }); - await sleep(); - - const following = await alice.client.request('users/following', { userId: alice.id }); - strictEqual(following.length, 0); // Not following Bob because B server doesn't return Accept - - const followers = await bob.client.request('users/followers', { userId: bob.id }); - strictEqual(followers.length, 0); // Alice's Follow is not processed - }); - }); - }); }); diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index 974b2c1903..b80e79b64d 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -400,7 +400,7 @@ describe('UserSuspendService', () => { }); }); - describe('remote user suspension', () => { + describe('suspension for remote user', () => { test('should suspend remote user without AP delivery', async () => { const remoteUser = await createUser({ host: genHost() }); const moderator = await createUser(); @@ -422,9 +422,7 @@ describe('UserSuspendService', () => { // ActivityPub配信が呼ばれていないことを確認 expect(queueService.deliver).not.toHaveBeenCalled(); }); - }); - describe('remote user unsuspension', () => { test('should unsuspend remote user without AP delivery', async () => { const remoteUser = await createUser({ host: genHost(), isSuspended: true }); const moderator = await createUser(); @@ -448,4 +446,36 @@ describe('UserSuspendService', () => { expect(queueService.deliver).not.toHaveBeenCalled(); }); }); + + describe('suspension from remote', () => { + test('should suspend remote user and post suspend event', async () => { + const remoteUser = { id: secureRndstr(16), host: genHost() }; + await userSuspendService.suspendFromRemote(remoteUser); + + // ユーザーがリモート凍結されているかチェック + const suspendedUser = await usersRepository.findOneBy({ id: remoteUser.id }); + expect(suspendedUser?.isRemoteSuspended).toBe(true); + + // イベントが発行されているかチェック + expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith( + 'userChangeSuspendedState', + { id: remoteUser.id, isRemoteSuspended: true }, + ); + }); + + test('should unsuspend remote user and post unsuspend event', async () => { + const remoteUser = { id: secureRndstr(16), host: genHost() }; + await userSuspendService.unsuspendFromRemote(remoteUser); + + // ユーザーのリモート凍結が解除されているかチェック + const unsuspendedUser = await usersRepository.findOneBy({ id: remoteUser.id }); + expect(unsuspendedUser?.isRemoteSuspended).toBe(false); + + // イベントが発行されているかチェック + expect(globalEventService.publishInternalEvent).toHaveBeenCalledWith( + 'userChangeSuspendedState', + { id: remoteUser.id, isRemoteSuspended: false }, + ); + }); + }); }); From 1ae7150157507de68e6ae1240f3ec9ace281b2e8 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 16:02:17 +0900 Subject: [PATCH 50/85] isSuspendedEither --- packages/backend/src/core/UserSuspendService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index e425b01c9f..14bbb10919 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -137,7 +137,7 @@ export class UserSuspendService { // ユーザーが削除されている場合は何もしないでおく return; } - if (follower.isSuspended || follower.isRemoteSuspended) { + if (this.userEntityService.isSuspendedEither(follower)) { // フォロー関係を復元しない return; } From 96125673a62aa35fc63a5a04db47018cd5341d74 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 16:05:00 +0900 Subject: [PATCH 51/85] fix migration --- packages/backend/migration/1751848750315-RemoteSuspend.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/migration/1751848750315-RemoteSuspend.js b/packages/backend/migration/1751848750315-RemoteSuspend.js index 8edc0d88e6..efa7b83026 100644 --- a/packages/backend/migration/1751848750315-RemoteSuspend.js +++ b/packages/backend/migration/1751848750315-RemoteSuspend.js @@ -7,11 +7,13 @@ export class RemoteSuspend1751848750315 { async up(queryRunner) { await queryRunner.query(`ALTER TABLE "user" ADD "isRemoteSuspended" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isSuspended" IS 'Whether the User is suspended by the local moderators.'`); await queryRunner.query(`COMMENT ON COLUMN "user"."isRemoteSuspended" IS 'Whether the User is suspended by the remote moderators.'`); } async down(queryRunner) { await queryRunner.query(`COMMENT ON COLUMN "user"."isRemoteSuspended" IS 'Whether the User is suspended by the remote moderators.'`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isSuspended" IS 'Whether the User is suspended.'`); await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isRemoteSuspended"`); } } From dd09ce2eb2bd56bfb1d8c63889672731ce51f9bf Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 17 Jul 2025 16:10:03 +0900 Subject: [PATCH 52/85] fix unit tests? --- packages/backend/test/unit/UserSuspendService.ts | 1 + packages/backend/test/unit/entities/UserEntityService.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index b80e79b64d..184aef8895 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -57,6 +57,7 @@ describe('UserSuspendService', () => { usernameLower: secureRndstr(16).toLowerCase(), host: null, isSuspended: false, + isRemoteSuspended: false, ...data, } as MiUser; diff --git a/packages/backend/test/unit/entities/UserEntityService.ts b/packages/backend/test/unit/entities/UserEntityService.ts index ca6a639be8..f2b40e83a8 100644 --- a/packages/backend/test/unit/entities/UserEntityService.ts +++ b/packages/backend/test/unit/entities/UserEntityService.ts @@ -51,6 +51,7 @@ import { ReactionService } from '@/core/ReactionService.js'; import { NotificationService } from '@/core/NotificationService.js'; import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js'; import { ChatService } from '@/core/ChatService.js'; +import { UserSuspendService } from '@/core/UserSuspendService.js'; process.env.NODE_ENV = 'test'; @@ -170,6 +171,7 @@ describe('UserEntityService', () => { InstanceChart, ApLoggerService, AccountMoveService, + UserSuspendService, ReactionService, ReactionsBufferingService, NotificationService, From fbd6b67f1ff22d9c4e9ed29edbd17b62353383f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 17 Jul 2025 08:33:52 +0000 Subject: [PATCH 53/85] Bump version to 2025.7.0-rc.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 8cbc5a6396..bd26f773cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.7.0-beta.2", + "version": "2025.7.0-rc.0", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 4347e8e00d..28e8ee8886 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.7.0-beta.2", + "version": "2025.7.0-rc.0", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 0619dba04d7a4c6995e267d16113fee1b1f4abd6 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 17 Jul 2025 20:00:05 +0900 Subject: [PATCH 54/85] remove unused code --- packages/frontend/src/pages/my-lists/list.vue | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 74ac47c571..6b5a797023 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -73,10 +73,6 @@ import { Paginator } from '@/utility/paginator.js'; const $i = ensureSignin(); -const { - enableInfiniteScroll, -} = prefer.r; - const props = defineProps<{ listId: string; }>(); From a6865927344ea0b2b97770c937fc3b79efa54f18 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 17 Jul 2025 20:02:55 +0900 Subject: [PATCH 55/85] enhance(frontend): disable InfiniteScroll to improve stability #16229 --- CHANGELOG.md | 1 + packages/frontend/src/components/MkStreamingNotesTimeline.vue | 2 +- .../src/components/MkStreamingNotificationsTimeline.vue | 2 +- packages/frontend/src/pages/settings/preferences.vue | 2 ++ 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 112b73f331..2a31031830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Enhance: ウォーターマーク機能をロールで制御可能に ### Client +- Note: 「自動でもっと見る」オプションは無効になっています - Feat: モデログを検索できるように - Enhance: 設定の自動バックアップをオンにした直後に自動バックアップするように - Enhance: ファイルアップロード前にキャプション設定を行えるように diff --git a/packages/frontend/src/components/MkStreamingNotesTimeline.vue b/packages/frontend/src/components/MkStreamingNotesTimeline.vue index 693f551ffc..3e50bdefd2 100644 --- a/packages/frontend/src/components/MkStreamingNotesTimeline.vue +++ b/packages/frontend/src/components/MkStreamingNotesTimeline.vue @@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only - diff --git a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue index 0276b7eaee..4617e659c8 100644 --- a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue +++ b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index ef7b73c2b1..0e400778aa 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -110,6 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only + From 4d643c77c54fb67abe691d96f4ab8740f996d747 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 17 Jul 2025 20:03:13 +0900 Subject: [PATCH 56/85] New Crowdin updates (#16274) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (Korean) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Thai) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (Spanish) * New translations ja-jp.yml (English) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Portuguese) * New translations ja-jp.yml (English) --- locales/ca-ES.yml | 1 + locales/de-DE.yml | 27 +++++++++++ locales/en-US.yml | 3 +- locales/es-ES.yml | 117 +++++++++++++++++++++++----------------------- locales/ko-KR.yml | 1 + locales/pt-PT.yml | 15 +++++- locales/th-TH.yml | 1 + locales/zh-CN.yml | 1 + locales/zh-TW.yml | 1 + 9 files changed, 106 insertions(+), 61 deletions(-) diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index a3497bf4b2..6e5882f087 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "Especifica el tipus MIME. Es poden especificar diferents tipus MIME separats amb una nova línia, i es poden especificar comodins amb asteriscs (*). (Per exemple: image/*)" uploadableFileTypes_caption2: "Pot que no sigui possible determinar el tipus MIME d'alguns arxius. Per permetre aquests tipus d'arxius afegeix {x} a les especificacions." noteDraftLimit: "Nombre possible d'esborranys de notes al servidor" + watermarkAvailable: "Pots fer servir la marca d'aigua" _condition: roleAssignedTo: "Assignat a rols manuals" isLocal: "Usuari local" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 5f4c4d05a9..202e512e63 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1313,6 +1313,7 @@ availableRoles: "Verfügbare Rollen" acknowledgeNotesAndEnable: "Schalten Sie dies erst ein, wenn Sie die Vorsichtsmaßnahmen verstanden haben." federationSpecified: "Dieser Server arbeitet mit Whitelist-Föderation. Er kann nicht mit anderen als den vom Administrator angegebenen Servern interagieren." federationDisabled: "Föderation ist auf diesem Server deaktiviert. Es ist nicht möglich, mit Benutzern auf anderen Servern zu interagieren." +draft: "Entwurf" confirmOnReact: "Reagieren bestätigen" reactAreYouSure: "Willst du eine \"{emoji}\"-Reaktion hinzufügen?" markAsSensitiveConfirm: "Möchtest du dieses Medium als sensibel kennzeichnen?" @@ -1367,6 +1368,9 @@ redisplayAllTips: "Alle „Tipps und Tricks“ wieder anzeigen" hideAllTips: "Alle „Tipps und Tricks“ ausblenden" defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe" defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße.
Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität." +_order: + newest: "Neueste zuerst" + oldest: "Älteste zuerst" _chat: noMessagesYet: "Noch keine Nachrichten" newMessage: "Neue Nachricht" @@ -1993,6 +1997,8 @@ _role: uploadableFileTypes: "Hochladbare Dateitypen" uploadableFileTypes_caption: "Gibt die zulässigen MIME-/Dateitypen an. Mehrere MIME-Typen können durch einen Zeilenumbruch getrennt angegeben werden, und Platzhalter können mit einem Sternchen (*) angegeben werden. (z. B. image/*)" uploadableFileTypes_caption2: "Bei manchen Dateien ist es nicht möglich, den Typ zu bestimmen. Um solche Dateien zuzulassen, füge {x} der Spezifikation hinzu." + noteDraftLimit: "Anzahl der möglichen Entwürfe für serverseitige Notizen" + watermarkAvailable: "Kann die Wasserzeichenfunktion verwenden" _condition: roleAssignedTo: "Manuellen Rollen zugewiesen" isLocal: "Lokaler Benutzer" @@ -2152,6 +2158,7 @@ _theme: install: "Farbschemata installieren" manage: "Farbschemaverwaltung" code: "Farbschemencode" + copyThemeCode: "Farbschemencode kopieren" description: "Beschreibung" installed: "{name} wurde installiert" installedThemes: "Installierte Farbschemata" @@ -3103,6 +3110,7 @@ _serverSetupWizard: text2: "Wir würden uns über deine Unterstützung freuen, damit wir dieses Projekt auch in Zukunft weiterentwickeln können." text3: "Für Unterstützer gibt es auch besondere Vorteile!" _uploader: + editImage: "Bild bearbeiten" compressedToX: "Komprimiert zu {x}" savedXPercent: "{x}% gespart" abortConfirm: "Einige Dateien wurden nicht hochgeladen. Möchtest du den Vorgang abbrechen?" @@ -3141,6 +3149,12 @@ _watermarkEditor: stripeWidth: "Linienbreite" stripeFrequency: "Linienanzahl" angle: "Winkel" + polkadot: "Punktmuster" + polkadotMainDotOpacity: "Deckkraft des Hauptpunktes" + polkadotMainDotRadius: "Größe des Hauptpunktes" + polkadotSubDotOpacity: "Deckkraft des Unterpunktes" + polkadotSubDotRadius: "Größe des Unterpunktes" + polkadotSubDotDivisions: "Anzahl der Unterpunkte" _imageEffector: title: "Effekte" addEffect: "Effekte hinzufügen" @@ -3156,5 +3170,18 @@ _imageEffector: colorClampAdvanced: "Farbkomprimierung (erweitert)" distort: "Verzerrung" stripe: "Streifen" + polkadot: "Punktmuster" +drafts: "Entwurf" _drafts: + select: "Entwurf auswählen" + cannotCreateDraftAnymore: "Die Anzahl der Entwürfe, die erstellt werden können, wurde überschritten." + cannotCreateDraft: "Mit diesem Inhalt kann kein Entwurf erstellt werden." + delete: "Entwurf löschen" + deleteAreYouSure: "Entwurf löschen?" + noDrafts: "Keine Entwürfe" + replyTo: "Antwort an {user}" + quoteOf: "Zitat von {user}s Notiz" + saveToDraft: "Als Entwurf speichern" + restoreFromDraft: "Aus Entwurf wiederherstellen" restore: "Wiederherstellen" + listDrafts: "Liste der Entwürfe" diff --git a/locales/en-US.yml b/locales/en-US.yml index dfcb85402e..cc3b8f23ba 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1367,7 +1367,7 @@ tip: "Tips & Tricks" redisplayAllTips: "Show all “Tips & Tricks” again" hideAllTips: "Hide all \"Tips & Tricks\"" defaultImageCompressionLevel: "Default image compression level" -defaultImageCompressionLevel_description: "High, reduces the file size but also the image quality.
High, reduces the file size but also the image quality." +defaultImageCompressionLevel_description: "Lower level preserves image quality but increases file size.
Higher level reduce file size, but reduce image quality." _order: newest: "Newest First" oldest: "Oldest First" @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "Specifies the allowed MIME/file types. Multiple MIME types can be specified by separating them with a new line, and wildcards can be specified with an asterisk (*). (e.g., image/*)" uploadableFileTypes_caption2: "Some files types might fail to be detected. To allow such files, add {x} to the specification." noteDraftLimit: "Number of possible drafts of server notes" + watermarkAvailable: "Availability of watermark function" _condition: roleAssignedTo: "Assigned to manual roles" isLocal: "Local user" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index d4f4ee084d..9afebc6580 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -61,7 +61,7 @@ copyRSS: "Copiar RSS" copyUsername: "Copiar nombre de usuario" copyUserId: "Copiar ID del usuario" copyNoteId: "Copiar ID de la nota" -copyFileId: "Copiar ID del archivo" +copyFileId: "Copiar ID de archivo" copyFolderId: "Copiar ID de carpeta" copyProfileUrl: "Copiar la URL del perfil" searchUser: "Buscar un usuario" @@ -83,10 +83,10 @@ files: "Archivos" download: "Descargar" driveFileDeleteConfirm: "¿Desea borrar el archivo \"{name}\"? Las notas que tengan este archivo como adjunto serán eliminadas" unfollowConfirm: "¿Desea dejar de seguir a {name}?" -exportRequested: "Se ha solicitado la exportación. Puede tomar un tiempo. Cuando termine la exportación, se añadirá en el drive" -importRequested: "Se ha solicitado la importación. Puede tomar un tiempo." +exportRequested: "Has solicitado la exportación. Puede llevar un tiempo. Cuando termine la exportación, se añadirá al drive" +importRequested: "Has solicitado la importación. Puede llevar un tiempo." lists: "Listas" -noLists: "No tiene listas" +noLists: "No tienes ninguna lista" note: "Notas" notes: "Notas" following: "Siguiendo" @@ -99,9 +99,9 @@ somethingHappened: "Ocurrió un error" retry: "Reintentar" pageLoadError: "Error al leer la página" pageLoadErrorDescription: "Normalmente es debido a la red o al caché del navegador. Por favor limpie el caché o intente más tarde." -serverIsDead: "No hay respuesta del servidor. Espere un momento y vuelva a intentarlo." -youShouldUpgradeClient: "Para ver esta página, por favor refrezca el navegador y utiliza una versión más reciente del cliente." -enterListName: "Ingrese nombre de lista" +serverIsDead: "No hay respuesta del servidor. Espera un momento y vuelve a intentarlo." +youShouldUpgradeClient: "Para ver esta página, recarga el navegador para actualizar el cliente." +enterListName: "Introduce un nombre para la lista" privacy: "Privacidad" makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento" defaultNoteVisibility: "Visibilidad por defecto" @@ -134,28 +134,28 @@ emojiPicker: "Selector de emojis" pinnedEmojisForReactionSettingDescription: "Puedes seleccionar reacciones para fijarlos en el selector" pinnedEmojisSettingDescription: "Puedes seleccionar emojis para fijarlos en el selector" emojiPickerDisplay: "Mostrar el selector de emojis" -overwriteFromPinnedEmojisForReaction: "Sobreescribir las reacciones fijadas" -overwriteFromPinnedEmojis: "Sobreescribir los emojis fijados" -reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir." +overwriteFromPinnedEmojisForReaction: "Sobreescribir los ajustes de reacciones" +overwriteFromPinnedEmojis: "Sobreescribir los ajustes generales" +reactionSettingDescription2: "Arrastra para reordenar, click para borrar, pulsa \"+\" para añadir." rememberNoteVisibility: "Recordar visibilidad" attachCancel: "Quitar adjunto" -deleteFile: "Archivo eliminado" +deleteFile: "Eliminar archivo" markAsSensitive: "Marcar como sensible" unmarkAsSensitive: "Desmarcar como sensible" -enterFileName: "Ingrese el nombre del archivo" +enterFileName: "Introduce el nombre del archivo" mute: "Silenciar" unmute: "Dejar de silenciar" renoteMute: "Silenciar renota" renoteUnmute: "Desilenciar renota" block: "Bloquear" -unblock: "Dejar de bloquear" +unblock: "Desbloquear" suspend: "Suspender" unsuspend: "Dejar de suspender" -blockConfirm: "¿Quiere bloquear esta cuenta?" -unblockConfirm: "¿Quiere dejar de bloquear esta cuenta?" -suspendConfirm: "¿Quiere suspender esta cuenta?" -unsuspendConfirm: "¿Quiere dejar de suspender esta cuenta?" -selectList: "Seleccione una lista" +blockConfirm: "¿Quieres bloquear esta cuenta?" +unblockConfirm: "¿Quieres desbloquear esta cuenta?" +suspendConfirm: "¿Quieres suspender esta cuenta?" +unsuspendConfirm: "¿Quieres dejar de suspender esta cuenta?" +selectList: "Selecciona una lista" editList: "Editar lista" selectChannel: "Seleccionar canal" selectAntenna: "Seleccionar antena" @@ -163,55 +163,55 @@ editAntenna: "Editar antena" createAntenna: "Crear una antena" selectWidget: "Seleccionar widget" editWidgets: "Editar widgets" -editWidgetsExit: "Terminar edición" +editWidgetsExit: "Hecho" customEmojis: "Emojis personalizados" emoji: "Emoji" -emojis: "Emoji" +emojis: "Emojis" emojiName: "Nombre del emoji" -emojiUrl: "URL de la imagen del emoji" -addEmoji: "Agregar emoji" -settingGuide: "Configuración sugerida" -cacheRemoteFiles: "Mantener en cache los archivos remotos" -cacheRemoteFilesDescription: "Si desactiva esta configuración, Los archivos remotos se cargarán desde el link directo sin usar la caché. Con eso se puede ahorrar almacenamiento del servidor, pero eso aumentará el tráfico al no crear miniaturas." +emojiUrl: "URL del emoji" +addEmoji: "Añadir emoji" +settingGuide: "Configuración recomendada" +cacheRemoteFiles: "Mantener los archivos remotos en caché" +cacheRemoteFilesDescription: "Si desactivas esta configuración, los archivos remotos se cargarán directamente de los servidores remotos. Desactivar esto reducirá el uso de almacenamiento, pero incrementará el uso de tráfico, ya que no se generarán miniaturas." youCanCleanRemoteFilesCache: "Puedes vaciar la caché pulsando en el botón 🗑️ en el administrador de archivos." -cacheRemoteSensitiveFiles: "Cachear archivos remotos sensibles" -cacheRemoteSensitiveFilesDescription: "Cuando esta opción está desactivada, los archivos remotos sensibles son cargador directamente de la instancia origen sin ser cacheados." +cacheRemoteSensitiveFiles: "Mantener los archivos remotos sensibles en caché" +cacheRemoteSensitiveFilesDescription: "Cuando esta opción está desactivada, los archivos remotos sensibles se cargarán directamente desde los servidores remotos." flagAsBot: "Esta cuenta es un bot" -flagAsBotDescription: "En caso de que esta cuenta fuera usada por un programa, active esta opción. Al hacerlo, esta opción servirá para otros desarrolladores para evitar cadenas infinitas de reacciones, y ajustará los sistemas internos de Misskey para que trate a esta cuenta como un bot." -flagAsCat: "Esta cuenta es un gato" -flagAsCatDescription: "En caso de que declare que esta cuenta es de un gato, active esta opción." -flagShowTimelineReplies: "Mostrar respuestas a las notas en la biografía" -flagShowTimelineRepliesDescription: "Cuando se marca, la línea de tiempo muestra respuestas a otras notas además de las notas del usuario" +flagAsBotDescription: "Activa esta opción si la cuenta es utilizada por un programa. Si se activa, actuará como una etiqueta para otros desarrolladores para prevenir cadenas eternas de interacción con otros bots, y ajustará los sistemas internos de Misskey para tratar esta cuenta de manera acorde." +flagAsCat: "Marcar esta cuenta como gato" +flagAsCatDescription: "Activa esta opción para marcar esta cuenta como un gato." +flagShowTimelineReplies: "Mostrar respuestas en la línea de tiempo" +flagShowTimelineRepliesDescription: "Muestra respuestas de los usuarios a las notas de otros usuarios en la línea de tiempo al activar esta opción." autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues" -addAccount: "Agregar Cuenta" +addAccount: "Agregar cuenta" reloadAccountsList: "Recargar lista de cuentas" loginFailed: "Error al iniciar sesión." -showOnRemote: "Ver en una instancia remota" -continueOnRemote: "Ver en una instancia remota" +showOnRemote: "Ver en instancia remota" +continueOnRemote: "Continuar en una instancia remota" chooseServerOnMisskeyHub: "Elegir un servidor en Misskey Hub" specifyServerHost: "Especifica una instancia directamente" -inputHostName: "Introduzca el dominio" +inputHostName: "Introduce el dominio" general: "General" wallpaper: "Fondo de pantalla" setWallpaper: "Establecer fondo de pantalla" removeWallpaper: "Quitar fondo de pantalla" searchWith: "Buscar: {q}" -youHaveNoLists: "No tienes listas" -followConfirm: "¿Desea seguir a {name}?" +youHaveNoLists: "No tienes ninguna lista" +followConfirm: "¿Quieres seguir a {name}?" proxyAccount: "Cuenta proxy" -proxyAccountDescription: "Una cuenta proxy es una cuenta que actúa como un seguidor remoto de un usuario bajo ciertas condiciones. Por ejemplo, cuando un usuario añade un usuario remoto a una lista, si ningún usuario local sigue al usuario agregado a la lista, la instancia no puede obtener su actividad. Así que la cuenta proxy sigue al usuario añadido a la lista" -host: "Host" +proxyAccountDescription: "Una cuenta proxy es una cuenta que actúa como un seguidor remoto de un usuario bajo ciertas condiciones. Por ejemplo, cuando un usuario añade un usuario remoto a una lista, si ningún usuario local sigue al usuario agregado a la lista, la instancia no puede obtener su actividad, así que la cuenta proxy sigue al usuario añadido a la lista" +host: "Instancia" selectSelf: "Elígete a ti mismo" selectUser: "Elegir usuario" -recipient: "Recipiente" +recipient: "Receptor" annotation: "Anotación" federation: "Federación" -instances: "Instancia" +instances: "Instancias" registeredAt: "Registrado en" -latestRequestReceivedAt: "Ultimo pedido recibido" -latestStatus: "Último status" +latestRequestReceivedAt: "Última petición recibida" +latestStatus: "Último estado" storageUsage: "Almacenamiento usado" -charts: "Chat" +charts: "Métricas" perHour: "por hora" perDay: "por día" stopActivityDelivery: "Dejar de enviar actividades" @@ -226,40 +226,40 @@ metadata: "Metadatos" withNFiles: "{n} archivos" monitor: "Monitor" jobQueue: "Cola de trabajos" -cpuAndMemory: "CPU y Memoria" +cpuAndMemory: "CPU y memoria" network: "Red" disk: "Disco" -instanceInfo: "información de la instancia" +instanceInfo: "Información de la instancia" statistics: "Estadísticas" clearQueue: "Limpiar cola" -clearQueueConfirmTitle: "¿Desea limpiar la cola?" +clearQueueConfirmTitle: "¿Quieres limpiar la cola?" clearQueueConfirmText: "Las notas aún no entregadas no se federarán. Normalmente no se necesita ejecutar esta operación" clearCachedFiles: "Limpiar caché" -clearCachedFilesConfirm: "¿Desea borrar todos los archivos remotos cacheados?" +clearCachedFilesConfirm: "¿Quieres borrar todos los archivos remotos en caché?" blockedInstances: "Instancias bloqueadas" -blockedInstancesDescription: "Seleccione los hosts de las instancias que desea bloquear, separadas por una linea nueva. Las instancias bloqueadas no podrán comunicarse con esta instancia." +blockedInstancesDescription: "La lista de los dominios de las instancias que quieres bloquear, separadas por una linea nueva. Las instancias bloqueadas no podrán comunicarse con esta instancia." silencedInstances: "Instancias silenciadas" -silencedInstancesDescription: "Listar los hostname de las instancias que quieres silenciar. Todas las cuentas de las instancias listadas serán tratadas como silenciadas, solo podrán hacer peticiones de seguimiento, y no podrán mencionar cuentas locales si no las siguen. Esto no afecta a las instancias bloqueadas." -mediaSilencedInstances: "Servidores silenciados (Multimedia)" -mediaSilencedInstancesDescription: "Listar las instancias que quieres silenciar. Todas las cuentas de las instancias listadas serán tratadas como silenciadas, solo podrán hacer peticiones de seguimiento, y no podrán mencionar cuentas locales si no las siguen. Esto no afecta a las instancias bloqueadas." +silencedInstancesDescription: "La lista de los dominios de las instancias que quieres silenciar. Todas las cuentas de las instancias listadas serán tratadas como silenciadas, solo podrán hacer peticiones de seguimiento, y no podrán mencionar cuentas locales si no las siguen. Esto no afecta a las instancias bloqueadas." +mediaSilencedInstances: "Servidores con multimedia silenciada" +mediaSilencedInstancesDescription: "La lista de los dominios de las instancias cuya multimedia quieres silenciar. Todas las cuentas que pertenezcan a estas instancias serán marcadas como sensibles, y no podrán usar sus emojis personalizados. Esto no afectará a las instancias bloqueadas" federationAllowedHosts: "Servidores federados" -federationAllowedHostsDescription: "Establezca los nombres de los servidores que pueden federarse, separados por una nueva línea." +federationAllowedHostsDescription: "La lista de los dominios de las instancias cuya federación está permitida, separadas por saltos de línea." muteAndBlock: "Silenciar y bloquear" mutedUsers: "Usuarios silenciados" blockedUsers: "Usuarios bloqueados" noUsers: "No hay usuarios" editProfile: "Editar perfil" -noteDeleteConfirm: "¿Desea borrar esta nota?" -pinLimitExceeded: "Ya no se pueden fijar más posts" +noteDeleteConfirm: "¿Quieres borrar esta nota?" +pinLimitExceeded: "Ya no se pueden fijar más notas" done: "Terminado" -processing: "Procesando" +processing: "Procesando..." preview: "Vista previa" default: "Predeterminado" defaultValueIs: "Por defecto: {value}" noCustomEmojis: "No hay emojis personalizados" noJobs: "No hay trabajos" federating: "Federando" -blocked: "Bloqueando" +blocked: "Bloqueado" suspended: "Suspendido" all: "Todo" subscribing: "Suscribiendo" @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "Especifica los tipos MIME/archivos permitidos. Se pueden especificar varios tipos MIME separándolos con una nueva línea, y se pueden especificar comodines con un asterisco (*). (por ejemplo, image/*)" uploadableFileTypes_caption2: "Es posible que no se detecten algunos tipos de archivos. Para permitir estos archivos, añade {x} a la especificación." noteDraftLimit: "Número de posibles borradores de notas del servidor" + watermarkAvailable: "Disponibilidad de la función de marca de agua" _condition: roleAssignedTo: "Asignado a roles manuales" isLocal: "Usuario local" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index f038d1b167..00a470a718 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "MIME 유형을 " uploadableFileTypes_caption2: "파일에 따라서는 유형을 검사하지 못하는 경우가 있습니다. 그러한 파일을 허가하는 경우에는 {x}를 지정으로 추가해주십시오." noteDraftLimit: "서버측 노트 초안 작성 가능 수" + watermarkAvailable: "워터마크 기능의 사용 여부" _condition: roleAssignedTo: "수동 역할에 이미 할당됨" isLocal: "로컬 유저" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 0fa88fec93..978f81b626 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -446,7 +446,7 @@ exploreUsersCount: "Há um utilizador de {count}" exploreFediverse: "Explorar Fediverse" popularTags: "Tags populares" userList: "Listas" -about: "Informações" +about: "Sobre" aboutMisskey: "Sobre Misskey" administrator: "Administrador" token: "Símbolo" @@ -1339,7 +1339,7 @@ paste: "Colar" emojiPalette: "Paleta de emojis" postForm: "Campo de postagem" textCount: "Contagem de caracteres" -information: "Informações" +information: "Sobre" chat: "Conversas" migrateOldSettings: "Migrar configurações antigas de cliente" migrateOldSettings_description: "Isso deve ser feito automaticamente. Caso o processo de migração tenha falhado, você pode acioná-lo manualmente. As informações atuais de migração serão substituídas." @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "Especifica tipos MIME permitidos. Múltiplos tipos MIME podem ser especificados separando-os por linha. Curingas podem ser especificados com um asterisco (*). (exemplo, image/*)" uploadableFileTypes_caption2: "Alguns tipos de arquivos podem não ser detectados. Para permiti-los, adicione {x} à especificação." noteDraftLimit: "Limite de rascunhos possíveis" + watermarkAvailable: "Disponibilidade da função de marca d'água" _condition: roleAssignedTo: "Atribuído a cargos manuais" isLocal: "Usuário local" @@ -2806,6 +2807,7 @@ _fileViewer: url: "URL" uploadedAt: "Adicionado em" attachedNotes: "Notas anexadas" + usage: "Usado" thisPageCanBeSeenFromTheAuthor: "Essa página só pode ser vista pelo usuário que enviou esse arquivo." _externalResourceInstaller: title: "Instalar de site externo" @@ -3182,5 +3184,14 @@ drafts: "Rascunhos" _drafts: select: "Selecionar Rascunho" cannotCreateDraftAnymore: "O número máximo de rascunhos foi excedido." + cannotCreateDraft: "Você não pode criar um rascunho com esse conteúdo." delete: "Excluir Rascunho" + deleteAreYouSure: "Excluir rascunho?" + noDrafts: "Sem rascunhos" + replyTo: "Resposta a {user}" + quoteOf: "Citação à nota de {user}" + postTo: "Publicando em {channel}" + saveToDraft: "Salvar como Rascunho" + restoreFromDraft: "Restaurar de Rascunho" restore: "Redefinir" + listDrafts: "Lista de Rascunhos" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 2f3d70c34e..d6b0ef4de4 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "สามารถระบุ MIME type ได้ โดยใช้การขึ้นบรรทัดใหม่เพื่อแยกหลายรายการ และสามารถใช้ดอกจัน (*) เพื่อระบุแบบไวลด์การ์ดได้ (เช่น: image/*)" uploadableFileTypes_caption2: "ไฟล์บางประเภทอาจไม่สามารถระบุชนิดได้ หากต้องการอนุญาตไฟล์ลักษณะนั้น กรุณาเพิ่ม {x} ลงในรายการที่อนุญาต" noteDraftLimit: "จำนวนโน้ตฉบับร่างที่สามารถสร้างได้บนฝั่งเซิร์ฟเวอร์" + watermarkAvailable: "มีฟังก์ชั่นลายน้ำให้เลือกใช้" _condition: roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ" isLocal: "ผู้ใช้ท้องถิ่น" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index f236202abe..0958158e5e 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "指定 MIME 类型。可用换行指定多个类型,也可以用星号(*)作为通配符。(如 image/*)" uploadableFileTypes_caption2: "文件根据文件的不同,可能无法判断其类型。若要允许此类文件,请在指定中添加 {x}。" noteDraftLimit: "可在服务器上创建多少草稿" + watermarkAvailable: "能否使用水印功能" _condition: roleAssignedTo: "已分配给手动角色" isLocal: "是本地用户" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index c6ff0bb1a3..d0dbecfcba 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1998,6 +1998,7 @@ _role: uploadableFileTypes_caption: "請指定 MIME 類型。可以用換行區隔多個類型,也可以使用星號(*)作為萬用字元進行指定。(例如:image/*)\n" uploadableFileTypes_caption2: "有些檔案可能無法判斷其類型。若要允許這類檔案,請在指定中加入 {x}。" noteDraftLimit: "伺服器端可建立的貼文草稿數量上限\n" + watermarkAvailable: "浮水印功能是否可用" _condition: roleAssignedTo: "手動指派角色完成" isLocal: "本地使用者" From 598641de48e2534d839426cb063ba3766955e7ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 17 Jul 2025 11:07:00 +0000 Subject: [PATCH 57/85] Bump version to 2025.7.0-rc.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 bd26f773cd..47adc622be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.7.0-rc.0", + "version": "2025.7.0-rc.1", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 28e8ee8886..3a21e3d5cd 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.7.0-rc.0", + "version": "2025.7.0-rc.1", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 9852196ddcd68b55db3b3a967b7e033ab7249ea8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 18 Jul 2025 00:27:57 +0000 Subject: [PATCH 58/85] Release: 2025.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 47adc622be..7bfca56065 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.7.0-rc.1", + "version": "2025.7.0", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 3a21e3d5cd..59ed29eeab 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.7.0-rc.1", + "version": "2025.7.0", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 73419e8a61bf3a6423f490027b6a74ce4c6132f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 18 Jul 2025 00:28:02 +0000 Subject: [PATCH 59/85] [skip ci] Update CHANGELOG.md (prepend template) --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a31031830..5f6ca2a862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## Unreleased + +### General +- + +### Client +- + +### Server +- + + ## 2025.7.0 ### Note From 002e69d8c86873bdb9da5b5cfcfee00acfd56e17 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 19 Jul 2025 00:31:36 +0900 Subject: [PATCH 60/85] clean up --- packages/backend/src/core/activitypub/ApDeliverManagerService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 9d22ea6e3a..a8a6a1491e 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -208,7 +208,6 @@ export class ApDeliverManagerService { * 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 { From 090ab19f28dee09a059d5183410ff25ac0f3681b Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 19 Jul 2025 01:00:55 +0900 Subject: [PATCH 61/85] actions fail invest: skip test apdeliveerman --- packages/backend/test/unit/ApDeliverManagerService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test/unit/ApDeliverManagerService.ts b/packages/backend/test/unit/ApDeliverManagerService.ts index c92fc71ac7..a449ce3039 100644 --- a/packages/backend/test/unit/ApDeliverManagerService.ts +++ b/packages/backend/test/unit/ApDeliverManagerService.ts @@ -17,7 +17,7 @@ import { FollowingsRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; -describe('ApDeliverManagerService', () => { +describe.skip('ApDeliverManagerService', () => { let service: ApDeliverManagerService; let followingsRepository: jest.Mocked; let queueService: jest.Mocked; @@ -330,7 +330,7 @@ describe('ApDeliverManagerService', () => { }); }); -describe('ApDeliverManagerService (SQL)', () => { +describe.skip('ApDeliverManagerService (SQL)', () => { // followerにデータを挿入して、SQLの動作を確認します let app: TestingModule; let service: ApDeliverManagerService; From 4bac864b079d6eb9fc52897341fb7be5c17316a1 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 19 Jul 2025 01:24:14 +0900 Subject: [PATCH 62/85] Revert "actions fail invest: skip test apdeliveerman" This reverts commit 090ab19f28dee09a059d5183410ff25ac0f3681b. --- packages/backend/test/unit/ApDeliverManagerService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test/unit/ApDeliverManagerService.ts b/packages/backend/test/unit/ApDeliverManagerService.ts index a449ce3039..c92fc71ac7 100644 --- a/packages/backend/test/unit/ApDeliverManagerService.ts +++ b/packages/backend/test/unit/ApDeliverManagerService.ts @@ -17,7 +17,7 @@ import { FollowingsRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; -describe.skip('ApDeliverManagerService', () => { +describe('ApDeliverManagerService', () => { let service: ApDeliverManagerService; let followingsRepository: jest.Mocked; let queueService: jest.Mocked; @@ -330,7 +330,7 @@ describe.skip('ApDeliverManagerService', () => { }); }); -describe.skip('ApDeliverManagerService (SQL)', () => { +describe('ApDeliverManagerService (SQL)', () => { // followerにデータを挿入して、SQLの動作を確認します let app: TestingModule; let service: ApDeliverManagerService; From d4065d5a2b53acb9b2bec0fa1bb45f73854810b1 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 19 Jul 2025 01:24:31 +0900 Subject: [PATCH 63/85] actions fail invest: log jest setup --- packages/backend/test/jest.setup.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts index 7c6dd6a55f..159ffd429a 100644 --- a/packages/backend/test/jest.setup.ts +++ b/packages/backend/test/jest.setup.ts @@ -7,5 +7,7 @@ import { initTestDb, sendEnvResetRequest } from './utils.js'; beforeAll(async () => { await initTestDb(false); + console.log('Test database initialized.'); await sendEnvResetRequest(); + console.log('Environment reset completed.'); }); From 9447bbee44e14ee5b64941f480e047f5a1e86226 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 19 Jul 2025 02:03:07 +0900 Subject: [PATCH 64/85] actions fail invest: skip oauth test --- packages/backend/test/e2e/oauth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index f639f90ea6..b9718bcec4 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -152,7 +152,7 @@ async function assertDirectError(response: Response, status: number, error: stri assert.strictEqual(data.error, error); } -describe('OAuth', () => { +describe.skip('OAuth', () => { let fastify: FastifyInstance; let alice: misskey.entities.SignupResponse; From 3dac916640cf4294fda9307f94ff0d57f44a271a Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 19 Jul 2025 02:23:45 +0900 Subject: [PATCH 65/85] actions fail invest: log test-server entry --- packages/backend/test-server/entry.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts index 04bf62d209..7e6078856c 100644 --- a/packages/backend/test-server/entry.ts +++ b/packages/backend/test-server/entry.ts @@ -74,11 +74,14 @@ async function startControllerEndpoints(port = config.port + 1000) { }); fastify.post<{ Body: { key?: string, value?: string } }>('/env-reset', async (req, res) => { + console.log('env-reset'); process.env = JSON.parse(originEnv); await serverService.dispose(); await app.close(); + console.log('Nest application closed.'); + await killTestServer(); console.log('starting application...'); @@ -88,6 +91,7 @@ async function startControllerEndpoints(port = config.port + 1000) { }); serverService = app.get(ServerService); await serverService.launch(); + console.log('application launched.'); res.code(200).send({ success: true }); }); From 90c4551dfe457d3735e64e27995ab0ad3fb26f4a Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 19 Jul 2025 03:03:05 +0900 Subject: [PATCH 66/85] Revert "actions fail invest: skip oauth test" This reverts commit 9447bbee44e14ee5b64941f480e047f5a1e86226. --- packages/backend/test/e2e/oauth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index b9718bcec4..f639f90ea6 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -152,7 +152,7 @@ async function assertDirectError(response: Response, status: number, error: stri assert.strictEqual(data.error, error); } -describe.skip('OAuth', () => { +describe('OAuth', () => { let fastify: FastifyInstance; let alice: misskey.entities.SignupResponse; From 48246bd166af64d9ebb746b0851822854757a513 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, 19 Jul 2025 14:00:19 +0900 Subject: [PATCH 67/85] fix(deps): regenerate lockfile (#16302) --- pnpm-lock.yaml | 1415 +++++------------------------------------------- 1 file changed, 143 insertions(+), 1272 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08711cac71..7430550b6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1706,10 +1706,6 @@ packages: resolution: {integrity: sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA==} engines: {node: '>=18.0.0'} - '@babel/code-frame@7.24.7': - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -1764,18 +1760,10 @@ packages: resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.24.8': - resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 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-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} @@ -1788,15 +1776,6 @@ packages: resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.7': - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.28.0': resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} @@ -1887,14 +1866,6 @@ packages: resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} engines: {node: '>=6.9.0'} - '@babel/types@7.25.6': - resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} - engines: {node: '>=6.9.0'} - '@babel/types@7.28.1': resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} engines: {node: '>=6.9.0'} @@ -1996,258 +1967,129 @@ packages: '@discordapp/twemoji@15.1.0': resolution: {integrity: sha512-QdpV4ifTONAXvDjRrMohausZeGrQ1ac/Ox6togUh6Xl3XKJ/KAaMMuAEi0qsb0wDwoVTSZBll5Y6+N3hB2ktBw==} - '@emnapi/runtime@1.4.0': - resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==} - '@emnapi/runtime@1.4.3': resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} - '@esbuild/aix-ppc64@0.25.4': - resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.6': resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.4': - resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.6': resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.4': - resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.6': resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.4': - resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.6': resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.4': - resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.6': resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.4': - resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.6': resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.4': - resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.6': resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.4': - resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.6': resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.4': - resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.6': resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.4': - resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.6': resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.4': - resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.6': resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.4': - resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.6': resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.4': - resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.6': resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.4': - resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.6': resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.4': - resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.6': resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.4': - resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.6': resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.4': - resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.6': resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.4': - resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.25.6': resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.4': - resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.6': resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.4': - resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.25.6': resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.4': - resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.6': resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} engines: {node: '>=18'} @@ -2260,48 +2102,24 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.4': - resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.6': resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.4': - resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.6': resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.4': - resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.6': resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.4': - resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.6': resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} engines: {node: '>=18'} @@ -4566,18 +4384,12 @@ packages: '@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==} - '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} '@types/istanbul-lib-report@3.0.0': resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - '@types/istanbul-reports@3.0.1': - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} @@ -4800,9 +4612,6 @@ packages: '@types/yargs-parser@21.0.0': resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - '@types/yargs@17.0.19': - resolution: {integrity: sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==} - '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} @@ -5011,21 +4820,9 @@ packages: '@volar/typescript@2.4.15': resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} - '@vue/compiler-core@3.5.13': - resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} - - '@vue/compiler-core@3.5.16': - resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} - '@vue/compiler-core@3.5.17': resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==} - '@vue/compiler-dom@3.5.13': - resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} - - '@vue/compiler-dom@3.5.16': - resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} - '@vue/compiler-dom@3.5.17': resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==} @@ -5068,12 +4865,6 @@ packages: peerDependencies: vue: 3.5.17 - '@vue/shared@3.5.13': - resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} - - '@vue/shared@3.5.16': - resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} - '@vue/shared@3.5.17': resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==} @@ -5251,10 +5042,6 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -5322,10 +5109,6 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} - array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -5333,10 +5116,6 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} - array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -5345,34 +5124,18 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} - array.prototype.findlastindex@1.2.6: resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} engines: {node: '>= 0.4'} - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - array.prototype.flat@1.3.3: resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} engines: {node: '>= 0.4'} - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} - array.prototype.flatmap@1.3.3: resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} engines: {node: '>= 0.4'} - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - arraybuffer.prototype.slice@1.0.4: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} @@ -5566,11 +5329,6 @@ packages: browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} - browserslist@4.24.5: - resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.25.1: resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5647,10 +5405,6 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - call-bind@1.0.8: resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} @@ -5681,9 +5435,6 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001718: - resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} - caniuse-lite@1.0.30001727: resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} @@ -5715,10 +5466,6 @@ packages: resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==} engines: {node: '>=14.16'} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} @@ -5882,16 +5629,10 @@ packages: collect-v8-coverage@1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -6147,26 +5888,14 @@ packages: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} - data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} - data-view-byte-length@1.0.2: resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} engines: {node: '>= 0.4'} - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} - data-view-byte-offset@1.0.1: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} @@ -6391,9 +6120,6 @@ packages: domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} - domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -6425,9 +6151,6 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.155: - resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==} - electron-to-chromium@1.5.186: resolution: {integrity: sha512-lur7L4BFklgepaJxj4DqPk7vKbTEl0pajNlg2QjE5shefmlmBLm2HvQ7PMf1R/GvlevT/581cop33/quQcfX3A==} @@ -6483,10 +6206,6 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} - es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -6513,17 +6232,10 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - es-shim-unscopables@1.1.0: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - es-to-primitive@1.3.0: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} @@ -6542,11 +6254,6 @@ packages: peerDependencies: esbuild: '>=0.12 <1' - esbuild@0.25.4: - resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.25.6: resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} engines: {node: '>=18'} @@ -6589,27 +6296,6 @@ packages: eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - eslint-module-utils@2.12.0: - resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - eslint-module-utils@2.12.1: resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} engines: {node: '>=4'} @@ -6673,10 +6359,6 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-visitor-keys@4.2.1: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6983,17 +6665,10 @@ packages: debug: optional: true - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -7069,10 +6744,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - function.prototype.name@1.1.8: resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} @@ -7121,17 +6792,10 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.10.0: - resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} - get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -7156,11 +6820,6 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - glob@11.0.2: - resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==} - engines: {node: 20 || >=22} - hasBin: true - glob@11.0.3: resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} engines: {node: 20 || >=22} @@ -7191,10 +6850,6 @@ packages: resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} engines: {node: '>=18'} - globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} - globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -7267,10 +6922,6 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - has-proto@1.2.0: resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} @@ -7507,10 +7158,6 @@ packages: resolution: {integrity: sha512-+y6WywKZREw5rq7U2jvr2nmZpT7cbWbQQ0N/qfcseYnzHFz2cZz1Et52oY+XttYuYeTkI8Y+R2JNWj68MpQFSg==} hasBin: true - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -7550,10 +7197,6 @@ packages: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -7568,17 +7211,10 @@ packages: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - is-bigint@1.1.0: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -7587,26 +7223,14 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} - is-data-view@1.0.2: resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - is-date-object@1.1.0: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} @@ -7650,9 +7274,6 @@ packages: resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} engines: {node: '>=10'} - is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -7664,10 +7285,6 @@ packages: is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -7698,25 +7315,14 @@ packages: is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} - is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - is-shared-array-buffer@1.0.4: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} @@ -7733,10 +7339,6 @@ packages: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -7745,18 +7347,10 @@ packages: resolution: {integrity: sha512-uVg5yifaTxHoefNf5Jcx+i9RZe2OBYd/UStp1umx+EERa4xGRa3LLGXjoEph43qUORC0qkafUgrXZ6zzK89yGA==} engines: {node: '>=14.16'} - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - is-symbol@1.1.1: resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} @@ -7772,23 +7366,14 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} - is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - is-weakref@1.1.1: resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} engines: {node: '>= 0.4'} - is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} - is-weakset@2.0.4: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} @@ -7848,10 +7433,6 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.0.1: - resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==} - engines: {node: 20 || >=22} - jackspeak@4.1.1: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} @@ -8247,9 +7828,6 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loupe@3.1.3: - resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} - loupe@3.1.4: resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} @@ -8703,11 +8281,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@5.1.5: resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} engines: {node: ^18 || >=20} @@ -8750,10 +8323,6 @@ packages: nise@6.1.1: resolution: {integrity: sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==} - node-abi@3.74.0: - resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} - engines: {node: '>=10'} - node-abi@3.75.0: resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} engines: {node: '>=10'} @@ -8914,10 +8483,6 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - object.assign@4.1.7: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} @@ -8930,10 +8495,6 @@ packages: resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} engines: {node: '>= 0.4'} - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} - object.values@1.2.1: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} @@ -9075,9 +8636,6 @@ packages: parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} - parse5-htmlparser2-tree-adapter@7.0.0: - resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} - parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} @@ -9448,10 +9006,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -9810,10 +9364,6 @@ packages: regex@6.0.1: resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} @@ -9931,10 +9481,6 @@ packages: rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} - safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -9949,10 +9495,6 @@ packages: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - safe-regex-test@1.1.0: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} @@ -9975,9 +9517,6 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - sax@1.2.4: - resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} @@ -10339,10 +9878,6 @@ packages: std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} - stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} - stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} @@ -10417,13 +9952,6 @@ packages: resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} - string.prototype.trimend@1.0.9: resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} engines: {node: '>= 0.4'} @@ -10519,7 +10047,6 @@ packages: supertest@7.1.1: resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==} engines: {node: '>=14.18.0'} - deprecated: Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net supports-color@10.0.0: resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==} @@ -10635,10 +10162,6 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -10680,10 +10203,6 @@ packages: to-data-view@1.1.0: resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==} - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -10811,10 +10330,6 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - type-fest@4.26.1: - resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} - engines: {node: '>=16'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -10823,34 +10338,18 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - typed-array-byte-length@1.0.3: resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.4: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} - typed-array-length@1.0.7: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} @@ -10942,9 +10441,6 @@ packages: resolution: {integrity: sha512-fIRiVTJNcSRmXKPZtGzFQv9WRrZ3M9eoptl/teFJvjOzmpU+/K/JH6HZ8deBfb5vMEpicJcLn7JmvdknlMq7Zg==} hasBin: true - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -11221,8 +10717,8 @@ packages: vue-component-type-helpers@2.2.12: resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} - vue-component-type-helpers@3.0.1: - resolution: {integrity: sha512-j23mCB5iEbGsyIhnVdXdWUOg+UdwmVxpKnYYf2j+4ppCt5VSFXKjwu9YFt0QYxUaf5G99PuHsVfRScjHCRSsGQ==} + vue-component-type-helpers@3.0.3: + resolution: {integrity: sha512-koiBu7lO8e6w/UlbZAAIW11qcFQocYIl7Nh/SVwGZ804ej5KrncU32bRxi2zfU2Kyf6HWuk1CeeVP2rhIL+vyQ==} vue-demi@0.14.7: resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} @@ -11343,9 +10839,6 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -11354,9 +10847,6 @@ packages: resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} engines: {node: '>= 0.4'} - which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} - which-collection@1.0.2: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} @@ -11364,10 +10854,6 @@ packages: which-module@2.0.0: resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - which-typed-array@1.1.19: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} @@ -12062,11 +11548,6 @@ snapshots: '@smithy/types': 4.3.1 tslib: 2.8.1 - '@babel/code-frame@7.24.7': - dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.1.1 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -12078,15 +11559,15 @@ snapshots: '@babel/core@7.24.7': dependencies: '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 '@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.27.2 + '@babel/parser': 7.28.0 '@babel/template': 7.24.7 '@babel/traverse': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 convert-source-map: 2.0.0 debug: 4.4.1(supports-color@10.0.0) gensync: 1.0.0-beta.2 @@ -12097,7 +11578,7 @@ snapshots: '@babel/generator@7.24.7': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -12106,27 +11587,27 @@ snapshots: dependencies: '@babel/compat-data': 7.24.7 '@babel/helper-validator-option': 7.24.7 - browserslist: 4.24.5 + browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 '@babel/helper-environment-visitor@7.24.7': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@babel/helper-function-name@7.24.7': dependencies: '@babel/template': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@babel/helper-hoist-variables@7.24.7': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 transitivePeerDependencies: - supports-color @@ -12146,20 +11627,16 @@ snapshots: '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 transitivePeerDependencies: - supports-color '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.27.1 - - '@babel/helper-string-parser@7.24.8': {} + '@babel/types': 7.28.1 '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.24.7': {} - '@babel/helper-validator-identifier@7.27.1': {} '@babel/helper-validator-option@7.24.7': {} @@ -12167,18 +11644,7 @@ snapshots: '@babel/helpers@7.24.7': dependencies: '@babel/template': 7.24.7 - '@babel/types': 7.27.1 - - '@babel/highlight@7.24.7': - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/parser@7.27.2': - dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@babel/parser@7.28.0': dependencies: @@ -12260,36 +11726,25 @@ snapshots: '@babel/template@7.24.7': dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 '@babel/traverse@7.24.7': dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 '@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.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 debug: 4.4.1(supports-color@10.0.0) globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.25.6': - dependencies: - '@babel/helper-string-parser': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 - - '@babel/types@7.27.1': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/types@7.28.1': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -12445,166 +11900,86 @@ snapshots: jsonfile: 5.0.0 universalify: 0.1.2 - '@emnapi/runtime@1.4.0': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.4.3': dependencies: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.4': - optional: true - '@esbuild/aix-ppc64@0.25.6': optional: true - '@esbuild/android-arm64@0.25.4': - optional: true - '@esbuild/android-arm64@0.25.6': optional: true - '@esbuild/android-arm@0.25.4': - optional: true - '@esbuild/android-arm@0.25.6': optional: true - '@esbuild/android-x64@0.25.4': - optional: true - '@esbuild/android-x64@0.25.6': optional: true - '@esbuild/darwin-arm64@0.25.4': - optional: true - '@esbuild/darwin-arm64@0.25.6': optional: true - '@esbuild/darwin-x64@0.25.4': - optional: true - '@esbuild/darwin-x64@0.25.6': optional: true - '@esbuild/freebsd-arm64@0.25.4': - optional: true - '@esbuild/freebsd-arm64@0.25.6': optional: true - '@esbuild/freebsd-x64@0.25.4': - optional: true - '@esbuild/freebsd-x64@0.25.6': optional: true - '@esbuild/linux-arm64@0.25.4': - optional: true - '@esbuild/linux-arm64@0.25.6': optional: true - '@esbuild/linux-arm@0.25.4': - optional: true - '@esbuild/linux-arm@0.25.6': optional: true - '@esbuild/linux-ia32@0.25.4': - optional: true - '@esbuild/linux-ia32@0.25.6': optional: true - '@esbuild/linux-loong64@0.25.4': - optional: true - '@esbuild/linux-loong64@0.25.6': optional: true - '@esbuild/linux-mips64el@0.25.4': - optional: true - '@esbuild/linux-mips64el@0.25.6': optional: true - '@esbuild/linux-ppc64@0.25.4': - optional: true - '@esbuild/linux-ppc64@0.25.6': optional: true - '@esbuild/linux-riscv64@0.25.4': - optional: true - '@esbuild/linux-riscv64@0.25.6': optional: true - '@esbuild/linux-s390x@0.25.4': - optional: true - '@esbuild/linux-s390x@0.25.6': optional: true - '@esbuild/linux-x64@0.25.4': - optional: true - '@esbuild/linux-x64@0.25.6': optional: true - '@esbuild/netbsd-arm64@0.25.4': - optional: true - '@esbuild/netbsd-arm64@0.25.6': optional: true - '@esbuild/netbsd-x64@0.25.4': - optional: true - '@esbuild/netbsd-x64@0.25.6': optional: true - '@esbuild/openbsd-arm64@0.25.4': - optional: true - '@esbuild/openbsd-arm64@0.25.6': optional: true - '@esbuild/openbsd-x64@0.25.4': - optional: true - '@esbuild/openbsd-x64@0.25.6': optional: true '@esbuild/openharmony-arm64@0.25.6': optional: true - '@esbuild/sunos-x64@0.25.4': - optional: true - '@esbuild/sunos-x64@0.25.6': optional: true - '@esbuild/win32-arm64@0.25.4': - optional: true - '@esbuild/win32-arm64@0.25.6': optional: true - '@esbuild/win32-ia32@0.25.4': - optional: true - '@esbuild/win32-ia32@0.25.6': optional: true - '@esbuild/win32-x64@0.25.4': - optional: true - '@esbuild/win32-x64@0.25.6': optional: true @@ -12754,7 +12129,7 @@ snapshots: content-disposition: 0.5.4 fastify-plugin: 5.0.0 fastq: 1.17.1 - glob: 11.0.2 + glob: 11.0.3 '@fastify/view@10.0.2': dependencies: @@ -12933,7 +12308,7 @@ snapshots: '@img/sharp-wasm32@0.33.5': dependencies: - '@emnapi/runtime': 1.4.0 + '@emnapi/runtime': 1.4.3 optional: true '@img/sharp-wasm32@0.34.2': @@ -13153,7 +12528,7 @@ snapshots: dependencies: '@jest/console': 29.7.0 '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.1 '@jest/test-sequencer@29.7.0': @@ -13186,10 +12561,10 @@ snapshots: '@jest/types@29.6.3': dependencies: '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 '@types/node': 22.16.4 - '@types/yargs': 17.0.19 + '@types/yargs': 17.0.33 chalk: 4.1.2 '@jest/types@30.0.1': @@ -13912,7 +13287,7 @@ snapshots: '@readme/better-ajv-errors@2.2.2(ajv@8.17.1)': dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 '@babel/runtime': 7.27.0 '@humanwhocodes/momoa': 2.0.4 ajv: 8.17.1 @@ -14196,7 +13571,7 @@ snapshots: '@sentry/core': 8.55.0 '@sentry/node': 8.55.0 detect-libc: 2.0.4 - node-abi: 3.74.0 + node-abi: 3.75.0 transitivePeerDependencies: - supports-color @@ -14955,7 +14330,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.5.17(typescript@5.8.3) - vue-component-type-helpers: 3.0.1 + vue-component-type-helpers: 3.0.3 '@stylistic/eslint-plugin@2.13.0(eslint@9.31.0)(typescript@5.8.3)': dependencies: @@ -15159,7 +14534,7 @@ snapshots: '@testing-library/dom@10.4.0': dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 '@babel/runtime': 7.27.0 '@types/aria-query': 5.0.1 aria-query: 5.3.0 @@ -15170,7 +14545,7 @@ snapshots: '@testing-library/dom@9.3.4': dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 '@babel/runtime': 7.27.0 '@types/aria-query': 5.0.1 aria-query: 5.1.3 @@ -15224,7 +14599,7 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/archiver@6.0.3': dependencies: @@ -15236,31 +14611,31 @@ snapshots: '@types/babel__core@7.20.0': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 '@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.27.1 + '@babel/types': 7.28.1 '@types/babel__template@7.4.1': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 '@types/babel__traverse@7.20.0': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@types/bcryptjs@2.4.6': {} '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.36 - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/braces@3.0.1': {} @@ -15320,7 +14695,7 @@ snapshots: '@types/fluent-ffmpeg@2.1.27': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/graceful-fs@4.1.6': dependencies: @@ -15338,19 +14713,13 @@ snapshots: '@types/http-link-header@1.0.7': dependencies: - '@types/node': 22.15.31 - - '@types/istanbul-lib-coverage@2.0.4': {} + '@types/node': 22.16.4 '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.0': dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - - '@types/istanbul-reports@3.0.1': - dependencies: - '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports@3.0.4': dependencies: @@ -15365,7 +14734,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -15420,7 +14789,7 @@ snapshots: '@types/nodemailer@6.4.17': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/normalize-package-data@2.4.1': {} @@ -15431,11 +14800,11 @@ snapshots: '@types/oauth2orize@1.11.5': dependencies: '@types/express': 4.17.17 - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/oauth@0.9.6': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/offscreencanvas@2019.3.0': {} @@ -15447,7 +14816,7 @@ snapshots: '@types/pg@8.15.4': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 pg-protocol: 1.10.0 pg-types: 2.2.0 @@ -15465,7 +14834,7 @@ snapshots: '@types/qrcode@1.5.5': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/qs@6.9.7': {} @@ -15558,7 +14927,7 @@ snapshots: '@types/vary@1.1.3': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/wawoff2@1.0.2': dependencies: @@ -15566,18 +14935,14 @@ snapshots: '@types/web-push@3.6.4': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/ws@8.18.1': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.16.4 '@types/yargs-parser@21.0.0': {} - '@types/yargs@17.0.19': - dependencies: - '@types/yargs-parser': 21.0.0 - '@types/yargs@17.0.33': dependencies: '@types/yargs-parser': 21.0.0 @@ -15647,7 +15012,7 @@ snapshots: '@typescript-eslint/project-service@8.34.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.34.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) '@typescript-eslint/types': 8.37.0 debug: 4.4.1(supports-color@10.0.0) typescript: 5.8.3 @@ -15765,7 +15130,7 @@ snapshots: '@typescript-eslint/visitor-keys@8.34.0': dependencies: '@typescript-eslint/types': 8.34.0 - eslint-visitor-keys: 4.2.0 + eslint-visitor-keys: 4.2.1 '@typescript-eslint/visitor-keys@8.37.0': dependencies: @@ -15858,13 +15223,13 @@ snapshots: dependencies: '@vitest/pretty-format': 2.0.5 estree-walker: 3.0.3 - loupe: 3.1.3 + loupe: 3.1.4 tinyrainbow: 1.2.0 '@vitest/utils@2.1.1': dependencies: '@vitest/pretty-format': 2.1.1 - loupe: 3.1.3 + loupe: 3.1.4 tinyrainbow: 1.2.0 '@vitest/utils@3.2.4': @@ -15898,22 +15263,6 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.0.8 - '@vue/compiler-core@3.5.13': - dependencies: - '@babel/parser': 7.27.2 - '@vue/shared': 3.5.13 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-core@3.5.16': - dependencies: - '@babel/parser': 7.27.2 - '@vue/shared': 3.5.16 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - '@vue/compiler-core@3.5.17': dependencies: '@babel/parser': 7.28.0 @@ -15922,16 +15271,6 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.13': - dependencies: - '@vue/compiler-core': 3.5.13 - '@vue/shared': 3.5.13 - - '@vue/compiler-dom@3.5.16': - dependencies: - '@vue/compiler-core': 3.5.16 - '@vue/shared': 3.5.16 - '@vue/compiler-dom@3.5.17': dependencies: '@vue/compiler-core': 3.5.17 @@ -15962,8 +15301,8 @@ snapshots: '@vue/language-core@2.0.16(typescript@5.8.3)': dependencies: '@volar/language-core': 2.2.0 - '@vue/compiler-dom': 3.5.16 - '@vue/shared': 3.5.16 + '@vue/compiler-dom': 3.5.17 + '@vue/shared': 3.5.17 computeds: 0.0.1 minimatch: 9.0.5 path-browserify: 1.0.1 @@ -15974,9 +15313,9 @@ snapshots: '@vue/language-core@2.2.12(typescript@5.8.3)': dependencies: '@volar/language-core': 2.4.15 - '@vue/compiler-dom': 3.5.16 + '@vue/compiler-dom': 3.5.17 '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.16 + '@vue/shared': 3.5.17 alien-signals: 1.0.3 minimatch: 9.0.5 muggle-string: 0.4.1 @@ -16006,10 +15345,6 @@ snapshots: '@vue/shared': 3.5.17 vue: 3.5.17(typescript@5.8.3) - '@vue/shared@3.5.13': {} - - '@vue/shared@3.5.16': {} - '@vue/shared@3.5.17': {} '@vue/test-utils@2.4.1(@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))': @@ -16217,10 +15552,6 @@ snapshots: ansi-regex@6.0.1: {} - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -16291,11 +15622,6 @@ snapshots: dependencies: dequal: 2.0.3 - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 @@ -16303,15 +15629,6 @@ snapshots: array-flatten@1.1.1: {} - array-includes@3.1.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.0.7 - array-includes@3.1.9: dependencies: call-bind: 1.0.8 @@ -16325,15 +15642,6 @@ snapshots: array-union@2.1.0: {} - array.prototype.findlastindex@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.0.2 - array.prototype.findlastindex@1.2.6: dependencies: call-bind: 1.0.8 @@ -16344,44 +15652,19 @@ snapshots: es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 - array.prototype.flat@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - array.prototype.flat@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.24.0 - es-shim-unscopables: 1.0.2 - - array.prototype.flatmap@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.24.0 - es-shim-unscopables: 1.0.2 - - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 + es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: dependencies: @@ -16511,7 +15794,7 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 '@types/babel__core': 7.20.0 '@types/babel__traverse': 7.20.0 @@ -16539,7 +15822,7 @@ snapshots: babel-walk@3.0.0-canary-5: dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.28.1 bail@2.0.2: {} @@ -16626,13 +15909,6 @@ snapshots: browser-assert@1.2.1: {} - browserslist@4.24.5: - dependencies: - caniuse-lite: 1.0.30001718 - electron-to-chromium: 1.5.155 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.5) - browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001727 @@ -16738,14 +16014,6 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - call-bind@1.0.8: dependencies: call-bind-apply-helpers: 1.0.2 @@ -16775,12 +16043,10 @@ snapshots: caniuse-api@3.0.0: dependencies: browserslist: 4.25.1 - caniuse-lite: 1.0.30001718 + caniuse-lite: 1.0.30001727 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001718: {} - caniuse-lite@1.0.30001727: {} canonicalize@1.0.8: {} @@ -16806,19 +16072,13 @@ snapshots: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.1.3 + loupe: 3.1.4 pathval: 2.0.0 chalk-template@1.1.0: dependencies: chalk: 5.4.1 - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - chalk@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -16843,7 +16103,7 @@ snapshots: character-parser@2.2.0: dependencies: - is-regex: 1.1.4 + is-regex: 1.2.1 chart.js@4.5.0: dependencies: @@ -16879,18 +16139,18 @@ snapshots: css-what: 6.1.0 domelementtype: 2.3.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 cheerio@1.0.0: dependencies: cheerio-select: 2.1.0 dom-serializer: 2.0.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 encoding-sniffer: 0.2.0 htmlparser2: 9.1.0 parse5: 7.3.0 - parse5-htmlparser2-tree-adapter: 7.0.0 + parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 undici: 6.19.8 whatwg-mimetype: 4.0.0 @@ -16985,16 +16245,10 @@ snapshots: collect-v8-coverage@1.0.1: {} - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} color-string@1.9.1: @@ -17076,8 +16330,8 @@ snapshots: constantinople@4.0.1: dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.25.6 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 content-disposition@0.5.4: dependencies: @@ -17187,7 +16441,7 @@ snapshots: boolbase: 1.0.0 css-what: 6.1.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 nth-check: 2.1.1 css-tree@2.2.1: @@ -17319,36 +16573,18 @@ snapshots: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 - data-view-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - data-view-byte-length@1.0.2: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - data-view-byte-offset@1.0.1: dependencies: call-bound: 1.0.4 @@ -17436,23 +16672,23 @@ snapshots: deep-equal@2.2.0: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 es-get-iterator: 1.1.3 get-intrinsic: 1.3.0 is-arguments: 1.1.1 - is-array-buffer: 3.0.4 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 + is-array-buffer: 3.0.5 + is-date-object: 1.1.0 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 isarray: 2.0.5 object-is: 1.1.5 object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 + object.assign: 4.1.7 + regexp.prototype.flags: 1.5.4 side-channel: 1.1.0 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.15 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 deep-extend@0.6.0: optional: true @@ -17570,12 +16806,6 @@ snapshots: domelementtype: 2.3.0 domhandler: 4.3.1 - domutils@3.1.0: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -17612,8 +16842,6 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.155: {} - electron-to-chromium@1.5.186: {} emittery@0.13.1: {} @@ -17658,55 +16886,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.23.3: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.3.0 - get-symbol-description: 1.0.2 - globalthis: 1.0.3 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -17770,15 +16949,15 @@ snapshots: es-get-iterator@1.1.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 get-intrinsic: 1.3.0 has-symbols: 1.1.0 is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 - is-string: 1.0.7 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.1.1 isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 + stop-iteration-iterator: 1.1.0 es-module-lexer@1.7.0: {} @@ -17793,25 +16972,15 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 - es-shim-unscopables@1.1.0: dependencies: hasown: 2.0.2 - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - es-to-primitive@1.3.0: dependencies: is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 + is-date-object: 1.1.0 + is-symbol: 1.1.1 es-toolkit@1.27.0: {} @@ -17830,34 +16999,6 @@ snapshots: transitivePeerDependencies: - supports-color - esbuild@0.25.4: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.4 - '@esbuild/android-arm': 0.25.4 - '@esbuild/android-arm64': 0.25.4 - '@esbuild/android-x64': 0.25.4 - '@esbuild/darwin-arm64': 0.25.4 - '@esbuild/darwin-x64': 0.25.4 - '@esbuild/freebsd-arm64': 0.25.4 - '@esbuild/freebsd-x64': 0.25.4 - '@esbuild/linux-arm': 0.25.4 - '@esbuild/linux-arm64': 0.25.4 - '@esbuild/linux-ia32': 0.25.4 - '@esbuild/linux-loong64': 0.25.4 - '@esbuild/linux-mips64el': 0.25.4 - '@esbuild/linux-ppc64': 0.25.4 - '@esbuild/linux-riscv64': 0.25.4 - '@esbuild/linux-s390x': 0.25.4 - '@esbuild/linux-x64': 0.25.4 - '@esbuild/netbsd-arm64': 0.25.4 - '@esbuild/netbsd-x64': 0.25.4 - '@esbuild/openbsd-arm64': 0.25.4 - '@esbuild/openbsd-x64': 0.25.4 - '@esbuild/sunos-x64': 0.25.4 - '@esbuild/win32-arm64': 0.25.4 - '@esbuild/win32-ia32': 0.25.4 - '@esbuild/win32-x64': 0.25.4 - esbuild@0.25.6: optionalDependencies: '@esbuild/aix-ppc64': 0.25.6 @@ -17917,12 +17058,12 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7(supports-color@8.1.1) - is-core-module: 2.15.1 + is-core-module: 2.16.1 resolve: 1.22.8 transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: @@ -17945,24 +17086,24 @@ snapshots: eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0): dependencies: '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 9.31.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.34.0(eslint@9.31.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@9.31.0) hasown: 2.0.2 - is-core-module: 2.15.1 + is-core-module: 2.16.1 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 object.groupby: 1.0.3 - object.values: 1.2.0 + object.values: 1.2.1 semver: 6.3.1 - string.prototype.trimend: 1.0.8 + string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: '@typescript-eslint/parser': 8.34.0(eslint@9.31.0)(typescript@5.8.3) @@ -18022,8 +17163,6 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.0: {} - eslint-visitor-keys@4.2.1: {} eslint@9.31.0: @@ -18459,19 +17598,10 @@ snapshots: optionalDependencies: debug: 4.4.1(supports-color@10.0.0) - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - for-each@0.3.5: dependencies: is-callable: 1.2.7 - foreground-child@3.1.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -18547,13 +17677,6 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - functions-have-names: 1.2.3 - function.prototype.name@1.1.8: dependencies: call-bind: 1.0.8 @@ -18615,22 +17738,12 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.10.0: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -18656,22 +17769,13 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.1.1 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 - glob@11.0.2: - dependencies: - foreground-child: 3.1.1 - jackspeak: 4.0.1 - minimatch: 10.0.3 - minipass: 7.1.2 - package-json-from-dist: 1.0.0 - path-scurry: 2.0.0 - glob@11.0.3: dependencies: foreground-child: 3.3.1 @@ -18708,10 +17812,6 @@ snapshots: globals@16.3.0: {} - globalthis@1.0.3: - dependencies: - define-properties: 1.2.1 - globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -18761,7 +17861,7 @@ snapshots: lowercase-keys: 3.0.0 p-cancelable: 4.0.1 responselike: 3.0.0 - type-fest: 4.26.1 + type-fest: 4.41.0 graceful-fs@4.2.11: {} @@ -18795,8 +17895,6 @@ snapshots: dependencies: es-define-property: 1.0.1 - has-proto@1.0.3: {} - has-proto@1.2.0: dependencies: dunder-proto: 1.0.1 @@ -18887,14 +17985,14 @@ snapshots: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 entities: 4.5.0 htmlparser2@9.1.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 entities: 4.5.0 http-cache-semantics@4.1.1: {} @@ -19032,12 +18130,6 @@ snapshots: install-artifact-from-github@1.4.0: {} - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -19079,14 +18171,9 @@ snapshots: is-arguments@1.1.1: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 has-tostringtag: 1.0.2 - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.3.0 - is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -19105,19 +18192,10 @@ snapshots: has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 - is-bigint@1.1.0: dependencies: has-bigints: 1.0.2 - is-boolean-object@1.1.2: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -19125,28 +18203,16 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.15.1: - dependencies: - hasown: 2.0.2 - is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - is-data-view@1.0.2: dependencies: call-bound: 1.0.4 get-intrinsic: 1.3.0 is-typed-array: 1.1.15 - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.2 - is-date-object@1.1.0: dependencies: call-bound: 1.0.4 @@ -19184,18 +18250,12 @@ snapshots: global-dirs: 3.0.1 is-path-inside: 3.0.3 - is-map@2.0.2: {} - is-map@2.0.3: {} is-negative-zero@2.0.3: {} is-node-process@1.2.0: {} - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -19215,11 +18275,6 @@ snapshots: is-promise@2.2.2: {} - is-regex@1.1.4: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -19227,14 +18282,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - is-set@2.0.2: {} - is-set@2.0.3: {} - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 - is-shared-array-buffer@1.0.4: dependencies: call-bound: 1.0.4 @@ -19245,10 +18294,6 @@ snapshots: is-stream@4.0.1: {} - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -19258,20 +18303,12 @@ snapshots: dependencies: fast-xml-parser: 4.5.0 - is-symbol@1.0.4: - dependencies: - has-symbols: 1.1.0 - is-symbol@1.1.1: dependencies: call-bound: 1.0.4 has-symbols: 1.1.0 safe-regex-test: 1.1.0 - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.19 @@ -19282,23 +18319,12 @@ snapshots: is-unicode-supported@2.1.0: {} - is-weakmap@2.0.1: {} - is-weakmap@2.0.2: {} - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.7 - is-weakref@1.1.1: dependencies: call-bound: 1.0.4 - is-weakset@2.0.2: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.3.0 - is-weakset@2.0.4: dependencies: call-bound: 1.0.4 @@ -19323,7 +18349,7 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.24.7 - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -19333,7 +18359,7 @@ snapshots: istanbul-lib-instrument@6.0.0: dependencies: '@babel/core': 7.24.7 - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.2 @@ -19375,12 +18401,6 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jackspeak@4.0.1: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jackspeak@4.1.1: dependencies: '@isaacs/cliui': 8.0.2 @@ -19582,7 +18602,7 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 @@ -19595,7 +18615,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.15.31 + '@types/node': 22.16.4 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -19684,7 +18704,7 @@ snapshots: '@babel/generator': 7.24.7 '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.7) '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.7) - '@babel/types': 7.25.6 + '@babel/types': 7.28.1 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 @@ -19917,7 +18937,7 @@ snapshots: juice@11.0.1: dependencies: - cheerio: 1.0.0 + cheerio: 1.1.0 commander: 12.1.0 entities: 4.5.0 mensch: 0.3.4 @@ -20033,8 +19053,6 @@ snapshots: longest-streak@3.1.0: {} - loupe@3.1.3: {} - loupe@3.1.4: {} lowercase-keys@3.0.0: {} @@ -20067,8 +19085,8 @@ snapshots: magicast@0.3.5: dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 source-map-js: 1.2.1 mailcheck@1.1.1: {} @@ -20668,8 +19686,6 @@ snapshots: nanoid@3.3.11: {} - nanoid@3.3.8: {} - nanoid@5.1.5: {} napi-build-utils@2.0.0: @@ -20683,7 +19699,7 @@ snapshots: dependencies: debug: 3.2.7(supports-color@8.1.1) iconv-lite: 0.4.24 - sax: 1.2.4 + sax: 1.4.1 transitivePeerDependencies: - supports-color @@ -20709,14 +19725,9 @@ snapshots: just-extend: 6.2.0 path-to-regexp: 8.2.0 - node-abi@3.74.0: - dependencies: - semver: 7.7.2 - node-abi@3.75.0: dependencies: semver: 7.7.2 - optional: true node-abort-controller@3.1.1: {} @@ -20762,7 +19773,7 @@ snapshots: proc-log: 5.0.0 semver: 7.7.2 tar: 7.4.3 - tinyglobby: 0.2.13 + tinyglobby: 0.2.14 which: 5.0.0 transitivePeerDependencies: - supports-color @@ -20815,7 +19826,7 @@ snapshots: normalize-package-data@3.0.3: dependencies: hosted-git-info: 4.1.0 - is-core-module: 2.15.1 + is-core-module: 2.16.1 semver: 7.7.2 validate-npm-package-license: 3.0.4 @@ -20873,18 +19884,11 @@ snapshots: object-is@1.1.5: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 object-keys@1.1.1: {} - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - object.assign@4.1.7: dependencies: call-bind: 1.0.8 @@ -20896,22 +19900,16 @@ snapshots: object.fromentries@2.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 + es-abstract: 1.24.0 es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.3 - - object.values@1.2.0: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-abstract: 1.24.0 object.values@1.2.1: dependencies: @@ -21040,7 +20038,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.24.7 + '@babel/code-frame': 7.27.1 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -21059,11 +20057,6 @@ snapshots: dependencies: parse5: 6.0.1 - parse5-htmlparser2-tree-adapter@7.0.0: - dependencies: - domhandler: 5.0.3 - parse5: 7.3.0 - parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 @@ -21392,12 +20385,6 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.3: - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -21691,7 +20678,7 @@ snapshots: dependencies: '@babel/core': 7.24.7 '@babel/traverse': 7.24.7 - '@babel/types': 7.25.6 + '@babel/types': 7.28.1 '@types/babel__core': 7.20.0 '@types/babel__traverse': 7.20.0 '@types/doctrine': 0.0.9 @@ -21811,13 +20798,6 @@ snapshots: dependencies: regex-utilities: 2.3.0 - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 - regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -21895,7 +20875,7 @@ snapshots: resolve@1.22.8: dependencies: - is-core-module: 2.15.1 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -21971,13 +20951,6 @@ snapshots: dependencies: tslib: 2.8.1 - safe-array-concat@1.1.2: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -21995,12 +20968,6 @@ snapshots: es-errors: 1.3.0 isarray: 2.0.5 - safe-regex-test@1.0.3: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 - safe-regex-test@1.1.0: dependencies: call-bound: 1.0.4 @@ -22022,7 +20989,7 @@ snapshots: htmlparser2: 8.0.1 is-plain-object: 5.0.0 parse-srcset: 1.0.2 - postcss: 8.5.3 + postcss: 8.5.6 sass@1.89.2: dependencies: @@ -22032,8 +20999,6 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.0 - sax@1.2.4: {} - sax@1.4.1: {} saxes@6.0.0: @@ -22462,10 +21427,6 @@ snapshots: std-env@3.9.0: {} - stop-iteration-iterator@1.0.0: - dependencies: - internal-slot: 1.0.7 - stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -22549,19 +21510,6 @@ snapshots: es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.1.1 - - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - string.prototype.trimend@1.0.9: dependencies: call-bind: 1.0.8 @@ -22571,7 +21519,7 @@ snapshots: string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 es-object-atoms: 1.1.1 @@ -22806,11 +21754,6 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.13: - dependencies: - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 - tinyglobby@0.2.14: dependencies: fdir: 6.4.4(picomatch@4.0.2) @@ -22838,8 +21781,6 @@ snapshots: to-data-view@1.1.0: {} - to-fast-properties@2.0.0: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -22898,7 +21839,7 @@ snapshots: dependencies: chokidar: 4.0.3 commander: 9.5.0 - get-tsconfig: 4.10.0 + get-tsconfig: 4.10.1 globby: 11.1.0 mylas: 2.1.13 normalize-path: 3.0.0 @@ -22958,8 +21899,6 @@ snapshots: type-fest@2.19.0: {} - type-fest@4.26.1: {} - type-fest@4.41.0: {} type-is@1.6.18: @@ -22967,66 +21906,34 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 - typed-array-buffer@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-typed-array: 1.1.15 - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.2.0 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - typed-array-byte-length@1.0.3: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.2.0 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.6: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.2.0 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 - typed-array-length@1.0.7: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 possible-typed-array-names: 1.0.0 @@ -23072,13 +21979,6 @@ snapshots: ulid@2.4.0: {} - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.1.0 - which-boxed-primitive: 1.0.2 - unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -23165,12 +22065,6 @@ snapshots: untildify@4.0.0: {} - update-browserslist-db@1.1.3(browserslist@4.24.5): - dependencies: - browserslist: 4.24.5 - escalade: 3.2.0 - picocolors: 1.1.1 - update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: browserslist: 4.25.1 @@ -23200,8 +22094,8 @@ snapshots: inherits: 2.0.4 is-arguments: 1.1.1 is-generator-function: 1.0.10 - is-typed-array: 1.1.13 - which-typed-array: 1.1.15 + is-typed-array: 1.1.15 + which-typed-array: 1.1.19 utils-merge@1.0.1: {} @@ -23222,7 +22116,7 @@ snapshots: v8-to-istanbul@9.2.0: dependencies: '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 valid-data-url@3.0.1: {} @@ -23276,12 +22170,12 @@ snapshots: vite@6.3.5(@types/node@22.16.4)(sass@1.89.2)(terser@5.43.1)(tsx@4.20.3): dependencies: - esbuild: 0.25.4 + esbuild: 0.25.6 fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 - postcss: 8.5.3 + postcss: 8.5.6 rollup: 4.45.1 - tinyglobby: 0.2.13 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 22.16.4 fsevents: 2.3.3 @@ -23371,7 +22265,7 @@ snapshots: vue-component-type-helpers@2.2.12: {} - vue-component-type-helpers@3.0.1: {} + vue-component-type-helpers@3.0.3: {} vue-demi@0.14.7(vue@3.5.17(typescript@5.8.3)): dependencies: @@ -23379,9 +22273,9 @@ snapshots: vue-docgen-api@4.75.1(vue@3.5.17(typescript@5.8.3)): dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.25.6 - '@vue/compiler-dom': 3.5.13 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + '@vue/compiler-dom': 3.5.17 '@vue/compiler-sfc': 3.5.17 ast-types: 0.16.1 hash-sum: 2.0.0 @@ -23507,14 +22401,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -23539,13 +22425,6 @@ snapshots: which-collection: 1.0.2 which-typed-array: 1.1.19 - which-collection@1.0.1: - dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 - which-collection@1.0.2: dependencies: is-map: 2.0.3 @@ -23555,14 +22434,6 @@ snapshots: which-module@2.0.0: {} - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 @@ -23597,8 +22468,8 @@ snapshots: with@7.0.2: dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.25.6 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 @@ -23638,7 +22509,7 @@ snapshots: xml-js@1.6.11: dependencies: - sax: 1.2.4 + sax: 1.4.1 xml-name-validator@4.0.0: {} @@ -23646,7 +22517,7 @@ snapshots: xml2js@0.5.0: dependencies: - sax: 1.2.4 + sax: 1.4.1 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} From db67e81b99afef8402401ef615932c8a19419ba9 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 19:53:41 +0900 Subject: [PATCH 68/85] Reapply "actions fail invest: skip oauth test" This reverts commit 90c4551dfe457d3735e64e27995ab0ad3fb26f4a. --- packages/backend/test/e2e/oauth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index f639f90ea6..b9718bcec4 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -152,7 +152,7 @@ async function assertDirectError(response: Response, status: number, error: stri assert.strictEqual(data.error, error); } -describe('OAuth', () => { +describe.skip('OAuth', () => { let fastify: FastifyInstance; let alice: misskey.entities.SignupResponse; From 1c0344630409bb99e820d50323a1ad2812761087 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 21:31:41 +0900 Subject: [PATCH 69/85] actions fail invest: log fastify.closed --- packages/backend/test/e2e/oauth.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index b9718bcec4..f3c6d5a97c 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -152,7 +152,7 @@ async function assertDirectError(response: Response, status: number, error: stri assert.strictEqual(data.error, error); } -describe.skip('OAuth', () => { +describe('OAuth', () => { let fastify: FastifyInstance; let alice: misskey.entities.SignupResponse; @@ -183,7 +183,9 @@ describe.skip('OAuth', () => { }); afterAll(async () => { + console.log('closing fastify...'); await fastify.close(); + console.log('fastify closed.'); }); test('Full flow', async () => { From 89d919e4a4c428d023b15c0c26b16bd5bf162382 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 22:10:29 +0900 Subject: [PATCH 70/85] Revert "actions fail invest: log fastify.closed" This reverts commit 1c0344630409bb99e820d50323a1ad2812761087. --- packages/backend/test/e2e/oauth.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index f3c6d5a97c..b9718bcec4 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -152,7 +152,7 @@ async function assertDirectError(response: Response, status: number, error: stri assert.strictEqual(data.error, error); } -describe('OAuth', () => { +describe.skip('OAuth', () => { let fastify: FastifyInstance; let alice: misskey.entities.SignupResponse; @@ -183,9 +183,7 @@ describe('OAuth', () => { }); afterAll(async () => { - console.log('closing fastify...'); await fastify.close(); - console.log('fastify closed.'); }); test('Full flow', async () => { From 2cc46fc331aa00c48cba3f985f821e0bd3eb6cbc Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 22:10:41 +0900 Subject: [PATCH 71/85] actions fail invest: log fastify.closed --- packages/backend/test-server/entry.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts index 7e6078856c..03fd309953 100644 --- a/packages/backend/test-server/entry.ts +++ b/packages/backend/test-server/entry.ts @@ -78,9 +78,9 @@ async function startControllerEndpoints(port = config.port + 1000) { process.env = JSON.parse(originEnv); await serverService.dispose(); + console.log('ServerService application closed.'); await app.close(); - - console.log('Nest application closed.'); + console.log('MainModule application closed.'); await killTestServer(); From f81cc413ad39969245e00aea2ead5c2b45f71427 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 22:58:52 +0900 Subject: [PATCH 72/85] Revert "actions fail invest: log fastify.closed" This reverts commit 2cc46fc331aa00c48cba3f985f821e0bd3eb6cbc. --- packages/backend/test-server/entry.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts index 03fd309953..7e6078856c 100644 --- a/packages/backend/test-server/entry.ts +++ b/packages/backend/test-server/entry.ts @@ -78,9 +78,9 @@ async function startControllerEndpoints(port = config.port + 1000) { process.env = JSON.parse(originEnv); await serverService.dispose(); - console.log('ServerService application closed.'); await app.close(); - console.log('MainModule application closed.'); + + console.log('Nest application closed.'); await killTestServer(); From ef6e3ca2ad260dc7ca5ed521d51f9e6aeda92f44 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 23:07:14 +0900 Subject: [PATCH 73/85] Reapply "actions fail invest: log fastify.closed" This reverts commit f81cc413ad39969245e00aea2ead5c2b45f71427. --- packages/backend/test-server/entry.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts index 7e6078856c..03fd309953 100644 --- a/packages/backend/test-server/entry.ts +++ b/packages/backend/test-server/entry.ts @@ -78,9 +78,9 @@ async function startControllerEndpoints(port = config.port + 1000) { process.env = JSON.parse(originEnv); await serverService.dispose(); + console.log('ServerService application closed.'); await app.close(); - - console.log('Nest application closed.'); + console.log('MainModule application closed.'); await killTestServer(); From 382d6567a5255e669909409d0a58beb042cd83e9 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 23:07:34 +0900 Subject: [PATCH 74/85] Revert "Reapply "actions fail invest: skip oauth test"" This reverts commit db67e81b99afef8402401ef615932c8a19419ba9. --- packages/backend/test/e2e/oauth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index b9718bcec4..f639f90ea6 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -152,7 +152,7 @@ async function assertDirectError(response: Response, status: number, error: stri assert.strictEqual(data.error, error); } -describe.skip('OAuth', () => { +describe('OAuth', () => { let fastify: FastifyInstance; let alice: misskey.entities.SignupResponse; From 761c19adbe3303aae00afe7ee436843ee0fd909a Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 23:13:42 +0900 Subject: [PATCH 75/85] =?UTF-8?q?beforeEach=20=E2=86=92=20beforeAll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/test/unit/ApDeliverManagerService.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/backend/test/unit/ApDeliverManagerService.ts b/packages/backend/test/unit/ApDeliverManagerService.ts index c92fc71ac7..2471f934bb 100644 --- a/packages/backend/test/unit/ApDeliverManagerService.ts +++ b/packages/backend/test/unit/ApDeliverManagerService.ts @@ -53,7 +53,7 @@ describe('ApDeliverManagerService', () => { }, }; - beforeEach(async () => { + beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ ApDeliverManagerService, @@ -91,10 +91,6 @@ describe('ApDeliverManagerService', () => { apLoggerService = module.get(ApLoggerService); }); - afterEach(() => { - jest.clearAllMocks(); - }); - describe('deliverToFollowers', () => { it('should deliver activity to all followers', async () => { const mockFollowings = [ From f18e44f6fa531f109eb61fd02262c7d8bcd47b58 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 23:21:00 +0900 Subject: [PATCH 76/85] clearAllMocks --- packages/backend/test/unit/ApDeliverManagerService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/backend/test/unit/ApDeliverManagerService.ts b/packages/backend/test/unit/ApDeliverManagerService.ts index 2471f934bb..26cec66b63 100644 --- a/packages/backend/test/unit/ApDeliverManagerService.ts +++ b/packages/backend/test/unit/ApDeliverManagerService.ts @@ -91,6 +91,10 @@ describe('ApDeliverManagerService', () => { apLoggerService = module.get(ApLoggerService); }); + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('deliverToFollowers', () => { it('should deliver activity to all followers', async () => { const mockFollowings = [ From 2960c2069e7ba230b19b56add1f5b4a85c237916 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 20 Jul 2025 23:28:55 +0900 Subject: [PATCH 77/85] =?UTF-8?q?=20beforeEach=20=E2=86=92=20beforeAll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/test/unit/UserSuspendService.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index 8d6b01b730..63e8f7aa00 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -79,7 +79,7 @@ describe('UserSuspendService', () => { return following; } - beforeEach(async () => { + beforeAll(async () => { app = await Test.createTestingModule({ imports: [GlobalModule], providers: [ @@ -131,13 +131,10 @@ describe('UserSuspendService', () => { globalEventService = app.get(GlobalEventService) as jest.Mocked; apRendererService = app.get(ApRendererService) as jest.Mocked; moderationLogService = app.get(ModerationLogService) as jest.Mocked; - - // Reset mocks - jest.clearAllMocks(); }); - afterEach(async () => { - await app.close(); + beforeEach(() => { + jest.clearAllMocks(); }); describe('suspend', () => { From 6aad9299dc7c7c24cf09182bfbfb298621af9de9 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 21 Jul 2025 00:43:31 +0900 Subject: [PATCH 78/85] actions fail invest: log SerrverService.dispose --- packages/backend/src/server/ServerService.ts | 3 +++ packages/backend/src/server/api/StreamingApiServerService.ts | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 23c085ee27..00e1ae3c49 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -305,8 +305,11 @@ export class ServerService implements OnApplicationShutdown { @bindThis public async dispose(): Promise { + console.log('Disposing ServerService...'); await this.streamingApiServerService.detach(); + this.logger.info('Streaming API server detached.'); await this.#fastify.close(); + this.logger.info('Fastify server closed.'); } /** diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 2a4e1fc574..7c80f015b1 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -174,9 +174,11 @@ export class StreamingApiServerService { if (this.#cleanConnectionsIntervalId) { clearInterval(this.#cleanConnectionsIntervalId); this.#cleanConnectionsIntervalId = null; + console.log('Clean connections interval cleared.'); } return new Promise((resolve) => { this.#wss.close(() => resolve()); + console.log('WebSocket server closed.'); }); } } From 449fadd15a129698d082f8d0a0dca3d099b049d7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 21 Jul 2025 00:58:20 +0900 Subject: [PATCH 79/85] log fix --- packages/backend/src/server/ServerService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 00e1ae3c49..3f03ddd78d 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -307,9 +307,9 @@ export class ServerService implements OnApplicationShutdown { public async dispose(): Promise { console.log('Disposing ServerService...'); await this.streamingApiServerService.detach(); - this.logger.info('Streaming API server detached.'); + console.log('Streaming API server detached.'); await this.#fastify.close(); - this.logger.info('Fastify server closed.'); + console.log('Fastify server closed.'); } /** From 03e404ce2720c74db909f3fdd9a3be49fb2a06a0 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 21 Jul 2025 01:23:30 +0900 Subject: [PATCH 80/85] Revert "log fix" This reverts commit 449fadd15a129698d082f8d0a0dca3d099b049d7. --- packages/backend/src/server/ServerService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 3f03ddd78d..00e1ae3c49 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -307,9 +307,9 @@ export class ServerService implements OnApplicationShutdown { public async dispose(): Promise { console.log('Disposing ServerService...'); await this.streamingApiServerService.detach(); - console.log('Streaming API server detached.'); + this.logger.info('Streaming API server detached.'); await this.#fastify.close(); - console.log('Fastify server closed.'); + this.logger.info('Fastify server closed.'); } /** From 634f16f6b0e62a1b3472cbe79a355b614e662697 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 21 Jul 2025 01:23:42 +0900 Subject: [PATCH 81/85] Revert "actions fail invest: log SerrverService.dispose" This reverts commit 6aad9299dc7c7c24cf09182bfbfb298621af9de9. --- packages/backend/src/server/ServerService.ts | 3 --- packages/backend/src/server/api/StreamingApiServerService.ts | 2 -- 2 files changed, 5 deletions(-) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 00e1ae3c49..23c085ee27 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -305,11 +305,8 @@ export class ServerService implements OnApplicationShutdown { @bindThis public async dispose(): Promise { - console.log('Disposing ServerService...'); await this.streamingApiServerService.detach(); - this.logger.info('Streaming API server detached.'); await this.#fastify.close(); - this.logger.info('Fastify server closed.'); } /** diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 7c80f015b1..2a4e1fc574 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -174,11 +174,9 @@ export class StreamingApiServerService { if (this.#cleanConnectionsIntervalId) { clearInterval(this.#cleanConnectionsIntervalId); this.#cleanConnectionsIntervalId = null; - console.log('Clean connections interval cleared.'); } return new Promise((resolve) => { this.#wss.close(() => resolve()); - console.log('WebSocket server closed.'); }); } } From be688922c871e64036739ea077a0f7d340384956 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 21 Jul 2025 01:26:05 +0900 Subject: [PATCH 82/85] remove console.log --- packages/backend/test-server/entry.ts | 4 ---- packages/backend/test/jest.setup.ts | 2 -- 2 files changed, 6 deletions(-) diff --git a/packages/backend/test-server/entry.ts b/packages/backend/test-server/entry.ts index 03fd309953..04bf62d209 100644 --- a/packages/backend/test-server/entry.ts +++ b/packages/backend/test-server/entry.ts @@ -74,13 +74,10 @@ async function startControllerEndpoints(port = config.port + 1000) { }); fastify.post<{ Body: { key?: string, value?: string } }>('/env-reset', async (req, res) => { - console.log('env-reset'); process.env = JSON.parse(originEnv); await serverService.dispose(); - console.log('ServerService application closed.'); await app.close(); - console.log('MainModule application closed.'); await killTestServer(); @@ -91,7 +88,6 @@ async function startControllerEndpoints(port = config.port + 1000) { }); serverService = app.get(ServerService); await serverService.launch(); - console.log('application launched.'); res.code(200).send({ success: true }); }); diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts index 159ffd429a..7c6dd6a55f 100644 --- a/packages/backend/test/jest.setup.ts +++ b/packages/backend/test/jest.setup.ts @@ -7,7 +7,5 @@ import { initTestDb, sendEnvResetRequest } from './utils.js'; beforeAll(async () => { await initTestDb(false); - console.log('Test database initialized.'); await sendEnvResetRequest(); - console.log('Environment reset completed.'); }); From 1cc61594421b81db0094b9790fdd34a3e4ff3f26 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 24 Jul 2025 00:40:05 +0900 Subject: [PATCH 83/85] fix unit test --- packages/backend/src/core/UserSuspendService.ts | 2 +- packages/backend/test/unit/UserSuspendService.ts | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 14bbb10919..7772845bc5 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -73,7 +73,7 @@ export class UserSuspendService { (async () => { await this.postUnsuspend(user, false).catch((e: any) => { }); - await this.restoreFollowings(user).catch((e: any) => { }); + await this.restoreFollowings(user).catch((e: any) => { console.error(e); }); })(); } diff --git a/packages/backend/test/unit/UserSuspendService.ts b/packages/backend/test/unit/UserSuspendService.ts index 2b0444d363..6d69a8e5e1 100644 --- a/packages/backend/test/unit/UserSuspendService.ts +++ b/packages/backend/test/unit/UserSuspendService.ts @@ -30,6 +30,7 @@ import { AccountUpdateService } from '@/core/AccountUpdateService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { RelayService } from '@/core/RelayService.js'; import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js'; +import { MiRemoteUser } from '@/models/User.js'; function genHost() { return randomString() + '.example.com'; @@ -96,6 +97,7 @@ describe('UserSuspendService', () => { useFactory: () => ({ isLocalUser: jest.fn(), genLocalUserUri: jest.fn(), + isSuspendedEither: jest.fn(), }), }, { @@ -244,6 +246,8 @@ describe('UserSuspendService', () => { }); test('should restore follower relationships', async () => { + userEntityService.isSuspendedEither.mockReturnValue(false); + const user = await createUser({ isSuspended: true }); const followee1 = await createUser(); const followee2 = await createUser(); @@ -286,6 +290,8 @@ describe('UserSuspendService', () => { describe('integration test: suspend and unsuspend cycle', () => { test('should preserve follow relationships through suspend/unsuspend cycle', async () => { + userEntityService.isSuspendedEither.mockReturnValue(false); + const user = await createUser(); const followee1 = await createUser(); const followee2 = await createUser(); @@ -441,7 +447,7 @@ describe('UserSuspendService', () => { describe('suspension from remote', () => { test('should suspend remote user and post suspend event', async () => { - const remoteUser = { id: secureRndstr(16), host: genHost() }; + const remoteUser = await createUser({ host: genHost() }) as MiRemoteUser; await userSuspendService.suspendFromRemote(remoteUser); // ユーザーがリモート凍結されているかチェック @@ -456,7 +462,7 @@ describe('UserSuspendService', () => { }); test('should unsuspend remote user and post unsuspend event', async () => { - const remoteUser = { id: secureRndstr(16), host: genHost() }; + const remoteUser = await createUser({ host: genHost(), isRemoteSuspended: true }) as MiRemoteUser; await userSuspendService.unsuspendFromRemote(remoteUser); // ユーザーのリモート凍結が解除されているかチェック From 665c9dc38dfad2d8f91291082f1adc3eb642ba0e Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 24 Jul 2025 02:53:54 +0900 Subject: [PATCH 84/85] :v: --- .../backend/src/core/UserSuspendService.ts | 12 +++- .../activitypub/models/ApPersonService.ts | 4 +- .../server/api/endpoints/users/followers.ts | 4 +- .../test/user-suspension.test.ts | 65 +++++++------------ 4 files changed, 37 insertions(+), 48 deletions(-) diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 7772845bc5..3d109e0a40 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -56,7 +56,10 @@ export class UserSuspendService { isRemoteSuspended: true, }); - this.postSuspend(user, true); + (async () => { + await this.postSuspend(user, true).catch((e: any) => { }); + await this.suspendFollowings(user).catch((e: any) => { }); + })(); } @bindThis @@ -73,7 +76,7 @@ export class UserSuspendService { (async () => { await this.postUnsuspend(user, false).catch((e: any) => { }); - await this.restoreFollowings(user).catch((e: any) => { console.error(e); }); + await this.restoreFollowings(user).catch((e: any) => { }); })(); } @@ -83,7 +86,10 @@ export class UserSuspendService { isRemoteSuspended: false, }); - this.postUnsuspend(user, true); + (async () => { + await this.postUnsuspend(user, true).catch((e: any) => { }); + await this.restoreFollowings(user).catch((e: any) => { }); + })(); } @bindThis diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 4e548347f0..fdba16eab4 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -38,6 +38,7 @@ import { RoleService } from '@/core/RoleService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import type { AccountMoveService } from '@/core/AccountMoveService.js'; import { checkHttps } from '@/misc/check-https.js'; +import { UserSuspendService } from '@/core/UserSuspendService.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 +49,6 @@ import type { ApLoggerService } from '../ApLoggerService.js'; import type { ApImageService } from './ApImageService.js'; import type { IActor, ICollection, IObject, IOrderedCollection } from '../type.js'; -import { UserSuspendService } from '@/core/UserSuspendService.js'; const nameLength = 128; const summaryLength = 2048; @@ -606,10 +606,12 @@ export class ApPersonService implements OnModuleInit { //#region suspend if (exist.isRemoteSuspended === false && person.suspended === true) { // リモートサーバーでアカウントが凍結された + this.logger.info(`Remote User Suspended: acct=${exist.username}@${exist.host} id=${exist.id} uri=${exist.uri}`); this.userSuspendService.suspendFromRemote({ id: exist.id, host: exist.host }); } if (exist.isRemoteSuspended === true && person.suspended === false) { // リモートサーバーでアカウントが解凍された + this.logger.info(`Remote User Unsuspended: acct=${exist.username}@${exist.host} id=${exist.id} uri=${exist.uri}`); this.userSuspendService.unsuspendFromRemote({ id: exist.id, host: exist.host }); } //#endregion diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 3afba603a2..d2dd8cd744 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -137,13 +137,15 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('following.followeeId = :userId', { userId: user.id }) - .andWhere('following.isFollowerSuspended = false') + .andWhere('following.isFollowerSuspended = FALSE') .innerJoinAndSelect('following.follower', 'follower'); const followings = await query .limit(ps.limit) .getMany(); + console.log(followings); + return await this.followingEntityService.packMany(followings, me, { populateFollower: true }); }); } diff --git a/packages/backend/test-federation/test/user-suspension.test.ts b/packages/backend/test-federation/test/user-suspension.test.ts index e5d119d564..a2e6fb787d 100644 --- a/packages/backend/test-federation/test/user-suspension.test.ts +++ b/packages/backend/test-federation/test/user-suspension.test.ts @@ -35,13 +35,15 @@ describe('User Suspension', () => { await aAdmin.client.request('admin/suspend-user', { userId: alice.id }); await sleep(); - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 0); // no following relation + const aliceInBRaw = await bAdmin.client.request('admin/show-user', { userId: aliceInB.id }); + strictEqual(aliceInBRaw.isRemoteSuspended, true); + const renewedAliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(renewedAliceInB.isSuspended, true); await rejects( async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { - strictEqual(err.code, 'NO_SUCH_USER'); + strictEqual(err.code, 'ALREADY_FOLLOWING'); return true; }, ); @@ -51,35 +53,18 @@ describe('User Suspension', () => { await aAdmin.client.request('admin/unsuspend-user', { userId: alice.id }); await sleep(); - const followers = await alice.client.request('users/followers', { userId: alice.id }); - strictEqual(followers.length, 1); // FIXME: followers are not deleted?? + const aliceInBRenewed = await bAdmin.client.request('admin/show-user', { userId: aliceInB.id }); + strictEqual(aliceInBRenewed.isRemoteSuspended, false); - /** - * FIXME: still rejected! - * seems to can't process Undo Delete activity because it is not implemented - * related @see https://github.com/misskey-dev/misskey/issues/13273 - */ await rejects( async () => await bob.client.request('following/create', { userId: aliceInB.id }), (err: any) => { - strictEqual(err.code, 'NO_SUCH_USER'); - return true; - }, - ); - - // FIXME: resolving also fails - await rejects( - async () => await resolveRemoteUser('a.test', alice.id, bob), - (err: any) => { - strictEqual(err.code, 'INTERNAL_ERROR'); + strictEqual(err.code, 'ALREADY_FOLLOWING'); return true; }, ); }); - /** - * instead of simple unsuspension, let's tell existence by following from Alice - */ test('Alice can follow Bob', async () => { await alice.client.request('following/create', { userId: bobInA.id }); await sleep(); @@ -87,29 +72,23 @@ describe('User Suspension', () => { const bobFollowers = await bob.client.request('users/followers', { userId: bob.id }); strictEqual(bobFollowers.length, 1); // followed by Alice assert(bobFollowers[0].follower != null); - const renewedaliceInB = bobFollowers[0].follower; - assert(aliceInB.username === renewedaliceInB.username); - assert(aliceInB.host === renewedaliceInB.host); - assert(aliceInB.id !== renewedaliceInB.id); // TODO: Same username and host, but their ids are different! Is it OK? + const renewedAliceInB = bobFollowers[0].follower; + assert(aliceInB.username === renewedAliceInB.username); + assert(aliceInB.host === renewedAliceInB.host); + assert(aliceInB.id === renewedAliceInB.id); + }); - const following = await bob.client.request('users/following', { userId: bob.id }); - strictEqual(following.length, 0); // following are deleted + test('Alice follows Bob, and Alice gets suspended, the following relation hidden', async () => { + await aAdmin.client.request('admin/suspend-user', { userId: alice.id }); + await sleep(1000); - // Bob tries to follow Alice - await bob.client.request('following/create', { userId: renewedaliceInB.id }); - await sleep(); + const renewedAliceInB = await bob.client.request('users/show', { userId: aliceInB.id }); + strictEqual(renewedAliceInB.isSuspended, true); + const aliceInBRaw = await bAdmin.client.request('admin/show-user', { userId: aliceInB.id }); + strictEqual(aliceInBRaw.isRemoteSuspended, true); - const aliceFollowers = await alice.client.request('users/followers', { userId: alice.id }); - strictEqual(aliceFollowers.length, 1); - - // FIXME: but resolving still fails ... - await rejects( - async () => await resolveRemoteUser('a.test', alice.id, bob), - (err: any) => { - strictEqual(err.code, 'INTERNAL_ERROR'); - return true; - }, - ); + const bobFollowers = await bob.client.request('users/followers', { userId: bob.id }); + strictEqual(bobFollowers.length, 0); // Relation is hidden }); }); }); From b55b48d57e93def205d4c5c3b9387fdf4c2557d7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 24 Jul 2025 10:12:14 +0900 Subject: [PATCH 85/85] update AccountUpdateService --- packages/backend/src/core/AccountUpdateService.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 74f7f0fcab..bdbcc3fd1f 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -37,9 +37,12 @@ export class AccountUpdateService { @bindThis public async publishToFollowers(userId: MiUser['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); - if (user == null) throw new Error('user not found'); + if (user == null || user.isDeleted) { + // ユーザーが存在しない、または削除されている場合は何もしない + return; + } - // 投稿者がローカルユーザーならUpdateを配信 + // ローカルユーザーならUpdateを配信 if (this.userEntityService.isLocalUser(user)) { const content = await this.createUpdatePersonActivity(user); this.apDeliverManagerService.deliverToFollowers(user, content); @@ -50,9 +53,12 @@ export class AccountUpdateService { @bindThis async publishToFollowersAndSharedInboxAndRelays(userId: MiUser['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); - if (user == null) throw new Error('user not found'); + if (user == null || user.isDeleted) { + // ユーザーが存在しない、または削除されている場合は何もしない + return; + } - // 投稿者がローカルユーザーならUpdateを配信 + // ローカルユーザーならUpdateを配信 if (this.userEntityService.isLocalUser(user)) { const content = await this.createUpdatePersonActivity(user); const manager = this.apDeliverManagerService.createDeliverManager(user, content);