summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormisskey-release-bot[bot] <157398866+misskey-release-bot[bot]@users.noreply.github.com>2026-03-05 10:56:50 +0000
committerGitHub <noreply@github.com>2026-03-05 10:56:50 +0000
commitfe3dd8edb5f30104cd0a7ed755eb254feda2922d (patch)
treeaf6cf5fa4ca75302ac2de5db742cead00bc13d21
parentMerge pull request #16998 from misskey-dev/develop (diff)
parentRelease: 2026.3.0 (diff)
downloadmisskey-fe3dd8edb5f30104cd0a7ed755eb254feda2922d.tar.gz
misskey-fe3dd8edb5f30104cd0a7ed755eb254feda2922d.tar.bz2
misskey-fe3dd8edb5f30104cd0a7ed755eb254feda2922d.zip
Merge pull request #17217 from misskey-dev/develop
Release: 2026.3.0
-rw-r--r--.dockerignore1
-rw-r--r--.github/ISSUE_TEMPLATE/01_bug-report.yml4
-rw-r--r--.github/copilot-instructions.md3
-rw-r--r--.github/workflows/dockle.yml39
-rw-r--r--.github/workflows/report-backend-memory.yml127
-rw-r--r--.github/workflows/test-backend.yml7
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.md45
-rw-r--r--COPYING2
-rw-r--r--Dockerfile5
-rw-r--r--README.md12
-rw-r--r--locales/ar-SA.yml8
-rw-r--r--locales/bn-BD.yml8
-rw-r--r--locales/ca-ES.yml62
-rw-r--r--locales/cs-CZ.yml8
-rw-r--r--locales/de-DE.yml220
-rw-r--r--locales/en-US.yml52
-rw-r--r--locales/es-ES.yml66
-rw-r--r--locales/fr-FR.yml24
-rw-r--r--locales/id-ID.yml8
-rw-r--r--locales/it-IT.yml170
-rw-r--r--locales/ja-JP.yml44
-rw-r--r--locales/ja-KS.yml9
-rw-r--r--locales/ko-KR.yml64
-rw-r--r--locales/lo-LA.yml2
-rw-r--r--locales/nl-NL.yml2
-rw-r--r--locales/no-NO.yml4
-rw-r--r--locales/pl-PL.yml8
-rw-r--r--locales/pt-PT.yml10
-rw-r--r--locales/ro-RO.yml6
-rw-r--r--locales/ru-RU.yml131
-rw-r--r--locales/sk-SK.yml8
-rw-r--r--locales/sv-SE.yml3
-rw-r--r--locales/th-TH.yml164
-rw-r--r--locales/tr-TR.yml10
-rw-r--r--locales/uk-UA.yml8
-rw-r--r--locales/uz-UZ.yml6
-rw-r--r--locales/vi-VN.yml8
-rw-r--r--locales/zh-CN.yml160
-rw-r--r--locales/zh-TW.yml68
-rw-r--r--package.json51
-rw-r--r--packages/backend/assets/misc/bios.js2
-rw-r--r--packages/backend/build.js121
-rw-r--r--packages/backend/eslint.config.js1
-rw-r--r--packages/backend/migration/1767169026317-birthday-index.js20
-rw-r--r--packages/backend/ormconfig.js4
-rw-r--r--packages/backend/package.json134
-rw-r--r--packages/backend/scripts/check_connect.js30
-rw-r--r--packages/backend/scripts/generate_api_json.js6
-rw-r--r--packages/backend/scripts/measure-memory.mjs163
-rw-r--r--packages/backend/scripts/watch.mjs2
-rw-r--r--packages/backend/src/boot/entry.ts12
-rw-r--r--packages/backend/src/boot/master.ts19
-rw-r--r--packages/backend/src/config.ts34
-rw-r--r--packages/backend/src/core/AccountMoveService.ts2
-rw-r--r--packages/backend/src/core/AnnouncementService.ts2
-rw-r--r--packages/backend/src/core/AvatarDecorationService.ts2
-rw-r--r--packages/backend/src/core/CoreModule.ts4
-rw-r--r--packages/backend/src/core/EmailService.ts2
-rw-r--r--packages/backend/src/core/FileInfoService.ts28
-rw-r--r--packages/backend/src/core/GlobalEventService.ts6
-rw-r--r--packages/backend/src/core/MfmService.ts8
-rw-r--r--packages/backend/src/core/NoteDraftService.ts4
-rw-r--r--packages/backend/src/core/QueueService.ts16
-rw-r--r--packages/backend/src/core/RoleService.ts2
-rw-r--r--packages/backend/src/core/SearchService.ts3
-rw-r--r--packages/backend/src/core/UserSuspendService.ts6
-rw-r--r--packages/backend/src/core/UtilityService.ts2
-rw-r--r--packages/backend/src/core/activitypub/ApInboxService.ts14
-rw-r--r--packages/backend/src/core/activitypub/ApRendererService.ts2
-rw-r--r--packages/backend/src/core/activitypub/ApRequestService.ts2
-rw-r--r--packages/backend/src/core/activitypub/ApResolverService.ts79
-rw-r--r--packages/backend/src/core/activitypub/models/ApImageService.ts2
-rw-r--r--packages/backend/src/core/activitypub/models/ApNoteService.ts2
-rw-r--r--packages/backend/src/core/activitypub/models/ApPersonService.ts8
-rw-r--r--packages/backend/src/core/activitypub/models/ApQuestionService.ts4
-rw-r--r--packages/backend/src/core/entities/ChatEntityService.ts2
-rw-r--r--packages/backend/src/core/entities/DriveFileEntityService.ts45
-rw-r--r--packages/backend/src/core/entities/DriveFolderEntityService.ts154
-rw-r--r--packages/backend/src/core/entities/EmojiEntityService.ts4
-rw-r--r--packages/backend/src/core/entities/MetaEntityService.ts4
-rw-r--r--packages/backend/src/core/entities/NoteReactionEntityService.ts4
-rw-r--r--packages/backend/src/core/entities/ReversiGameEntityService.ts8
-rw-r--r--packages/backend/src/core/entities/UserEntityService.ts2
-rw-r--r--packages/backend/src/misc/check-word-mute.ts2
-rw-r--r--packages/backend/src/misc/get-ip-hash.ts2
-rw-r--r--packages/backend/src/misc/i18n.ts2
-rw-r--r--packages/backend/src/misc/json-schema.ts4
-rw-r--r--packages/backend/src/misc/split-id-and-objects.ts27
-rw-r--r--packages/backend/src/misc/unique-by-key.ts21
-rw-r--r--packages/backend/src/models/AbuseReportNotificationRecipient.ts6
-rw-r--r--packages/backend/src/models/AbuseUserReport.ts6
-rw-r--r--packages/backend/src/models/AccessToken.ts4
-rw-r--r--packages/backend/src/models/Announcement.ts2
-rw-r--r--packages/backend/src/models/AnnouncementRead.ts4
-rw-r--r--packages/backend/src/models/Antenna.ts4
-rw-r--r--packages/backend/src/models/App.ts2
-rw-r--r--packages/backend/src/models/AuthSession.ts4
-rw-r--r--packages/backend/src/models/Blocking.ts4
-rw-r--r--packages/backend/src/models/BubbleGameRecord.ts2
-rw-r--r--packages/backend/src/models/Channel.ts4
-rw-r--r--packages/backend/src/models/ChannelFavorite.ts4
-rw-r--r--packages/backend/src/models/ChannelFollowing.ts4
-rw-r--r--packages/backend/src/models/ChannelMuting.ts4
-rw-r--r--packages/backend/src/models/ChatApproval.ts4
-rw-r--r--packages/backend/src/models/ChatMessage.ts8
-rw-r--r--packages/backend/src/models/ChatRoom.ts2
-rw-r--r--packages/backend/src/models/ChatRoomInvitation.ts4
-rw-r--r--packages/backend/src/models/ChatRoomMembership.ts4
-rw-r--r--packages/backend/src/models/Clip.ts2
-rw-r--r--packages/backend/src/models/ClipFavorite.ts4
-rw-r--r--packages/backend/src/models/ClipNote.ts4
-rw-r--r--packages/backend/src/models/DriveFile.ts4
-rw-r--r--packages/backend/src/models/DriveFolder.ts4
-rw-r--r--packages/backend/src/models/Flash.ts2
-rw-r--r--packages/backend/src/models/FlashLike.ts4
-rw-r--r--packages/backend/src/models/FollowRequest.ts4
-rw-r--r--packages/backend/src/models/Following.ts4
-rw-r--r--packages/backend/src/models/GalleryLike.ts4
-rw-r--r--packages/backend/src/models/GalleryPost.ts2
-rw-r--r--packages/backend/src/models/Meta.ts8
-rw-r--r--packages/backend/src/models/ModerationLog.ts2
-rw-r--r--packages/backend/src/models/Muting.ts4
-rw-r--r--packages/backend/src/models/Note.ts8
-rw-r--r--packages/backend/src/models/NoteDraft.ts8
-rw-r--r--packages/backend/src/models/NoteFavorite.ts4
-rw-r--r--packages/backend/src/models/NoteReaction.ts4
-rw-r--r--packages/backend/src/models/NoteThreadMuting.ts2
-rw-r--r--packages/backend/src/models/Page.ts4
-rw-r--r--packages/backend/src/models/PageLike.ts4
-rw-r--r--packages/backend/src/models/PasswordResetRequest.ts2
-rw-r--r--packages/backend/src/models/Poll.ts2
-rw-r--r--packages/backend/src/models/PollVote.ts4
-rw-r--r--packages/backend/src/models/PromoNote.ts2
-rw-r--r--packages/backend/src/models/PromoRead.ts4
-rw-r--r--packages/backend/src/models/RegistrationTicket.ts4
-rw-r--r--packages/backend/src/models/RegistryItem.ts2
-rw-r--r--packages/backend/src/models/RenoteMuting.ts4
-rw-r--r--packages/backend/src/models/ReversiGame.ts4
-rw-r--r--packages/backend/src/models/RoleAssignment.ts4
-rw-r--r--packages/backend/src/models/Signin.ts2
-rw-r--r--packages/backend/src/models/SwSubscription.ts2
-rw-r--r--packages/backend/src/models/SystemAccount.ts2
-rw-r--r--packages/backend/src/models/User.ts4
-rw-r--r--packages/backend/src/models/UserKeypair.ts2
-rw-r--r--packages/backend/src/models/UserList.ts2
-rw-r--r--packages/backend/src/models/UserListFavorite.ts4
-rw-r--r--packages/backend/src/models/UserListMembership.ts4
-rw-r--r--packages/backend/src/models/UserMemo.ts4
-rw-r--r--packages/backend/src/models/UserNotePining.ts4
-rw-r--r--packages/backend/src/models/UserProfile.ts4
-rw-r--r--packages/backend/src/models/UserPublickey.ts2
-rw-r--r--packages/backend/src/models/UserSecurityKey.ts2
-rw-r--r--packages/backend/src/models/Webhook.ts2
-rw-r--r--packages/backend/src/models/json-schema/meta.ts23
-rw-r--r--packages/backend/src/models/json-schema/reversi-game.ts2
-rw-r--r--packages/backend/src/models/json-schema/user.ts3
-rw-r--r--packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts4
-rw-r--r--packages/backend/src/queue/processors/PostScheduledNoteProcessorService.ts2
-rw-r--r--packages/backend/src/server/ActivityPubServerService.ts2
-rw-r--r--packages/backend/src/server/FileServerService.ts500
-rw-r--r--packages/backend/src/server/NodeinfoServerService.ts2
-rw-r--r--packages/backend/src/server/ServerModule.ts76
-rw-r--r--packages/backend/src/server/api/ApiCallService.ts2
-rw-r--r--packages/backend/src/server/api/SigninApiService.ts2
-rw-r--r--packages/backend/src/server/api/SigninWithPasskeyApiService.ts2
-rw-r--r--packages/backend/src/server/api/SignupApiService.ts2
-rw-r--r--packages/backend/src/server/api/StreamingApiServerService.ts35
-rw-r--r--packages/backend/src/server/api/endpoint-list.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/admin/announcements/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/announcements/list.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/copy.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts34
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/list.ts34
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/get-user-ips.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts20
-rw-r--r--packages/backend/src/server/api/endpoints/ap/get.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/ap/show.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/hashtags/users.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/i/2fa/key-done.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/2fa/register-key.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/2fa/register.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/2fa/unregister.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/change-password.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/delete-account.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/notifications-grouped.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/i/update-email.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/update.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/following.ts17
-rw-r--r--packages/backend/src/server/api/endpoints/users/get-following-users-by-birthday.ts167
-rw-r--r--packages/backend/src/server/api/openapi/schemas.ts5
-rw-r--r--packages/backend/src/server/api/stream/ChannelsService.ts94
-rw-r--r--packages/backend/src/server/api/stream/Connection.ts93
-rw-r--r--packages/backend/src/server/api/stream/channel.ts19
-rw-r--r--packages/backend/src/server/api/stream/channels/admin.ts34
-rw-r--r--packages/backend/src/server/api/stream/channels/antenna.ts37
-rw-r--r--packages/backend/src/server/api/stream/channels/channel.ts36
-rw-r--r--packages/backend/src/server/api/stream/channels/chat-room.ts37
-rw-r--r--packages/backend/src/server/api/stream/channels/chat-user.ts37
-rw-r--r--packages/backend/src/server/api/stream/channels/drive.ts34
-rw-r--r--packages/backend/src/server/api/stream/channels/global-timeline.ts41
-rw-r--r--packages/backend/src/server/api/stream/channels/hashtag.ts37
-rw-r--r--packages/backend/src/server/api/stream/channels/home-timeline.ts37
-rw-r--r--packages/backend/src/server/api/stream/channels/hybrid-timeline.ts41
-rw-r--r--packages/backend/src/server/api/stream/channels/local-timeline.ts43
-rw-r--r--packages/backend/src/server/api/stream/channels/main.ts37
-rw-r--r--packages/backend/src/server/api/stream/channels/queue-stats.ts34
-rw-r--r--packages/backend/src/server/api/stream/channels/reversi-game.ts39
-rw-r--r--packages/backend/src/server/api/stream/channels/reversi.ts33
-rw-r--r--packages/backend/src/server/api/stream/channels/role-timeline.ts39
-rw-r--r--packages/backend/src/server/api/stream/channels/server-stats.ts34
-rw-r--r--packages/backend/src/server/api/stream/channels/user-list.ts49
-rw-r--r--packages/backend/src/server/file/FileServerDriveHandler.ts116
-rw-r--r--packages/backend/src/server/file/FileServerFileResolver.ts126
-rw-r--r--packages/backend/src/server/file/FileServerProxyHandler.ts272
-rw-r--r--packages/backend/src/server/file/FileServerUtils.ts107
-rw-r--r--packages/backend/src/server/oauth/OAuth2ProviderService.ts91
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts43
-rw-r--r--packages/backend/test-federation/compose.tpl.yml4
-rw-r--r--packages/backend/test-federation/compose.yml2
-rw-r--r--packages/backend/test-federation/test/utils.ts75
-rw-r--r--packages/backend/test-server/.swcrc2
-rw-r--r--packages/backend/test/compose.yml8
-rw-r--r--packages/backend/test/e2e/oauth.ts422
-rw-r--r--packages/backend/test/resources/dummy-for-file-server-service.pngbin0 -> 6285 bytes
-rw-r--r--packages/backend/test/tsconfig.json1
-rw-r--r--packages/backend/test/unit/SearchService.ts483
-rw-r--r--packages/backend/test/unit/entities/DriveFileEntityService.ts227
-rw-r--r--packages/backend/test/unit/entities/DriveFolderEntityService.ts171
-rw-r--r--packages/backend/test/unit/server/FileServerService.ts770
-rw-r--r--packages/backend/test/utils.ts43
-rw-r--r--packages/backend/tsconfig.json1
-rw-r--r--packages/frontend-builder/eslint.config.js1
-rw-r--r--packages/frontend-builder/package.json13
-rw-r--r--packages/frontend-builder/tsconfig.json1
-rw-r--r--packages/frontend-embed/eslint.config.js11
-rw-r--r--packages/frontend-embed/package.json43
-rw-r--r--packages/frontend-embed/src/components/EmAvatar.vue12
-rw-r--r--packages/frontend-embed/src/components/EmImgWithBlurhash.vue2
-rw-r--r--packages/frontend-embed/src/components/EmMediaImage.vue2
-rw-r--r--packages/frontend-embed/src/components/EmMfm.ts2
-rw-r--r--packages/frontend-embed/src/pages/clip.vue2
-rw-r--r--packages/frontend-embed/src/pages/tag.vue8
-rw-r--r--packages/frontend-embed/src/pages/user-timeline.vue6
-rw-r--r--packages/frontend-embed/tsconfig.json3
-rw-r--r--packages/frontend-shared/build.js2
-rw-r--r--packages/frontend-shared/eslint.config.js1
-rw-r--r--packages/frontend-shared/js/worker-multi-dispatch.ts2
-rw-r--r--packages/frontend-shared/package.json19
-rw-r--r--packages/frontend-shared/tsconfig.json1
-rw-r--r--packages/frontend/.storybook/charts.ts2
-rw-r--r--packages/frontend/eslint.config.js11
-rw-r--r--packages/frontend/package.json114
-rw-r--r--packages/frontend/src/accounts.ts6
-rw-r--r--packages/frontend/src/aiscript/ui.ts4
-rw-r--r--packages/frontend/src/analytics.ts1
-rw-r--r--packages/frontend/src/boot/main-boot.ts9
-rw-r--r--packages/frontend/src/components/MkAbuseReport.vue4
-rw-r--r--packages/frontend/src/components/MkAchievements.vue7
-rw-r--r--packages/frontend/src/components/MkAnnouncementDialog.vue6
-rw-r--r--packages/frontend/src/components/MkAsUi.vue6
-rw-r--r--packages/frontend/src/components/MkAuthConfirm.vue2
-rw-r--r--packages/frontend/src/components/MkAutocomplete.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkAutocomplete.vue23
-rw-r--r--packages/frontend/src/components/MkButton.vue6
-rw-r--r--packages/frontend/src/components/MkCaptcha.vue67
-rw-r--r--packages/frontend/src/components/MkChannelList.vue2
-rw-r--r--packages/frontend/src/components/MkChart.vue10
-rw-r--r--packages/frontend/src/components/MkClickerGame.vue4
-rw-r--r--packages/frontend/src/components/MkCodeEditor.vue22
-rw-r--r--packages/frontend/src/components/MkContextMenu.stories.impl.ts6
-rw-r--r--packages/frontend/src/components/MkContextMenu.vue8
-rw-r--r--packages/frontend/src/components/MkCropperDialog.vue14
-rw-r--r--packages/frontend/src/components/MkDialog.vue18
-rw-r--r--packages/frontend/src/components/MkDivider.vue2
-rw-r--r--packages/frontend/src/components/MkDraggable.vue311
-rw-r--r--packages/frontend/src/components/MkDrive.file.vue2
-rw-r--r--packages/frontend/src/components/MkDrive.folder.vue14
-rw-r--r--packages/frontend/src/components/MkDrive.vue59
-rw-r--r--packages/frontend/src/components/MkEmbedCodeGenDialog.vue85
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.section.vue8
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.vue4
-rw-r--r--packages/frontend/src/components/MkExtensionInstaller.vue9
-rw-r--r--packages/frontend/src/components/MkFileListForAdmin.vue8
-rw-r--r--packages/frontend/src/components/MkFolder.vue2
-rw-r--r--packages/frontend/src/components/MkFollowButton.vue8
-rw-r--r--packages/frontend/src/components/MkForm.file.vue (renamed from packages/frontend/src/components/MkFormDialog.file.vue)2
-rw-r--r--packages/frontend/src/components/MkForm.vue125
-rw-r--r--packages/frontend/src/components/MkFormDialog.vue99
-rw-r--r--packages/frontend/src/components/MkHeatmap.vue5
-rw-r--r--packages/frontend/src/components/MkImageEffectorDialog.vue112
-rw-r--r--packages/frontend/src/components/MkImageEffectorFxForm.vue10
-rw-r--r--packages/frontend/src/components/MkImageFrameEditorDialog.vue265
-rw-r--r--packages/frontend/src/components/MkImgPreviewDialog.vue7
-rw-r--r--packages/frontend/src/components/MkImgWithBlurhash.vue2
-rw-r--r--packages/frontend/src/components/MkInput.vue14
-rw-r--r--packages/frontend/src/components/MkInstanceCardMini.vue2
-rw-r--r--packages/frontend/src/components/MkInstanceStats.vue32
-rw-r--r--packages/frontend/src/components/MkLaunchPad.vue4
-rw-r--r--packages/frontend/src/components/MkMediaAudio.vue12
-rw-r--r--packages/frontend/src/components/MkMediaBanner.vue15
-rw-r--r--packages/frontend/src/components/MkMediaImage.vue28
-rw-r--r--packages/frontend/src/components/MkMediaList.vue33
-rw-r--r--packages/frontend/src/components/MkMediaVideo.vue32
-rw-r--r--packages/frontend/src/components/MkMenu.vue23
-rw-r--r--packages/frontend/src/components/MkModal.vue4
-rw-r--r--packages/frontend/src/components/MkNote.vue32
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue37
-rw-r--r--packages/frontend/src/components/MkNoteDraftsDialog.vue4
-rw-r--r--packages/frontend/src/components/MkNoteMediaGrid.vue28
-rw-r--r--packages/frontend/src/components/MkNotification.vue8
-rw-r--r--packages/frontend/src/components/MkNotificationSelectWindow.vue2
-rw-r--r--packages/frontend/src/components/MkObjectView.value.vue2
-rw-r--r--packages/frontend/src/components/MkPagination.vue2
-rw-r--r--packages/frontend/src/components/MkPagingButtons.vue4
-rw-r--r--packages/frontend/src/components/MkPolkadots.vue2
-rw-r--r--packages/frontend/src/components/MkPoll.vue3
-rw-r--r--packages/frontend/src/components/MkPollEditor.vue4
-rw-r--r--packages/frontend/src/components/MkPostForm.vue195
-rw-r--r--packages/frontend/src/components/MkPostFormAttaches.vue35
-rw-r--r--packages/frontend/src/components/MkPreferenceContainer.vue2
-rw-r--r--packages/frontend/src/components/MkPreview.vue10
-rw-r--r--packages/frontend/src/components/MkPreviewWithControls.vue93
-rw-r--r--packages/frontend/src/components/MkPullToRefresh.vue2
-rw-r--r--packages/frontend/src/components/MkPushNotificationAllowButton.vue2
-rw-r--r--packages/frontend/src/components/MkRadio.vue136
-rw-r--r--packages/frontend/src/components/MkRadios.vue280
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.reaction.vue2
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.vue8
-rw-r--r--packages/frontend/src/components/MkRetentionHeatmap.vue4
-rw-r--r--packages/frontend/src/components/MkRetentionLineChart.vue17
-rw-r--r--packages/frontend/src/components/MkRoleSelectDialog.vue6
-rw-r--r--packages/frontend/src/components/MkSelect.vue2
-rw-r--r--packages/frontend/src/components/MkServerSetupWizard.vue61
-rw-r--r--packages/frontend/src/components/MkServerSetupWizardDialog.vue2
-rw-r--r--packages/frontend/src/components/MkSignin.input.vue2
-rw-r--r--packages/frontend/src/components/MkSortOrderEditor.vue6
-rw-r--r--packages/frontend/src/components/MkSpot.vue4
-rw-r--r--packages/frontend/src/components/MkStreamingNotesTimeline.vue7
-rw-r--r--packages/frontend/src/components/MkStreamingNotificationsTimeline.vue4
-rw-r--r--packages/frontend/src/components/MkSuperMenu.vue2
-rw-r--r--packages/frontend/src/components/MkSystemWebhookEditor.impl.ts2
-rw-r--r--packages/frontend/src/components/MkSystemWebhookEditor.vue2
-rw-r--r--packages/frontend/src/components/MkTabs.vue6
-rw-r--r--packages/frontend/src/components/MkTagItem.vue6
-rw-r--r--packages/frontend/src/components/MkTextarea.vue25
-rw-r--r--packages/frontend/src/components/MkTokenGenerateWindow.vue16
-rw-r--r--packages/frontend/src/components/MkTooltip.vue4
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.Note.vue4
-rw-r--r--packages/frontend/src/components/MkUpdated.vue6
-rw-r--r--packages/frontend/src/components/MkUploaderDialog.vue6
-rw-r--r--packages/frontend/src/components/MkUploaderItems.vue8
-rw-r--r--packages/frontend/src/components/MkUserAnnouncementEditDialog.vue26
-rw-r--r--packages/frontend/src/components/MkUserCardMini.vue2
-rw-r--r--packages/frontend/src/components/MkUserList.vue2
-rw-r--r--packages/frontend/src/components/MkUserPopup.vue2
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Profile.vue2
-rw-r--r--packages/frontend/src/components/MkVisibilityPicker.vue3
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue2
-rw-r--r--packages/frontend/src/components/MkVisitorDashboard.vue2
-rw-r--r--packages/frontend/src/components/MkWaitingDialog.vue4
-rw-r--r--packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue2
-rw-r--r--packages/frontend/src/components/MkWatermarkEditorDialog.vue126
-rw-r--r--packages/frontend/src/components/MkWidgetSettingsDialog.vue174
-rw-r--r--packages/frontend/src/components/MkWidgets.vue72
-rw-r--r--packages/frontend/src/components/MkWindow.vue131
-rw-r--r--packages/frontend/src/components/MkYouTubePlayer.vue6
-rw-r--r--packages/frontend/src/components/global/I18n.vue2
-rw-r--r--packages/frontend/src/components/global/MkA.vue4
-rw-r--r--packages/frontend/src/components/global/MkAvatar.vue16
-rw-r--r--packages/frontend/src/components/global/MkCondensedLine.vue14
-rw-r--r--packages/frontend/src/components/global/MkCustomEmoji.vue2
-rw-r--r--packages/frontend/src/components/global/MkEmoji.vue2
-rw-r--r--packages/frontend/src/components/global/MkMfm.ts8
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.tabs.vue8
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue11
-rw-r--r--packages/frontend/src/components/global/MkResult.vue2
-rw-r--r--packages/frontend/src/components/global/MkTip.vue2
-rw-r--r--packages/frontend/src/components/global/PageWithHeader.vue2
-rw-r--r--packages/frontend/src/components/global/StackingRouterView.vue16
-rw-r--r--packages/frontend/src/components/grid/MkDataCell.vue2
-rw-r--r--packages/frontend/src/components/grid/MkGrid.vue2
-rw-r--r--packages/frontend/src/composables/use-chart-tooltip.ts7
-rw-r--r--packages/frontend/src/composables/use-form.ts2
-rw-r--r--packages/frontend/src/composables/use-mkselect.ts3
-rw-r--r--packages/frontend/src/composables/use-note-capture.ts22
-rw-r--r--packages/frontend/src/composables/use-tooltip.ts8
-rw-r--r--packages/frontend/src/composables/use-uploader.ts2
-rw-r--r--packages/frontend/src/custom-emojis.ts4
-rw-r--r--packages/frontend/src/deck.ts4
-rw-r--r--packages/frontend/src/directives/appear.ts2
-rw-r--r--packages/frontend/src/directives/index.ts2
-rw-r--r--packages/frontend/src/drag-and-drop.ts1
-rw-r--r--packages/frontend/src/filters/bytes.ts2
-rw-r--r--packages/frontend/src/filters/kmg.ts2
-rw-r--r--packages/frontend/src/filters/note.ts2
-rw-r--r--packages/frontend/src/instance.ts8
-rw-r--r--packages/frontend/src/lib/nirax.ts10
-rw-r--r--packages/frontend/src/lib/pizzax.ts61
-rw-r--r--packages/frontend/src/navbar.ts15
-rw-r--r--packages/frontend/src/os.ts145
-rw-r--r--packages/frontend/src/pages/about.federation.vue2
-rw-r--r--packages/frontend/src/pages/admin-user.vue12
-rw-r--r--packages/frontend/src/pages/admin/RolesEditorFormula.vue101
-rw-r--r--packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue4
-rw-r--r--packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue6
-rw-r--r--packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue2
-rw-r--r--packages/frontend/src/pages/admin/abuses.vue2
-rw-r--r--packages/frontend/src/pages/admin/ads.vue33
-rw-r--r--packages/frontend/src/pages/admin/announcements.vue77
-rw-r--r--packages/frontend/src/pages/admin/bot-protection.vue18
-rw-r--r--packages/frontend/src/pages/admin/branding.vue22
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue9
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.register.vue3
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue2
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager2.vue4
-rw-r--r--packages/frontend/src/pages/admin/federation-job-queue.chart.chart.vue4
-rw-r--r--packages/frontend/src/pages/admin/index.vue2
-rw-r--r--packages/frontend/src/pages/admin/job-queue.vue6
-rw-r--r--packages/frontend/src/pages/admin/overview.active-users.vue2
-rw-r--r--packages/frontend/src/pages/admin/overview.vue2
-rw-r--r--packages/frontend/src/pages/admin/roles.edit.vue15
-rw-r--r--packages/frontend/src/pages/admin/roles.editor.vue15
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue27
-rw-r--r--packages/frontend/src/pages/admin/security.vue14
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue48
-rw-r--r--packages/frontend/src/pages/admin/settings.vue12
-rw-r--r--packages/frontend/src/pages/admin/system-webhook.item.vue8
-rw-r--r--packages/frontend/src/pages/admin/users.vue3
-rw-r--r--packages/frontend/src/pages/announcements.vue9
-rw-r--r--packages/frontend/src/pages/api-console.vue2
-rw-r--r--packages/frontend/src/pages/auth.form.vue8
-rw-r--r--packages/frontend/src/pages/auth.vue2
-rw-r--r--packages/frontend/src/pages/avatar-decoration-edit-dialog.vue6
-rw-r--r--packages/frontend/src/pages/avatar-decorations.vue4
-rw-r--r--packages/frontend/src/pages/channel-editor.vue40
-rw-r--r--packages/frontend/src/pages/channels.vue17
-rw-r--r--packages/frontend/src/pages/chat/XMessage.vue8
-rw-r--r--packages/frontend/src/pages/chat/home.home.vue6
-rw-r--r--packages/frontend/src/pages/chat/room.form.vue2
-rw-r--r--packages/frontend/src/pages/chat/room.vue2
-rw-r--r--packages/frontend/src/pages/clip.vue9
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue43
-rw-r--r--packages/frontend/src/pages/drive.file.info.vue5
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.game.vue6
-rw-r--r--packages/frontend/src/pages/emoji-edit-dialog.vue30
-rw-r--r--packages/frontend/src/pages/emojis.emoji.vue22
-rw-r--r--packages/frontend/src/pages/flash/flash-edit.vue2
-rw-r--r--packages/frontend/src/pages/flash/flash.vue20
-rw-r--r--packages/frontend/src/pages/follow-requests.vue8
-rw-r--r--packages/frontend/src/pages/gallery/edit.root.vue4
-rw-r--r--packages/frontend/src/pages/gallery/post.vue4
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue4
-rw-r--r--packages/frontend/src/pages/notifications.vue7
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.blocks.vue41
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.vue4
-rw-r--r--packages/frontend/src/pages/page.vue8
-rw-r--r--packages/frontend/src/pages/qr.read.vue2
-rw-r--r--packages/frontend/src/pages/reversi/game.board.vue23
-rw-r--r--packages/frontend/src/pages/reversi/game.setting.vue68
-rw-r--r--packages/frontend/src/pages/reversi/game.vue6
-rw-r--r--packages/frontend/src/pages/reversi/index.vue12
-rw-r--r--packages/frontend/src/pages/scratchpad.vue2
-rw-r--r--packages/frontend/src/pages/search.note.vue29
-rw-r--r--packages/frontend/src/pages/search.user.vue14
-rw-r--r--packages/frontend/src/pages/settings/2fa.vue7
-rw-r--r--packages/frontend/src/pages/settings/account-data.vue12
-rw-r--r--packages/frontend/src/pages/settings/accounts.vue4
-rw-r--r--packages/frontend/src/pages/settings/apps.vue4
-rw-r--r--packages/frontend/src/pages/settings/deck.vue34
-rw-r--r--packages/frontend/src/pages/settings/drive-cleaner.vue11
-rw-r--r--packages/frontend/src/pages/settings/drive.ImageFrameItem.vue2
-rw-r--r--packages/frontend/src/pages/settings/drive.WatermarkItem.vue2
-rw-r--r--packages/frontend/src/pages/settings/drive.vue5
-rw-r--r--packages/frontend/src/pages/settings/email.vue4
-rw-r--r--packages/frontend/src/pages/settings/emoji-palette.palette.vue33
-rw-r--r--packages/frontend/src/pages/settings/emoji-palette.vue57
-rw-r--r--packages/frontend/src/pages/settings/index.vue8
-rw-r--r--packages/frontend/src/pages/settings/mute-block.emoji-mute.vue6
-rw-r--r--packages/frontend/src/pages/settings/mute-block.vue7
-rw-r--r--packages/frontend/src/pages/settings/mute-block.word-mute.vue8
-rw-r--r--packages/frontend/src/pages/settings/navbar.vue39
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue22
-rw-r--r--packages/frontend/src/pages/settings/other.vue8
-rw-r--r--packages/frontend/src/pages/settings/plugin.vue4
-rw-r--r--packages/frontend/src/pages/settings/preferences.vue117
-rw-r--r--packages/frontend/src/pages/settings/profile.vue43
-rw-r--r--packages/frontend/src/pages/settings/profiles.vue9
-rw-r--r--packages/frontend/src/pages/settings/sounds.sound.vue4
-rw-r--r--packages/frontend/src/pages/settings/sounds.vue11
-rw-r--r--packages/frontend/src/pages/settings/statusbar.statusbar.vue16
-rw-r--r--packages/frontend/src/pages/settings/theme.vue2
-rw-r--r--packages/frontend/src/pages/tag.vue5
-rw-r--r--packages/frontend/src/pages/theme-editor.vue4
-rw-r--r--packages/frontend/src/pages/timeline.vue13
-rw-r--r--packages/frontend/src/pages/user-tag.vue1
-rw-r--r--packages/frontend/src/pages/user/activity.following.vue2
-rw-r--r--packages/frontend/src/pages/user/activity.notes.vue2
-rw-r--r--packages/frontend/src/pages/user/activity.pv.vue2
-rw-r--r--packages/frontend/src/pages/user/home.vue23
-rw-r--r--packages/frontend/src/pages/user/index.activity.vue2
-rw-r--r--packages/frontend/src/pages/user/index.files.vue2
-rw-r--r--packages/frontend/src/pages/user/notes.vue2
-rw-r--r--packages/frontend/src/pages/welcome.setup.vue8
-rw-r--r--packages/frontend/src/plugin.ts17
-rw-r--r--packages/frontend/src/pref-migrate.ts24
-rw-r--r--packages/frontend/src/preferences/def.ts7
-rw-r--r--packages/frontend/src/preferences/manager.ts80
-rw-r--r--packages/frontend/src/store.ts96
-rw-r--r--packages/frontend/src/stream.ts1
-rw-r--r--packages/frontend/src/theme.ts8
-rw-r--r--packages/frontend/src/tips.ts2
-rw-r--r--packages/frontend/src/types/menu.ts5
-rw-r--r--packages/frontend/src/types/option-value.ts6
-rw-r--r--packages/frontend/src/types/overload-to-union.ts31
-rw-r--r--packages/frontend/src/types/page-header.ts3
-rw-r--r--packages/frontend/src/ui/_common_/common.ts4
-rw-r--r--packages/frontend/src/ui/_common_/navbar-h.vue16
-rw-r--r--packages/frontend/src/ui/_common_/navbar.vue26
-rw-r--r--packages/frontend/src/ui/_common_/statusbar-federation.vue2
-rw-r--r--packages/frontend/src/ui/_common_/widgets.vue30
-rw-r--r--packages/frontend/src/ui/deck.vue10
-rw-r--r--packages/frontend/src/ui/deck/column.vue18
-rw-r--r--packages/frontend/src/ui/deck/main-column.vue4
-rw-r--r--packages/frontend/src/ui/deck/tl-column.vue2
-rw-r--r--packages/frontend/src/ui/deck/widgets-column.vue13
-rw-r--r--packages/frontend/src/ui/universal.vue8
-rw-r--r--packages/frontend/src/ui/zen.vue5
-rw-r--r--packages/frontend/src/utility/admin-lookup.ts10
-rw-r--r--packages/frontend/src/utility/autocomplete.ts50
-rw-r--r--packages/frontend/src/utility/chart-vline.ts2
-rw-r--r--packages/frontend/src/utility/check-word-mute.ts2
-rw-r--r--packages/frontend/src/utility/collect-page-vars.ts73
-rw-r--r--packages/frontend/src/utility/deep-equal.ts2
-rw-r--r--packages/frontend/src/utility/drive.ts30
-rw-r--r--packages/frontend/src/utility/element-contains.ts (renamed from packages/frontend/src/utility/contains.ts)5
-rw-r--r--packages/frontend/src/utility/file-drop.ts22
-rw-r--r--packages/frontend/src/utility/form.ts54
-rw-r--r--packages/frontend/src/utility/get-drive-file-menu.ts7
-rw-r--r--packages/frontend/src/utility/get-embed-code.ts13
-rw-r--r--packages/frontend/src/utility/get-note-menu.ts2
-rw-r--r--packages/frontend/src/utility/get-user-environment.ts2
-rw-r--r--packages/frontend/src/utility/image-compositor-functions/blur.glsl55
-rw-r--r--packages/frontend/src/utility/image-compositor-functions/blur.ts4
-rw-r--r--packages/frontend/src/utility/image-compositor-functions/fill.glsl11
-rw-r--r--packages/frontend/src/utility/image-compositor-functions/pixelate.glsl34
-rw-r--r--packages/frontend/src/utility/image-frame-renderer/ImageFrameRenderer.ts2
-rw-r--r--packages/frontend/src/utility/is-birthday.ts28
-rw-r--r--packages/frontend/src/utility/mfm-function-picker.ts48
-rw-r--r--packages/frontend/src/utility/paginator.ts6
-rw-r--r--packages/frontend/src/utility/please-login.ts6
-rw-r--r--packages/frontend/src/utility/sensitive-file.ts33
-rw-r--r--packages/frontend/src/utility/snowfall-effect.ts22
-rw-r--r--packages/frontend/src/utility/sound.ts2
-rw-r--r--packages/frontend/src/utility/storage.ts14
-rw-r--r--packages/frontend/src/utility/timeline-date-separate.ts2
-rw-r--r--packages/frontend/src/utility/tour.ts2
-rw-r--r--packages/frontend/src/widgets/WidgetActivity.chart.vue25
-rw-r--r--packages/frontend/src/widgets/WidgetActivity.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetAichan.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetAiscript.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetAiscriptApp.vue4
-rw-r--r--packages/frontend/src/widgets/WidgetBirthdayFollowings.user.vue86
-rw-r--r--packages/frontend/src/widgets/WidgetBirthdayFollowings.vue201
-rw-r--r--packages/frontend/src/widgets/WidgetButton.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetCalendar.vue1
-rw-r--r--packages/frontend/src/widgets/WidgetChat.vue1
-rw-r--r--packages/frontend/src/widgets/WidgetClicker.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetClock.vue42
-rw-r--r--packages/frontend/src/widgets/WidgetDigitalClock.vue8
-rw-r--r--packages/frontend/src/widgets/WidgetFederation.vue9
-rw-r--r--packages/frontend/src/widgets/WidgetInstanceCloud.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetJobQueue.vue26
-rw-r--r--packages/frontend/src/widgets/WidgetMemo.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetNotifications.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetOnlineUsers.vue1
-rw-r--r--packages/frontend/src/widgets/WidgetPhotos.vue6
-rw-r--r--packages/frontend/src/widgets/WidgetRss.vue11
-rw-r--r--packages/frontend/src/widgets/WidgetRssTicker.vue12
-rw-r--r--packages/frontend/src/widgets/WidgetSlideshow.vue7
-rw-r--r--packages/frontend/src/widgets/WidgetTimeline.vue14
-rw-r--r--packages/frontend/src/widgets/WidgetTrends.vue3
-rw-r--r--packages/frontend/src/widgets/WidgetUnixClock.vue5
-rw-r--r--packages/frontend/src/widgets/WidgetUserList.vue1
-rw-r--r--packages/frontend/src/widgets/index.ts6
-rw-r--r--packages/frontend/src/widgets/server-metric/index.vue2
-rw-r--r--packages/frontend/src/widgets/widget.ts69
-rw-r--r--packages/frontend/test/is-birthday.test.ts55
-rw-r--r--packages/frontend/test/scroll.test.ts4
-rw-r--r--packages/frontend/tsconfig.json3
-rw-r--r--packages/frontend/vite.config.ts2
-rw-r--r--packages/i18n/build.ts2
-rw-r--r--packages/i18n/package.json15
-rw-r--r--packages/i18n/src/autogen/locale.ts152
-rw-r--r--packages/icons-subsetter/package.json11
-rw-r--r--packages/misskey-bubble-game/build.js2
-rw-r--r--packages/misskey-bubble-game/package.json15
-rw-r--r--packages/misskey-js/LICENSE2
-rw-r--r--packages/misskey-js/build.js2
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md23
-rw-r--r--packages/misskey-js/generator/package.json13
-rw-r--r--packages/misskey-js/package.json23
-rw-r--r--packages/misskey-js/src/autogen/apiClientJSDoc.ts11
-rw-r--r--packages/misskey-js/src/autogen/endpoint.ts3
-rw-r--r--packages/misskey-js/src/autogen/entities.ts2
-rw-r--r--packages/misskey-js/src/autogen/models.ts1
-rw-r--r--packages/misskey-js/src/autogen/types.ts176
-rw-r--r--packages/misskey-js/src/consts.ts1
-rw-r--r--packages/misskey-js/src/streaming.types.ts12
-rw-r--r--packages/misskey-reversi/build.js2
-rw-r--r--packages/misskey-reversi/package.json13
-rw-r--r--packages/shared/eslint.config.js16
-rw-r--r--packages/sw/package.json9
-rw-r--r--packages/sw/src/scripts/create-notification.ts46
-rw-r--r--packages/sw/tsconfig.json2
-rw-r--r--pnpm-lock.yaml7192
-rw-r--r--pnpm-workspace.yaml4
-rw-r--r--scripts/build-assets.mjs2
-rw-r--r--scripts/build-pre.mjs (renamed from scripts/build-pre.js)5
-rw-r--r--scripts/changelog-checker/package-lock.json398
-rw-r--r--scripts/changelog-checker/package.json10
-rw-r--r--scripts/clean-all.mjs (renamed from scripts/clean-all.js)7
-rw-r--r--scripts/clean.mjs (renamed from scripts/clean.js)5
-rw-r--r--scripts/tarball.mjs2
628 files changed, 13529 insertions, 9333 deletions
diff --git a/.dockerignore b/.dockerignore
index f204349160..39cbe2726f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -6,6 +6,7 @@
Dockerfile
build/
built/
+src-js/
db/
.devcontainer/compose.yml
node_modules/
diff --git a/.github/ISSUE_TEMPLATE/01_bug-report.yml b/.github/ISSUE_TEMPLATE/01_bug-report.yml
index fd68e602dd..00da7e9a2a 100644
--- a/.github/ISSUE_TEMPLATE/01_bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/01_bug-report.yml
@@ -54,7 +54,7 @@ body:
* Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4
* Browser: Chrome 113.0.5672.126
* Server URL: misskey.example.com
- * Misskey: 2025.x.x
+ * Misskey: 2026.x.x
value: |
* Model and OS of the device(s):
* Browser:
@@ -74,7 +74,7 @@ body:
Examples:
* Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment
- * Misskey: 2025.x.x
+ * Misskey: 2026.x.x
* Node: 20.x.x
* PostgreSQL: 18.x.x
* Redis: 7.x.x
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 0000000000..7c30489afe
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,3 @@
+# Copilot Instructions for Misskey
+
+- en-US.yml を編集しないでください。
diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml
index 45b8d23dda..ec7073c9fd 100644
--- a/.github/workflows/dockle.yml
+++ b/.github/workflows/dockle.yml
@@ -11,6 +11,7 @@ on:
jobs:
dockle:
runs-on: ubuntu-latest
+
env:
DOCKER_CONTENT_TRUST: 1
DOCKLE_VERSION: 0.4.15
@@ -20,29 +21,33 @@ jobs:
- name: Download and install dockle v${{ env.DOCKLE_VERSION }}
run: |
+ set -eux
curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v${DOCKLE_VERSION}/dockle_${DOCKLE_VERSION}_Linux-64bit.deb"
sudo dpkg -i dockle.deb
- - run: |
- cp .config/docker_example.env .config/docker.env
- cp ./compose_example.yml ./compose.yml
-
- - run: |
- docker compose up -d web
- IMAGE_ID=$(docker compose images --format json web | jq -r '.[0].ID')
- docker tag "${IMAGE_ID}" misskey-web:latest
-
- - name: Prune docker junk (optional but recommended)
+ - name: Build web image (docker build)
run: |
- docker system prune -af
- docker volume prune -f
+ set -eux
+ docker build -t "misskey-web:ci" .
+ docker image ls
- - name: Save image for Dockle
+ - name: Mount tmpfs for Dockle tar
+ env:
+ TMPFS_SIZE: 8G
run: |
- docker save misskey-web:latest -o ./misskey-web.tar
- ls -lh ./misskey-web.tar
+ set -eux
+ sudo mkdir -p /mnt/dockle-tmp
+ sudo mount -t tmpfs -o size=${{ env.TMPFS_SIZE }} tmpfs /mnt/dockle-tmp
+ free -h
+ df -h
- - name: Run Dockle with tar input
+ - name: Save image tar into tmpfs
run: |
- dockle --exit-code 1 --input ./misskey-web.tar
+ set -eux
+ docker save misskey-web:ci -o /mnt/dockle-tmp/misskey-web.tar
+ ls -lh /mnt/dockle-tmp/misskey-web.tar
+ - name: Run Dockle Scan (tar input)
+ run: |
+ set -eux
+ dockle --exit-code 1 --input /mnt/dockle-tmp/misskey-web.tar
diff --git a/.github/workflows/report-backend-memory.yml b/.github/workflows/report-backend-memory.yml
index c339ca49b4..bf2e311c83 100644
--- a/.github/workflows/report-backend-memory.yml
+++ b/.github/workflows/report-backend-memory.yml
@@ -54,55 +54,110 @@ jobs:
BASE_MEMORY=$(cat ./artifacts/memory-base.json)
HEAD_MEMORY=$(cat ./artifacts/memory-head.json)
- BASE_RSS=$(echo "$BASE_MEMORY" | jq -r '.memory.rss // 0')
- HEAD_RSS=$(echo "$HEAD_MEMORY" | jq -r '.memory.rss // 0')
+ variation() {
+ calc() {
+ BASE=$(echo "$BASE_MEMORY" | jq -r ".${1}.${2} // 0")
+ HEAD=$(echo "$HEAD_MEMORY" | jq -r ".${1}.${2} // 0")
- # Calculate difference
- if [ "$BASE_RSS" -gt 0 ] && [ "$HEAD_RSS" -gt 0 ]; then
- DIFF=$((HEAD_RSS - BASE_RSS))
- DIFF_PERCENT=$(echo "scale=2; ($DIFF * 100) / $BASE_RSS" | bc)
+ DIFF=$((HEAD - BASE))
+ if [ "$BASE" -gt 0 ]; then
+ DIFF_PERCENT=$(echo "scale=2; ($DIFF * 100) / $BASE" | bc)
+ else
+ DIFF_PERCENT=0
+ fi
- # Convert to MB for readability
- BASE_MB=$(echo "scale=2; $BASE_RSS / 1048576" | bc)
- HEAD_MB=$(echo "scale=2; $HEAD_RSS / 1048576" | bc)
- DIFF_MB=$(echo "scale=2; $DIFF / 1048576" | bc)
+ # Convert KB to MB for readability
+ BASE_MB=$(echo "scale=2; $BASE / 1024" | bc)
+ HEAD_MB=$(echo "scale=2; $HEAD / 1024" | bc)
+ DIFF_MB=$(echo "scale=2; $DIFF / 1024" | bc)
- echo "base_mb=$BASE_MB" >> "$GITHUB_OUTPUT"
- echo "head_mb=$HEAD_MB" >> "$GITHUB_OUTPUT"
- echo "diff_mb=$DIFF_MB" >> "$GITHUB_OUTPUT"
- echo "diff_percent=$DIFF_PERCENT" >> "$GITHUB_OUTPUT"
- echo "has_data=true" >> "$GITHUB_OUTPUT"
+ JSON=$(jq -c -n \
+ --argjson base "$BASE_MB" \
+ --argjson head "$HEAD_MB" \
+ --argjson diff "$DIFF_MB" \
+ --argjson diff_percent "$DIFF_PERCENT" \
+ '{base: $base, head: $head, diff: $diff, diff_percent: $diff_percent}')
- # Determine if this is a significant change (more than 5% increase)
- if [ "$(echo "$DIFF_PERCENT > 5" | bc)" -eq 1 ]; then
- echo "significant_increase=true" >> "$GITHUB_OUTPUT"
- else
- echo "significant_increase=false" >> "$GITHUB_OUTPUT"
- fi
- else
- echo "has_data=false" >> "$GITHUB_OUTPUT"
- fi
+ echo "$JSON"
+ }
+
+ JSON=$(jq -c -n \
+ --argjson VmRSS "$(calc $1 VmRSS)" \
+ --argjson VmHWM "$(calc $1 VmHWM)" \
+ --argjson VmSize "$(calc $1 VmSize)" \
+ --argjson VmData "$(calc $1 VmData)" \
+ '{VmRSS: $VmRSS, VmHWM: $VmHWM, VmSize: $VmSize, VmData: $VmData}')
+
+ echo "$JSON"
+ }
+
+ JSON=$(jq -c -n \
+ --argjson beforeGc "$(variation beforeGc)" \
+ --argjson afterGc "$(variation afterGc)" \
+ --argjson afterRequest "$(variation afterRequest)" \
+ '{beforeGc: $beforeGc, afterGc: $afterGc, afterRequest: $afterRequest}')
+
+ echo "res=$JSON" >> "$GITHUB_OUTPUT"
- id: build-comment
name: Build memory comment
+ env:
+ RES: ${{ steps.compare.outputs.res }}
run: |
- HEADER="## Backend Memory Usage Comparison"
+ HEADER="## Backend memory usage comparison"
FOOTER="[See workflow logs for details](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})"
echo "$HEADER" > ./output.md
echo >> ./output.md
- if [ "${{ steps.compare.outputs.has_data }}" == "true" ]; then
- echo "| Metric | base | head | Diff |" >> ./output.md
- echo "|--------|------|------|------|" >> ./output.md
- echo "| RSS | ${{ steps.compare.outputs.base_mb }} MB | ${{ steps.compare.outputs.head_mb }} MB | ${{ steps.compare.outputs.diff_mb }} MB (${{ steps.compare.outputs.diff_percent }}%) |" >> ./output.md
- echo >> ./output.md
+ table() {
+ echo "| Metric | base (MB) | head (MB) | Diff (MB) | Diff (%) |" >> ./output.md
+ echo "|--------|------:|------:|------:|------:|" >> ./output.md
+
+ line() {
+ METRIC=$2
+ BASE=$(echo "$RES" | jq -r ".${1}.${2}.base")
+ HEAD=$(echo "$RES" | jq -r ".${1}.${2}.head")
+ DIFF=$(echo "$RES" | jq -r ".${1}.${2}.diff")
+ DIFF_PERCENT=$(echo "$RES" | jq -r ".${1}.${2}.diff_percent")
+
+ if (( $(echo "$DIFF_PERCENT > 0" | bc -l) )); then
+ DIFF="+$DIFF"
+ DIFF_PERCENT="+$DIFF_PERCENT"
+ fi
+
+ # highlight VmRSS
+ if [ "$2" = "VmRSS" ]; then
+ METRIC="**${METRIC}**"
+ BASE="**${BASE}**"
+ HEAD="**${HEAD}**"
+ DIFF="**${DIFF}**"
+ DIFF_PERCENT="**${DIFF_PERCENT}**"
+ fi
+
+ echo "| ${METRIC} | ${BASE} MB | ${HEAD} MB | ${DIFF} MB | ${DIFF_PERCENT}% |" >> ./output.md
+ }
+
+ line $1 VmRSS
+ line $1 VmHWM
+ line $1 VmSize
+ line $1 VmData
+ }
+
+ echo "### Before GC" >> ./output.md
+ table beforeGc
+ echo >> ./output.md
+
+ echo "### After GC" >> ./output.md
+ table afterGc
+ echo >> ./output.md
+
+ echo "### After Request" >> ./output.md
+ table afterRequest
+ echo >> ./output.md
- if [ "${{ steps.compare.outputs.significant_increase }}" == "true" ]; then
- echo "⚠️ **Warning**: Memory usage has increased by more than 5%. Please verify this is not an unintended change." >> ./output.md
- echo >> ./output.md
- fi
- else
- echo "Could not retrieve memory usage data." >> ./output.md
+ # Determine if this is a significant change (more than 5% increase)
+ if [ "$(echo "$RES" | jq -r '.afterGc.VmRSS.diff_percent | tonumber > 5')" = "true" ]; then
+ echo "⚠️ **Warning**: Memory usage has increased by more than 5%. Please verify this is not an unintended change." >> ./output.md
echo >> ./output.md
fi
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 562ec76b85..77bbdb2b0a 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -48,6 +48,13 @@ jobs:
image: redis:7
ports:
- 56312:6379
+ meilisearch:
+ image: getmeili/meilisearch:v1.3.4
+ ports:
+ - 57712:7700
+ env:
+ MEILI_NO_ANALYTICS: true
+ MEILI_ENV: development
steps:
- uses: actions/checkout@v6.0.1
diff --git a/.gitignore b/.gitignore
index ac7502f384..7839e4de66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,7 @@ docker-compose.yml
built
built-test
js-built
+src-js
/data
/.cache-loader
/db
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c7d99b3ce..536b50c31f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,48 @@
+## 2026.3.0
+
+### Note
+- `users/following` の `birthday` プロパティは非推奨になりました。代わりに `users/get-following-users-by-birthday` をご利用ください。
+
+### General
+- Enhance: 「もうすぐ誕生日のユーザー」ウィジェットで、誕生日が至近のユーザーも表示できるように
+ (Cherry-picked from https://github.com/MisskeyIO/misskey)
+ - 「今日誕生日のユーザー」は「もうすぐ誕生日のユーザー」に名称変更されました
+- Fix: ユーザーハッシュタグページでユーザーの読み込みが重複する問題を修正
+- 依存関係の更新
+
+### Client
+- Enhance: ドライブのファイル一覧で自動でもっと見るを利用可能に
+- Enhance: ウィジェットの表示設定をプレビューを見ながら行えるように
+- Enhance: ウィジェットの設定項目のラベルの多言語対応
+- Enhance: 画面幅が広いときにメディアを横並びで表示できるようにするオプションを追加
+- Enhance: パフォーマンスの向上
+- Fix: ドライブクリーナーでファイルを削除しても画面に反映されない問題を修正 #16061
+- Fix: 非ログイン時にログインを求めるダイアログが表示された後にダイアログのぼかしが解除されず操作不能になることがある問題を修正
+- Fix: ドライブのソートが「登録日(昇順)」の場合に正しく動作しない問題を修正
+- Fix: 高度なMFMのピッカーを使用する際の挙動を改善
+- Fix: 管理画面でアーカイブ済のお知らせを表示した際にアクティブなお知らせが多い旨の警告が出る問題を修正
+- Fix: ファイルタブのセンシティブメディアを開く際に確認ダイアログを出す設定が適用されない問題を修正
+- Fix: 2月29日を誕生日に設定している場合、閏年以外は3月1日を誕生日として扱うように修正
+- Fix: `Mk:C:container` の `borderWidth` が正しく反映されない問題を修正
+- Fix: mCaptchaが正しく動作しない問題を修正
+- Fix: 非ログイン時にリバーシの対局が表示されない問題を修正
+- Fix: ノートの詳細表示でリアクションが全件表示されない問題を修正
+- Fix: 動画埋め込みプレイヤーなどの一部ウィンドウで、ウィンドウのサイズ変更や移動が正常に行えない問題を修正
+- Fix: 画像エフェクトの修正
+ - 塗りつぶし・モザイク・ぼかしエフェクトを回転させると歪む問題を修正
+ - モザイクの格子のサイズが画像の縦横比によって長方形となる問題を修正
+ - モザイクの色味がより自然になるように修正
+ - ぼかしに不自然な縦線が入る問題を修正
+- Fix: フォロー承認通知でフォローされた際のメッセージの絵文字が表示されない問題を修正
+- Fix: HTTP環境など(Secure Contextのない環境)で、設定画面が閲覧できない問題を修正
+
+### Server
+- Enhance: OAuthのクライアント情報取得(Client Information Discovery)において、IndieWeb Living Standard 11 July 2024で定義されているJSONドキュメント形式に対応しました
+ - JSONによるClient Information Discoveryを行うには、レスポンスの`Content-Type`ヘッダーが`application/json`である必要があります
+ - 従来の実装(12 February 2022版・HTML Microformat形式)も引き続きサポートされます
+- Enhance: メモリ使用量を削減
+- Fix: `/admin/get-user-ips` エンドポイントのアクセス権限を管理者のみに修正
+
## 2025.12.2
### Note
diff --git a/COPYING b/COPYING
index 7635bfc913..a17c82c002 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,5 @@
Unless otherwise stated this repository is
-Copyright © 2014-2025 syuilo and contributors
+Copyright © 2014-2026 syuilo and contributors
And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE.
diff --git a/Dockerfile b/Dockerfile
index 02739d9ca2..c1ac81f09e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
-# syntax = docker/dockerfile:1.4
+# syntax = docker/dockerfile:1.20
-ARG NODE_VERSION=22.15.0-bookworm
+ARG NODE_VERSION=22.21.1-bookworm
# build assets & compile TypeScript
@@ -102,6 +102,7 @@ COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-js/
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-reversi/built ./packages/misskey-reversi/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/built ./packages/backend/built
+COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/src-js ./packages/backend/src-js
COPY --chown=misskey:misskey --from=native-builder /misskey/packages/i18n/built ./packages/i18n/built
COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis
COPY --chown=misskey:misskey . ./
diff --git a/README.md b/README.md
index a73102d713..e3261d13c2 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/misskey-dev/misskey)
+<a href="https://flatt.tech/oss/gmo/trampoline" target="_blank"><img src="https://flatt.tech/assets/images/badges/gmo-oss.svg" height="24px"/></a>
+
</div>
## Thanks
@@ -49,3 +51,13 @@ Thanks to [Crowdin](https://crowdin.com/) for providing the localization platfor
<a href="https://hub.docker.com/"><img src="https://user-images.githubusercontent.com/20679825/230148221-f8e73a32-a49b-47c3-9029-9a15c3824f92.png" height="30" alt="Docker" /></a>
Thanks to [Docker](https://hub.docker.com/) for providing the container platform that helps us run Misskey in production.
+
+---
+
+<div align="center">
+
+Support us with a ⭐ !
+
+[![Star History Chart](https://api.star-history.com/svg?repos=misskey-dev/misskey&type=Date)](https://star-history.com/#misskey-dev/misskey&Date)
+
+</div>
diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index d130d4b4b3..a72429e941 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -1365,6 +1365,14 @@ _widgets:
userList: "قائمة المستخدمين"
_userList:
chooseList: "اختر قائمة"
+_widgetOptions:
+ height: "الإرتفاع"
+ _button:
+ colored: "ملوّن"
+ _clock:
+ size: "الحجم"
+ _birthdayFollowings:
+ period: "المدة"
_cw:
hide: "إخفاء"
show: "عرض المزيد"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index e7d391cd70..9af4fc0ec4 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -1137,6 +1137,14 @@ _widgets:
aichan: "আই চান"
_userList:
chooseList: "লিস্ট নির্বাচন করুন"
+_widgetOptions:
+ height: "উচ্চতা"
+ _button:
+ colored: "রঙ্গিন"
+ _clock:
+ size: "আকার"
+ _birthdayFollowings:
+ period: "ব্যাপ্তিকাল"
_cw:
hide: "লুকান"
show: "আরও দেখুন"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 59768668ef..5bd4b01b27 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -543,6 +543,7 @@ regenerate: "Regenera"
fontSize: "Mida del text"
mediaListWithOneImageAppearance: "Altura de la llista de fitxers amb una única imatge"
limitTo: "Limita a {x}"
+showMediaListByGridInWideArea: "Mostra la llista de medis en vista quadrícula quan l'amplada de la pantalla ho permeti"
noFollowRequests: "No tens sol·licituds de seguiment"
openImageInNewTab: "Obre imatges a una nova pestanya"
dashboard: "Tauler de control"
@@ -1389,7 +1390,7 @@ defaultCompressionLevel_description: "Si el redueixes augmentaràs la qualitat d
inMinutes: "Minut(s)"
inDays: "Di(a)(es)"
safeModeEnabled: "Mode segur activat"
-pluginsAreDisabledBecauseSafeMode: "Els afegits no estan activats perquè el mode segur està activat."
+pluginsAreDisabledBecauseSafeMode: "Les extensions no estan activades perquè el mode segur està activat."
customCssIsDisabledBecauseSafeMode: "El CSS personalitzat no s'aplica perquè el mode segur es troba activat."
themeIsDefaultBecauseSafeMode: "El tema predeterminat es farà servir mentre el mode segur estigui activat. Una vegada es desactivi el mode segur es restablirà el tema escollit."
thankYouForTestingBeta: "Gràcies per ajudar-nos a provar la versió beta!"
@@ -1406,6 +1407,7 @@ youAreAdmin: "Ets l'administrador "
frame: "Marc"
presets: "Predefinit"
zeroPadding: "Sense omplir"
+nothingToConfigure: "No hi ha res a configurar"
_imageEditing:
_vars:
caption: "Títol de l'arxiu"
@@ -1550,6 +1552,9 @@ _settings:
showPageTabBarBottom: "Mostrar les pestanyes de les línies de temps a la part inferior"
emojiPaletteBanner: "Pots registrar ajustos preestablerts com paletes perquè es mostrin permanentment al selector d'emojis, o personalitzar la configuració de visió del selector."
enableAnimatedImages: "Activar imatges animades"
+ settingsPersistence_title: "Persistència de la configuració "
+ settingsPersistence_description1: "Habilitar la persistència de la configuració permet que no es perdi la informació de la configuració "
+ settingsPersistence_description2: "Depenent de l'entorn pot ser que no puguis habilitar aquesta opció."
_chat:
showSenderName: "Mostrar el nom del remitent"
sendOnEnter: "Introdueix per enviar"
@@ -1609,8 +1614,8 @@ _bubbleGame:
highScore: "Millor puntuació "
maxChain: "Nombre màxim de combos"
yen: "{yen}Ien"
- estimatedQty: "{qty}peces"
- scoreSweets: "{onigiriQtyWithUnit}ongiris"
+ estimatedQty: "{qty} Peces"
+ scoreSweets: "{onigiriQtyWithUnit} Boles d'arròs "
_howToPlay:
section1: "Ajusta la posició i deixa caure l'objecte dintre la caixa."
section2: "Quan dos objectes del mateix tipus es toquen, canviaran en un objecte diferent i guanyares punts."
@@ -2180,8 +2185,8 @@ _email:
title: "Has rebut una sol·licitud de seguiment"
_plugin:
install: "Instal·lar un afegit "
- installWarn: "Si us plau, no instal·lis afegits que no siguin de confiança."
- manage: "Gestionar els afegits"
+ installWarn: "Si us plau, no instal·lis extensions que no siguin de confiança."
+ manage: "Gestiona les extensions"
viewSource: "Veure l'origen "
viewLog: "Mostra el registre"
_preferencesBackups:
@@ -2541,6 +2546,44 @@ _widgets:
clicker: "Clicker"
birthdayFollowings: "Usuaris que fan l'aniversari avui"
chat: "Xateja amb aquest usuari"
+_widgetOptions:
+ showHeader: "Mostrar la capçalera"
+ transparent: "Fons transparent"
+ height: "Alçada "
+ _button:
+ colored: "Colorit"
+ _clock:
+ size: "Mida"
+ thickness: "Amplada de l'agulla "
+ thicknessThin: "Esvelt "
+ thicknessMedium: "Normal"
+ thicknessThick: "Gruixut "
+ graduations: "Marques de l'esfera "
+ graduationDots: "Punt"
+ graduationArabic: "Nombres àrabs "
+ fadeGraduations: "Efecte gradient "
+ sAnimation: "Animació de la maneta dels segons"
+ sAnimationElastic: "Real"
+ sAnimationEaseOut: "Suau"
+ twentyFour: "Format 24 hores"
+ labelTime: "Temps"
+ labelTz: "Fus horari"
+ labelTimeAndTz: "Hora i fus horari"
+ timezone: "Fus horari"
+ showMs: "Mostrar mil·lisegons"
+ showLabel: "Mostrar etiqueta"
+ _jobQueue:
+ sound: "Reprodueix so"
+ _rss:
+ url: "URL del canal RSS"
+ refreshIntervalSec: "Interval d'actualitzacions (segons)"
+ maxEntries: "Nombre màxim d'entrades a mostrar"
+ _rssTicker:
+ shuffle: "Visualització aleatòria "
+ duration: "Velocitat desplaçament bàner informatiu "
+ reverse: "Desplaçament contrari"
+ _birthdayFollowings:
+ period: "Període"
_cw:
hide: "Amagar"
show: "Carregar més"
@@ -2816,6 +2859,14 @@ _deck:
usedAsMinWidthWhenFlexible: "L'amplada mínima es farà servir quan \"Ajust automàtic de l'amplada\" estigui activat"
flexible: "Ajust automàtic de l'amplada"
enableSyncBetweenDevicesForProfiles: "Activar la sincronització de la informació de perfils de dispositiu a dispositiu"
+ showHowToUse: "Veure la descripció de la interfície d'usuari "
+ _howToUse:
+ addColumn_title: "Afegir columna"
+ addColumn_description: "Pots seleccionar i afegir tipus de columnes."
+ settings_title: "Configuració de la interfície d'usuari "
+ settings_description: "Pots configurar la interfície d'usuari amb detall."
+ switchProfile_title: "Canviar perfil"
+ switchProfile_description: "Pots desar el disseny de la interfície d'usuari com un perfil i anar canviant entre ells quan vulguis."
_columns:
main: "Principal"
widgets: "Ginys"
@@ -3299,7 +3350,6 @@ _imageEffector:
title: "Efecte"
addEffect: "Afegeix un efecte"
discardChangesConfirm: "Vols descartar els canvis i sortir?"
- nothingToConfigure: "No hi ha opcions de configuració disponibles"
failedToLoadImage: "Error en carregar la imatge"
_fxs:
chromaticAberration: "Aberració cromàtica"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 4738b1de13..22962a4243 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1789,6 +1789,14 @@ _widgets:
_userList:
chooseList: "Vybrat seznam"
clicker: "Clicker"
+_widgetOptions:
+ height: "Výška"
+ _button:
+ colored: "Barevné"
+ _clock:
+ size: "Velikost"
+ _birthdayFollowings:
+ period: "Trvání"
_cw:
hide: "Skrýt"
show: "Zobrazit více"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 33e19f1cc1..a0591b4931 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -83,6 +83,7 @@ files: "Dateien"
download: "Herunterladen"
driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Einige Inhalte, die diese Datei verwenden, werden auch verschwinden."
unfollowConfirm: "Möchtest du {name} wirklich nicht mehr folgen?"
+cancelFollowRequestConfirm: "Möchten Sie die Voll-Anfrage an {name} zurückziehen?"
rejectFollowRequestConfirm: "Möchtest du die Follow-Anfrage von {name} ablehnen?"
exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
@@ -254,6 +255,7 @@ noteDeleteConfirm: "Möchtest du diese Notiz wirklich löschen?"
pinLimitExceeded: "Du kannst nicht noch mehr Notizen anheften."
done: "Fertig"
processing: "In Bearbeitung …"
+preprocessing: "In Vorbereitung"
preview: "Vorschau"
default: "Standard"
defaultValueIs: "Standardwert: {value}"
@@ -302,6 +304,7 @@ uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis das Hochladen abgeschl
uploadNFiles: "Lade {n} Dateien hoch"
explore: "Erkunden"
messageRead: "Gelesen"
+readAllChatMessages: "Alle Nachrichten als gelesen markieren"
noMoreHistory: "Kein weiterer Verlauf vorhanden"
startChat: "Chat starten"
nUsersRead: "Von {n} Benutzern gelesen"
@@ -334,6 +337,7 @@ fileName: "Dateiname"
selectFile: "Datei auswählen"
selectFiles: "Dateien auswählen"
selectFolder: "Ordner auswählen"
+unselectFolder: "Ordnerauswahl aufheben"
selectFolders: "Ordner auswählen"
fileNotSelected: "Keine Datei ausgewählt"
renameFile: "Datei umbenennen"
@@ -346,6 +350,7 @@ addFile: "Datei hinzufügen"
showFile: "Datei anzeigen"
emptyDrive: "Deine Drive ist leer"
emptyFolder: "Dieser Ordner ist leer"
+dropHereToUpload: "Dateien hier ablegen, um sie hochzuladen."
unableToDelete: "Nicht löschbar"
inputNewFileName: "Gib einen neuen Dateinamen ein"
inputNewDescription: "Gib eine neue Beschreibung ein"
@@ -538,6 +543,7 @@ regenerate: "Regenerieren"
fontSize: "Schriftgröße"
mediaListWithOneImageAppearance: "Höhe von Medienlisten mit nur einem Bild"
limitTo: "Auf {x} begrenzen"
+showMediaListByGridInWideArea: "Medienlisten auf breiteren Bildschirmen nebeneinander anzeigen"
noFollowRequests: "Keine ausstehenden Follow-Anfragen vorhanden"
openImageInNewTab: "Bilder in neuem Tab öffnen"
dashboard: "Dashboard"
@@ -773,6 +779,7 @@ lockedAccountInfo: "Auch wenn du Follow-Anfragen auf manuelle Bestätigung setzt
alwaysMarkSensitive: "Medien standardmäßig als sensibel markieren"
loadRawImages: "Anstatt Vorschaubilder immer Originalbilder anzeigen"
disableShowingAnimatedImages: "Animierte Bilder nicht abspielen"
+disableShowingAnimatedImages_caption: "Unabhängig von dieser Einstellung kann es vorkommen, dass animierte Bilder nicht abgespielt werden, wenn z. B. die Barrierefreiheits- oder Energiespareinstellungen des Browsers oder des Betriebssystems eingreifen."
highlightSensitiveMedia: "Sensitive Medien markieren"
verificationEmailSent: "Eine Bestätigungsmail wurde an deine Email-Adresse versendet. Besuche den dort enthaltenen Link, um die Verifizierung abzuschließen."
notSet: "Nicht konfiguriert"
@@ -1020,6 +1027,8 @@ pushNotificationNotSupported: "Entweder dein Browser oder deine Instanz unterst
sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald sie gelesen wurden"
sendPushNotificationReadMessageCaption: "Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen."
pleaseAllowPushNotification: "Bitte erlauben Sie Benachrichtigungen in Ihrem Browser."
+browserPushNotificationDisabled: "Das Abrufen der Berechtigung zum Senden von Benachrichtigungen ist fehlgeschlagen."
+browserPushNotificationDisabledDescription: "Sie haben keine Berechtigung, Benachrichtigungen von {serverName} zu senden. Bitte erlauben Sie Benachrichtigungen in den Browser-Einstellungen und versuchen Sie es erneut."
windowMaximize: "Maximieren"
windowMinimize: "Minimieren"
windowRestore: "Wiederherstellen"
@@ -1095,6 +1104,7 @@ prohibitedWordsDescription2: "Durch die Verwendung von Leerzeichen können AND-V
hiddenTags: "Ausgeblendete Hashtags"
hiddenTagsDescription: "Die hier eingestellten Tags werden nicht mehr in den Trends angezeigt. Mit der Umschalttaste können mehrere ausgewählt werden."
notesSearchNotAvailable: "Die Notizsuche ist nicht verfügbar."
+usersSearchNotAvailable: "Die Benutzersuche ist nicht verfügbar."
license: "Lizenz"
unfavoriteConfirm: "Wirklich aus Favoriten entfernen?"
myClips: "Meine Clips"
@@ -1169,6 +1179,7 @@ installed: "Installiert"
branding: "Branding"
enableServerMachineStats: "Hardwareinformationen des Servers veröffentlichen"
enableIdenticonGeneration: "Generierung von Benutzer-Identicons aktivieren"
+showRoleBadgesOfRemoteUsers: "Rollensymbole anzeigen, die Remote-Benutzern zugewiesen wurden."
turnOffToImprovePerformance: "Deaktivierung kann zu höherer Leistung führen."
createInviteCode: "Einladung erstellen"
createWithOptions: "Einladung mit Optionen erstellen"
@@ -1317,6 +1328,7 @@ acknowledgeNotesAndEnable: "Schalten Sie dies erst ein, wenn Sie die Vorsichtsma
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"
+draftsAndScheduledNotes: "Entwürfe und geplante Beiträge"
confirmOnReact: "Reagieren bestätigen"
reactAreYouSure: "Willst du eine \"{emoji}\"-Reaktion hinzufügen?"
markAsSensitiveConfirm: "Möchtest du dieses Medium als sensibel kennzeichnen?"
@@ -1345,6 +1357,7 @@ textCount: "Zeichenanzahl"
information: "Über"
chat: "Chat"
directMessage: "Mit dem Benutzer chatten"
+directMessage_short: "Nachrichten"
migrateOldSettings: "Alte Client-Einstellungen migrieren"
migrateOldSettings_description: "Dies sollte normalerweise automatisch geschehen, aber wenn die Migration aus irgendeinem Grund nicht erfolgreich war, kannst du den Migrationsprozess selbst manuell auslösen. Die aktuellen Konfigurationsinformationen werden dabei überschrieben."
compress: "Komprimieren"
@@ -1372,28 +1385,83 @@ 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. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität."
+defaultCompressionLevel: "Standard-Kompressionsgrad"
+defaultCompressionLevel_description: "Bei einem niedrigeren Wert bleibt die Qualität erhalten, aber die Dateigröße nimmt zu.<br> Bei einem höheren Wert lässt sich die Dateigröße verringern, aber die Qualität nimmt ab."
inMinutes: "Minute(n)"
inDays: "Tag(en)"
safeModeEnabled: "Der abgesicherte Modus ist aktiviert."
+pluginsAreDisabledBecauseSafeMode: "Da der abgesicherte Modus aktiviert ist, sind alle Plugins deaktiviert."
+customCssIsDisabledBecauseSafeMode: "Da der abgesicherte Modus aktiviert ist, wird benutzerdefiniertes CSS nicht angewendet."
+themeIsDefaultBecauseSafeMode: "Solange der abgesicherte Modus aktiviert ist, wird das Standard-Theme verwendet. Wenn Sie den abgesicherten Modus deaktivieren, wird es wieder zurückgesetzt."
+thankYouForTestingBeta: "Vielen Dank für Ihre Unterstützung beim Testen der Beta-Version!"
+createUserSpecifiedNote: "Benutzerdefinierte Notiz erstellen"
+schedulePost: "Beitrag planen"
+scheduleToPostOnX: "Der Beitrag wird für {x} geplant.x"
+scheduledToPostOnX: "Der Beitrag ist für {x} geplant."
schedule: "Planen"
scheduled: "Geplant"
widgets: "Widgets"
deviceInfo: "Geräteinformation"
+deviceInfoDescription: "Bei technischen Anfragen kann es hilfreich sein, die folgenden Informationen anzugeben, da dies zur Lösung des Problems beitragen kann."
youAreAdmin: "Sie sind ein Administrator"
+frame: "Rahmen"
presets: "Vorlage"
+zeroPadding: "Nullauffüllung"
+nothingToConfigure: "Es sind keine Einstellungen verfügbar"
_imageEditing:
_vars:
+ caption: "Dateibeschriftung"
filename: "Dateiname"
+ filename_without_ext: "Dateiname ohne Erweiterung"
+ year: "Jahr der Aufnahme"
+ month: "Monat der Aufnahme"
+ day: "Tag der Aufnahme"
+ hour: "Stunde der Aufnahmezeit"
+ minute: "Minute der Aufnahmezeit"
+ second: "Sekunde der Aufnahmezeit"
+ camera_model: "Kameraname"
+ camera_lens_model: "Objektivname"
+ camera_mm: "Brennweite"
+ camera_mm_35: "Brennweite (35-mm-Äquivalent)"
+ camera_f: "Blende"
+ camera_s: "Verschlusszeit"
+ camera_iso: "ISO-Empfindlichkeit"
+ gps_lat: "Breitengrad"
+ gps_long: "Längengrad"
_imageFrameEditor:
+ title: "Rahmenbearbeitung"
+ tip: "Sie können das Bild dekorieren, indem Sie einen Rahmen sowie ein Etikett mit Metadaten hinzufügen."
header: "Kopfzeile"
+ footer: "Fußzeile"
+ borderThickness: "Randbreite"
+ labelThickness: "Beschriftungsbreite"
+ labelScale: "Etikettenskala"
+ centered: "Zentriert"
+ captionMain: "Überschrift (groß)"
+ captionSub: "Beschriftung (klein)"
+ availableVariables: "Verfügbare Variablen"
+ withQrCode: "QR-Code"
+ backgroundColor: "Hintergrundfarbe"
+ textColor: "Textfarbe"
font: "Schriftart"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
quitWithoutSaveConfirm: "Nicht gespeicherte Änderungen verwerfen?"
+ failedToLoadImage: "Das Laden des Bildes ist fehlgeschlagen."
+_compression:
+ _quality:
+ high: "Hohe Qualität"
+ medium: "Mittlere Qualität"
+ low: "Niedrige Qualität"
+ _size:
+ large: "Groß"
+ medium: "Medium"
+ small: "Klein"
_order:
newest: "Neueste zuerst"
oldest: "Älteste zuerst"
_chat:
+ messages: "Nachrichten"
noMessagesYet: "Noch keine Nachrichten"
newMessage: "Neue Nachricht"
individualChat: "Privater Chat"
@@ -1481,6 +1549,12 @@ _settings:
contentsUpdateFrequency_description2: "Wenn der Echtzeitmodus aktiviert ist, werden die Inhalte unabhängig von dieser Einstellung in Echtzeit aktualisiert."
showUrlPreview: "URL-Vorschau anzeigen"
showAvailableReactionsFirstInNote: "Zeige die verfügbaren Reaktionen im oberen Bereich an."
+ showPageTabBarBottom: "Tab-Leiste der Seite unten anzeigen"
+ emojiPaletteBanner: "Sie können Voreinstellungen, die im Emoji-Picker dauerhaft angezeigt werden sollen, als Palette registrieren oder die Anzeigeart des Pickers anpassen."
+ enableAnimatedImages: "Animierte Bilder aktivieren"
+ settingsPersistence_title: "Persistenz der Einstellungen"
+ settingsPersistence_description1: "Durch das Aktivieren der persistenten Speicherung der Einstellungen kann verhindert werden, dass Einstellungsinformationen verloren gehen."
+ settingsPersistence_description2: "Je nach Umgebung ist eine Aktivierung möglicherweise nicht möglich."
_chat:
showSenderName: "Name des Absenders anzeigen"
sendOnEnter: "Eingabetaste sendet Nachricht"
@@ -1489,6 +1563,8 @@ _preferencesProfile:
profileNameDescription: "Lege einen Namen fest, der dieses Gerät identifiziert."
profileNameDescription2: "Beispiel: \"Haupt-PC\", \"Smartphone\""
manageProfiles: "Profile verwalten"
+ shareSameProfileBetweenDevicesIsNotRecommended: "Es wird nicht empfohlen, dasselbe Profil auf mehreren Geräten zu teilen."
+ useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "Wenn es Einstellungselemente gibt, die Sie über mehrere Geräte synchronisieren möchten, aktivieren Sie bitte die Option „Über mehrere Geräte synchronisieren“ jeweils einzeln."
_preferencesBackup:
autoBackup: "Automatische Sicherung"
restoreFromBackup: "Wiederherstellen aus der Sicherung"
@@ -1498,6 +1574,7 @@ _preferencesBackup:
youNeedToNameYourProfileToEnableAutoBackup: "Um die automatische Sicherung zu aktivieren, müssen Profilnamen festgelegt werden."
autoPreferencesBackupIsNotEnabledForThisDevice: "Die automatische Sicherung der Einstellungen ist auf diesem Gerät nicht aktiviert."
backupFound: "Konfigurationssicherung gefunden."
+ forceBackup: "Erzwungenes Backup der Einstellungen"
_accountSettings:
requireSigninToViewContents: "Anmeldung erfordern, um Inhalte anzuzeigen"
requireSigninToViewContentsDescription1: "Erfordere eine Anmeldung, um alle Notizen und andere Inhalte anzuzeigen, die du erstellt hast. Dadurch wird verhindert, dass Crawler deine Informationen sammeln."
@@ -1654,6 +1731,10 @@ _serverSettings:
fanoutTimelineDbFallback: "Auf die Datenbank zurückfallen"
fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. "
reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis."
+ remoteNotesCleaning: "Automatische Bereinigung von Remote-Beiträgen"
+ remoteNotesCleaning_description: "Wenn diese Option aktiviert ist, werden Remote-Beiträge, die eine bestimmte Zeit überschritten haben, regelmäßig bereinigt, um ein Aufblähen der Datenbank zu verhindern."
+ remoteNotesCleaningMaxProcessingDuration: "Maximale fortlaufende Dauer des Reinigungsverarbeitungsprozesses"
+ remoteNotesCleaningExpiryDaysForEachNotes: "Mindestaufbewahrungsdauer für Notizen"
inquiryUrl: "Kontakt-URL"
inquiryUrlDescription: "Gib eine URL für das Kontaktformular der Serverbetreiber oder eine Webseite an, die Kontaktinformationen enthält."
openRegistration: "Registrierung von Konten aktivieren"
@@ -1672,6 +1753,11 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "Sichtbarkeit von nutzergenerierten Inhalten für Gäste"
userGeneratedContentsVisibilityForVisitor_description: "Dies ist nützlich, um zu verhindern, dass unangemessene Inhalte, die nicht gut moderiert sind, ungewollt über deinen eigenen Server im Internet veröffentlicht werden."
userGeneratedContentsVisibilityForVisitor_description2: "Die uneingeschränkte Veröffentlichung aller Inhalte des Servers im Internet, einschließlich der vom Server empfangenen Fremdinhalte, birgt Risiken. Dies ist besonders wichtig für Betrachter, die sich des dezentralen Charakters der Inhalte nicht bewusst sind, da sie selbst fremde Inhalte fälschlicherweise als auf dem Server erstellte Inhalte wahrnehmen könnten."
+ restartServerSetupWizardConfirm_title: "Möchten Sie den Assistenten für die Ersteinrichtung des Servers erneut ausführen?"
+ restartServerSetupWizardConfirm_text: "Einige aktuelle Einstellungen werden zurückgesetzt."
+ entrancePageStyle: "Stil der Einstiegsseite"
+ showTimelineForVisitor: "Zeitleiste anzeigen"
+ showActivitiesForVisitor: "Aktivitäten anzeigen"
_userGeneratedContentsVisibilityForVisitor:
all: "Alles ist öffentlich"
localOnly: "Nur lokale Inhalte werden veröffentlicht, fremde Inhalte bleiben privat"
@@ -1994,6 +2080,7 @@ _role:
canManageAvatarDecorations: "Profilbilddekorationen verwalten"
driveCapacity: "Drive-Kapazität"
maxFileSize: "Maximale Dateigröße, die hochgeladen werden kann"
+ maxFileSize_caption: "Bei einem Reverse Proxy oder einem CDN können andere vorgelagerte Konfigurationswerte vorhanden sein."
alwaysMarkNsfw: "Dateien immer als NSFW markieren"
canUpdateBioMedia: "Kann ein Profil- oder ein Bannerbild bearbeiten"
pinMax: "Maximale Anzahl an angehefteten Notizen"
@@ -2008,6 +2095,7 @@ _role:
descriptionOfRateLimitFactor: "Je niedriger desto weniger restriktiv, je höher destro restriktiver."
canHideAds: "Kann Werbung ausblenden"
canSearchNotes: "Nutzung der Notizsuchfunktion"
+ canSearchUsers: "Nutzung der Benutzersuche"
canUseTranslator: "Verwendung des Übersetzers"
avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können"
canImportAntennas: "Importieren von Antennen erlauben"
@@ -2020,6 +2108,7 @@ _role:
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"
+ scheduledNoteLimit: "Maximale Anzahl gleichzeitig erstellbarer geplanter Beiträge"
watermarkAvailable: "Kann die Wasserzeichenfunktion verwenden"
_condition:
roleAssignedTo: "Manuellen Rollen zugewiesen"
@@ -2280,6 +2369,7 @@ _time:
minute: "Minute(n)"
hour: "Stunde(n)"
day: "Tag(en)"
+ month: "Monat(e)"
_2fa:
alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert."
registerTOTP: "Authentifizierungs-App registrieren"
@@ -2409,6 +2499,7 @@ _auth:
scopeUser: "Als folgender Benutzer agieren"
pleaseLogin: "Bitte logge dich ein, um Apps zu authorisieren."
byClickingYouWillBeRedirectedToThisUrl: "Wenn der Zugang gewährt wird, wirst du automatisch zu folgender URL weitergeleitet"
+ alreadyAuthorized: "Dieser Anwendung wurde bereits Zugriff gewährt."
_antennaSources:
all: "Alle Notizen"
homeTimeline: "Notizen von Benutzern, denen gefolgt wird"
@@ -2455,6 +2546,44 @@ _widgets:
clicker: "Klickzähler"
birthdayFollowings: "Nutzer, die heute Geburtstag haben"
chat: "Mit dem Benutzer chatten"
+_widgetOptions:
+ showHeader: "Kopfzeile anzeigen"
+ transparent: "Hintergrund transparent machen"
+ height: "Höhe"
+ _button:
+ colored: "Farbig"
+ _clock:
+ size: "Größe"
+ thickness: "Dicke"
+ thicknessThin: "Dünn"
+ thicknessMedium: "Normal"
+ thicknessThick: "Dick"
+ graduations: "Zifferblattskala"
+ graduationDots: "Punkt"
+ graduationArabic: "Zahlen"
+ fadeGraduations: "Skala ausblenden"
+ sAnimation: "Zweite Animation"
+ sAnimationElastic: "Elastisch"
+ sAnimationEaseOut: "Weich"
+ twentyFour: "24-Stunden-Format"
+ labelTime: "Uhrzeit"
+ labelTz: "Zeitzone"
+ labelTimeAndTz: "Zeit und Zeitzone"
+ timezone: "Zeitzone"
+ showMs: "Millisekunden anzeigen"
+ showLabel: "Beschriftung anzeigen"
+ _jobQueue:
+ sound: "Ton abspielen"
+ _rss:
+ url: "RSS-Feed-URL"
+ refreshIntervalSec: "Aktualisierungsintervall (Sekunden)"
+ maxEntries: "Maximale Anzahl der angezeigten Einträge"
+ _rssTicker:
+ shuffle: "Zufällige Anzeigereihenfolge"
+ duration: "Banner-Scrollgeschwindigkeit (in Sekunden)"
+ reverse: "In andere Richtung scrollen"
+ _birthdayFollowings:
+ period: "Dauer"
_cw:
hide: "Inhalt verbergen"
show: "Inhalt anzeigen"
@@ -2499,9 +2628,20 @@ _postForm:
replyPlaceholder: "Dieser Notiz antworten …"
quotePlaceholder: "Diese Notiz zitieren …"
channelPlaceholder: "In einen Kanal senden"
+ showHowToUse: "Formularbeschreibung anzeigen"
_howToUse:
+ content_title: "Dieser Text"
+ content_description: "Bitte geben Sie den Inhalt ein, den Sie veröffentlichen möchten."
+ toolbar_title: "Symbolleiste"
+ toolbar_description: "Sie können Dateien oder Umfragen anhängen, Anmerkungen und Hashtags festlegen sowie Emojis und Erwähnungen einfügen."
+ account_title: "Profilmenü"
+ account_description: "Du kannst das Konto wechseln, von dem du postest, und dir eine Liste der im Konto gespeicherten Entwürfe und geplanten Beiträge anzeigen lassen."
visibility_title: "Sichtbarkeit"
+ visibility_description: "Sie können den Umfang festlegen, in dem die Notizen veröffentlicht werden."
menu_title: "Menü"
+ menu_description: "Sie können außerdem weitere Aktionen durchführen, z. B. als Entwurf speichern, das Posten planen oder Reaktionen einstellen."
+ submit_title: "Senden-Button"
+ submit_description: "Du kannst die Notiz posten. Du kannst sie auch mit Strg + Enter / Cmd + Enter posten."
_placeholders:
a: "Was machst du momentan?"
b: "Was ist um dich herum los?"
@@ -2647,6 +2787,8 @@ _notification:
youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten"
yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert"
pollEnded: "Umfrageergebnisse sind verfügbar"
+ scheduledNotePosted: "Geplante Notiz wurde veröffentlicht"
+ scheduledNotePostFailed: "Veröffentlichen der geplanten Notiz fehlgeschlagen"
newNote: "Neue Notiz"
unreadAntennaNote: "Antenne {name}"
roleAssigned: "Rolle zugewiesen"
@@ -2676,6 +2818,8 @@ _notification:
quote: "Zitationen"
reaction: "Reaktionen"
pollEnded: "Ende von Umfragen"
+ scheduledNotePosted: "Der geplante Beitrag wurde erfolgreich veröffentlicht."
+ scheduledNotePostFailed: "Der geplante Beitrag ist fehlgeschlagen."
receiveFollowRequest: "Erhaltene Follow-Anfragen"
followRequestAccepted: "Akzeptierte Follow-Anfragen"
roleAssigned: "Rolle zugewiesen"
@@ -2715,6 +2859,14 @@ _deck:
usedAsMinWidthWhenFlexible: "Ist \"Automatische Breitenanpassung\" aktiviert, wird hierfür die minimale Breite verwendet"
flexible: "Automatische Breitenanpassung"
enableSyncBetweenDevicesForProfiles: "Aktivieren der Synchronisierung von Profilinformationen zwischen Geräten"
+ showHowToUse: "Siehe dir die UI-Beschreibung an."
+ _howToUse:
+ addColumn_title: "Spalte hinzufügen"
+ addColumn_description: "Sie können den Spaltentyp auswählen und hinzufügen."
+ settings_title: "UI-Einstellungen"
+ settings_description: "Sie können die detaillierten Einstellungen der Deck-UI vornehmen."
+ switchProfile_title: "Profil wechseln"
+ switchProfile_description: "Das UI-Layout kann als Profil gespeichert werden, sodass du jederzeit zwischen den Profilen wechseln kannst."
_columns:
main: "Hauptspalte"
widgets: "Widgets"
@@ -2775,6 +2927,8 @@ _abuseReport:
notifiedWebhook: "Zu verwendender Webhook"
deleteConfirm: "Bist du sicher, dass du den Empfänger der Benachrichtigung entfernen möchtest?"
_moderationLogTypes:
+ clearQueue: "Warteschlange leeren"
+ promoteQueue: "Warteschlange erneut ausführen"
createRole: "Rolle erstellt"
deleteRole: "Rolle gelöscht"
updateRole: "Rolle aktualisiert"
@@ -2832,6 +2986,7 @@ _fileViewer:
url: "URL"
uploadedAt: "Hochgeladen am"
attachedNotes: "Zugehörige Notizen"
+ usage: "Nutzung"
thisPageCanBeSeenFromTheAuthor: "Nur der Benutzer, der diese Datei hochgeladen hat, kann diese Seite sehen."
_externalResourceInstaller:
title: "Von externer Seite installieren"
@@ -3084,6 +3239,7 @@ _bootErrors:
otherOption1: "Client-Einstellungen und Cache löschen"
otherOption2: "Einfachen Client starten"
otherOption3: "Starte das Reparaturwerkzeug"
+ otherOption4: "Misskey im abgesicherten Modus starten"
_search:
searchScopeAll: "Alle"
searchScopeLocal: "Lokal"
@@ -3120,6 +3276,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "Bei Anschluss an ein Netz von verteilten Servern (Fediverse) können Inhalte mit anderen Servern ausgetauscht werden."
doYouConnectToFediverse_description2: "Die Verbindung mit dem Fediverse wird auch als „Föderation“ bezeichnet."
youCanConfigureMoreFederationSettingsLater: "Erweiterte Einstellungen, wie z. B. die Angabe von föderierbaren Servern, können später vorgenommen werden."
+ remoteContentsCleaning: "Automatische Bereinigung von Remote-Inhalten"
+ remoteContentsCleaning_description: "Wenn Sie eine Föderation durchführen, empfangen Sie fortlaufend viele Inhalte. Wenn Sie die automatische Bereinigung aktivieren, werden Remote-Inhalte, deren bestimmter Zeitraum abgelaufen ist, automatisch vom Server gelöscht, wodurch Speicherplatz eingespart werden kann."
adminInfo: "Administrator-Informationen"
adminInfo_description: "Legt die Administrator-Informationen fest, die für den Empfang von Anfragen verwendet werden."
adminInfo_mustBeFilled: "Dies ist auf einem offenen Server oder bei aktivierter Föderation erforderlich."
@@ -3144,6 +3302,7 @@ _uploader:
allowedTypes: "Hochladbare Dateitypen"
tip: "Die Datei ist noch nicht hochgeladen worden. In diesem Dialog kannst du die Datei vor dem Hochladen anzeigen, umbenennen, komprimieren und zuschneiden. Wenn du fertig bist, klicke auf „Hochladen“, um den Upload zu starten."
_clientPerformanceIssueTip:
+ title: "Wenn du das Gefühl hast, dass der Akku sich schnell entlädt."
makeSureDisabledAdBlocker: "Deaktiviere deinen Adblocker"
makeSureDisabledAdBlocker_description: "Adblocker können die Leistung beeinträchtigen; vergewissere dich, ob in deinem Betriebssystem, Browser oder deinen Add-ons Adblocker aktiviert sind."
makeSureDisabledCustomCss: "Benutzerdefiniertes CSS deaktivieren"
@@ -3163,10 +3322,14 @@ _watermarkEditor:
driveFileTypeWarnDescription: "Bilddatei auswählen"
title: "Wasserzeichen bearbeiten"
cover: "Alles bedecken"
+ repeat: "Wiederholen"
+ preserveBoundingRect: "So einstellen, dass beim Drehen nichts herausragt"
opacity: "Transparenz"
scale: "Größe"
text: "Text"
+ qr: "QR-Code"
position: "Position"
+ margin: "Abstand"
type: "Art"
image: "Bilder"
advanced: "Fortgeschritten"
@@ -3175,35 +3338,75 @@ _watermarkEditor:
stripeWidth: "Linienbreite"
stripeFrequency: "Linienanzahl"
polkadot: "Punktmuster"
+ checker: "Prüfer"
polkadotMainDotOpacity: "Deckkraft des Hauptpunktes"
polkadotMainDotRadius: "Größe des Hauptpunktes"
polkadotSubDotOpacity: "Deckkraft des Unterpunktes"
polkadotSubDotRadius: "Größe des Unterpunktes"
polkadotSubDotDivisions: "Anzahl der Unterpunkte"
+ leaveBlankToAccountUrl: "Wenn Sie es leer lassen, wird das Profilbild des Kontos verwendet."
+ failedToLoadImage: "Bild konnte nicht geladen werden"
_imageEffector:
title: "Effekte"
addEffect: "Effekte hinzufügen"
discardChangesConfirm: "Änderungen verwerfen und beenden?"
+ failedToLoadImage: "Bild konnte nicht geladen werden"
_fxs:
chromaticAberration: "Chromatische Abweichung"
glitch: "Glitch"
mirror: "Spiegeln"
invert: "Farben umkehren"
grayscale: "Schwarzweiß"
+ blur: "Verwischen"
+ pixelate: "Verpixeln"
colorAdjust: "Farbkorrektur"
colorClamp: "Farbkomprimierung"
colorClampAdvanced: "Farbkomprimierung (erweitert)"
distort: "Verzerrung"
+ threshold: "inarisierun"
+ zoomLines: "Konzentrationslinien"
stripe: "Streifen"
polkadot: "Punktmuster"
+ checker: "Prüfer"
+ blockNoise: "Blockrauschen"
+ tearing: "Tearing"
+ fill: "Ausfüllen"
_fxProps:
angle: "Winkel"
scale: "Größe"
size: "Größe"
+ radius: "Radius"
+ samples: "Stichprobengröße"
offset: "Position"
color: "Farbe"
opacity: "Transparenz"
+ normalize: "Normalisierung"
+ amount: "Menge"
lightness: "Erhellen"
+ contrast: "Kontrast"
+ hue: "Farbton"
+ brightness: "Helligkeit"
+ saturation: "Sättigung"
+ max: "Maximum"
+ min: "Minimum"
+ direction: "Richtung"
+ phase: "Sättigung"
+ frequency: "Häufigkeit"
+ strength: "Stärke"
+ glitchChannelShift: "Verschiebung"
+ seed: "Seed-Wert"
+ redComponent: "Rot-Anteil"
+ greenComponent: "Grün-Anteil"
+ blueComponent: "Blau-Anteil"
+ threshold: "Schwellenwert"
+ centerX: "Zentrum X"
+ centerY: "Zentrum Y"
+ zoomLinesSmoothing: "Glättung"
+ zoomLinesSmoothingDescription: "Die Einstellungen für die Glättung und für die Breite der Konzentrationslinien können nicht gleichzeitig verwendet werden."
+ zoomLinesThreshold: "Breite der Konzentrationslinien"
+ zoomLinesMaskSize: "Mitteldurchmesser"
+ zoomLinesBlack: "Schwarz machen"
+ circle: "Kreisförmig"
drafts: "Entwurf"
_drafts:
select: "Entwurf auswählen"
@@ -3214,10 +3417,27 @@ _drafts:
noDrafts: "Keine Entwürfe"
replyTo: "Antwort an {user}"
quoteOf: "Zitat von {user}s Notiz"
+ postTo: "Beitrag im {channel}"
saveToDraft: "Als Entwurf speichern"
restoreFromDraft: "Aus Entwurf wiederherstellen"
restore: "Wiederherstellen"
listDrafts: "Liste der Entwürfe"
+ schedule: "Beitragsplanung"
+ listScheduledNotes: "Liste der geplanten Beiträge"
+ cancelSchedule: "Reservierung stornieren"
+qr: "QR-Code"
_qr:
showTabTitle: "Anzeigeart"
+ readTabTitle: "Auslesen"
+ shareTitle: "{name} {acct}"
+ shareText: "Bitte folge mir im Fediverse!"
+ chooseCamera: "Kamera auswählen"
+ cannotToggleFlash: "Blitzauswahl nicht möglich"
+ turnOnFlash: "Blitz einschalten"
+ turnOffFlash: "Blitz ausschalten"
+ startQr: "QR-Code-Leser starten"
+ stopQr: "QR-Code-Leser stoppen"
+ noQrCodeFound: "QR-Code wurde nicht gefunden"
+ scanFile: "Gerätebilder scannen"
raw: "Text"
+ mfm: "MFM"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 22e9d6eeb9..d08611fd23 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -543,6 +543,7 @@ regenerate: "Regenerate"
fontSize: "Font size"
mediaListWithOneImageAppearance: "Height of media lists with one image only"
limitTo: "Limit to {x}"
+showMediaListByGridInWideArea: "Display the media list in a grid when the screen width is wide"
noFollowRequests: "You don't have any pending follow requests"
openImageInNewTab: "Open images in new tab"
dashboard: "Dashboard"
@@ -1406,6 +1407,7 @@ youAreAdmin: "You are admin"
frame: "Frame"
presets: "Preset"
zeroPadding: "Zero padding"
+nothingToConfigure: "No configurable options available"
_imageEditing:
_vars:
caption: "File caption"
@@ -1550,6 +1552,9 @@ _settings:
showPageTabBarBottom: "Show page tab bar at the bottom"
emojiPaletteBanner: "You can register presets as palettes to display prominently in the emoji picker or customize the appearance of the picker."
enableAnimatedImages: "Enable animated images"
+ settingsPersistence_title: "Persistence of Settings"
+ settingsPersistence_description1: "Enabling setting persistence prevents configuration information from being lost."
+ settingsPersistence_description2: "It may not be possible to enable this depending on the environment."
_chat:
showSenderName: "Show sender's name"
sendOnEnter: "Press Enter to send"
@@ -2541,6 +2546,44 @@ _widgets:
clicker: "Clicker"
birthdayFollowings: "Today's Birthdays"
chat: "Chat with user"
+_widgetOptions:
+ showHeader: "Show header"
+ transparent: "Make background transparent"
+ height: "Height"
+ _button:
+ colored: "Colored"
+ _clock:
+ size: "Size"
+ thickness: "Needle thickness"
+ thicknessThin: "Thin"
+ thicknessMedium: "Normal"
+ thicknessThick: "Thick"
+ graduations: "Dial markings"
+ graduationDots: "Dot"
+ graduationArabic: "Arabic numbers"
+ fadeGraduations: "Fade the scale"
+ sAnimation: "Second hand animation"
+ sAnimationElastic: "Real"
+ sAnimationEaseOut: "Smooth"
+ twentyFour: "24 Hour Format"
+ labelTime: "Time"
+ labelTz: "Timezone"
+ labelTimeAndTz: "Time and time zone"
+ timezone: "Timezone"
+ showMs: "Show Miliseconds"
+ showLabel: "Show Label"
+ _jobQueue:
+ sound: "Play Sounds"
+ _rss:
+ url: "RSS Feed Url"
+ refreshIntervalSec: "Update interval (in seconds)"
+ maxEntries: "Maximum number of items to display"
+ _rssTicker:
+ shuffle: "Random display order"
+ duration: "Banner scroll speed (in seconds)"
+ reverse: "Scroll in the opposite direction"
+ _birthdayFollowings:
+ period: "Duration"
_cw:
hide: "Hide"
show: "Show content"
@@ -2816,6 +2859,14 @@ _deck:
usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled"
flexible: "Auto-adjust width"
enableSyncBetweenDevicesForProfiles: "Enable profile information sync between devices"
+ showHowToUse: ""
+ _howToUse:
+ addColumn_title: "Add column"
+ addColumn_description: "You can select and add column types."
+ settings_title: "UI Settings"
+ settings_description: "You can configure detailed settings for the deck UI."
+ switchProfile_title: "Profile Switching"
+ switchProfile_description: "You can save UI layouts as profiles and switch between them at any time."
_columns:
main: "Main"
widgets: "Widgets"
@@ -3299,7 +3350,6 @@ _imageEffector:
title: "Effects"
addEffect: "Add Effects"
discardChangesConfirm: "Are you sure you want to leave? You have unsaved changes."
- nothingToConfigure: "No configurable options available"
failedToLoadImage: "Failed to load image"
_fxs:
chromaticAberration: "Chromatic Aberration"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index dddfa4d57b..2d8c39d91a 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -543,6 +543,7 @@ regenerate: "Regenerar"
fontSize: "Tamaño de la letra"
mediaListWithOneImageAppearance: "Altura de la lista de medios con una sola imagen."
limitTo: "{x} hasta un máximo de"
+showMediaListByGridInWideArea: "Cuando el ancho de la pantalla sea grande, muestra la lista de multimedia uno al lado del otro."
noFollowRequests: "No hay solicitudes de seguimiento"
openImageInNewTab: "Abrir imagen en nueva pestaña"
dashboard: "Panel de control"
@@ -1406,6 +1407,7 @@ youAreAdmin: "Eres administrador."
frame: "Marco"
presets: "Predefinido"
zeroPadding: "Relleno cero"
+nothingToConfigure: "No hay nada que configurar"
_imageEditing:
_vars:
caption: "Título del archivo"
@@ -1550,6 +1552,9 @@ _settings:
showPageTabBarBottom: "Mostrar la barra de pestañas de la página en la parte inferior."
emojiPaletteBanner: "Puedes registrar ajustes preestablecidos como paletas para que se muestren permanentemente en el selector de emojis, o personalizar el método de visualización del selector."
enableAnimatedImages: "Habilitar imágenes animadas"
+ settingsPersistence_title: "Persistencia de la configuración"
+ settingsPersistence_description1: "Habilitar la persistencia de la configuración evita que se pierda la información de configuración."
+ settingsPersistence_description2: "Es posible que no se pueda habilitar esta función dependiendo del entorno."
_chat:
showSenderName: "Mostrar el nombre del remitente"
sendOnEnter: "Intro para enviar"
@@ -1670,9 +1675,9 @@ _initialTutorial:
title: "El concepto de Línea de tiempo"
description1: "Misskey proporciona múltiples líneas de tiempo basadas en su uso (algunas pueden no estar disponibles dependiendo de las políticas de la instancia)."
home: "Puedes ver los posts de las cuentas que sigues."
- local: "Puedes ver los posts de todos los usuarios de este servidor."
+ local: "Puedes ver los posts de todos los usuarios de este servidor (también llamado instancia)."
social: "Se ven los posts de la línea de tiempo de inicio junto con los de la línea de tiempo local."
- global: "Puedes ver notas de todos los servidores conectados."
+ global: "Puedes ver notas de todos los servidores (instancias) conectados."
description2: "Puedes cambiar la línea de tiempo en la parte superior de la pantalla cuando quieras."
description3: "Además, hay listas de líneas de tiempo y listas de canales. Para más detalle, por favor visita este enlace: {link}"
_postNote:
@@ -1682,14 +1687,14 @@ _initialTutorial:
description: "Puedes limitar quién puede ver tu nota."
public: "Tu nota será visible para todos los usuarios."
home: "Publicar solo en la línea de tiempo de Inicio. La nota se verá en tu perfil, la verán tus seguidores y también cuando sea renotada."
- followers: "Visible solo para seguidores. Sólo tus seguidores podrán ver la nota, y no podrá ser renotada por otras personas."
+ followers: "Visible solo para seguidores. Solo tus seguidores podrán ver la nota, y no podrá ser renotada por otras personas."
direct: "Visible sólo para usuarios específicos, y el destinatario será notificado. Puede usarse como alternativa a la mensajería directa."
doNotSendConfidencialOnDirect1: "¡Ten cuidado cuando vayas a enviar información sensible!"
doNotSendConfidencialOnDirect2: "Los administradores del servidor, también llamado instancia, pueden leer lo que escribes. Ten cuidado cuando envíes información sensible en notas directas en servidores o instancias no confiables."
localOnly: "Publicando con esta opción seleccionada, la nota no se federará hacia otros servidores. Los usuarios de otros servidores no podrán ver estas notas directamente, sin importar los ajustes seleccionados más arriba."
_cw:
title: "Alerta de contenido (CW)"
- description: "En lugar de mostrarse el contenido de la nota, se mostrará lo que escribas en el campo \"comentarios\". Pulsando en \"leer más\" desplegará el contenido de la nota."
+ description: "En lugar de mostrarse el contenido de la nota, se mostrará lo que escribas en el campo \"comentarios\". Pulsando en \"Ver más\" desplegará el contenido de la nota."
_exampleNote:
cw: "¡Esto te hará tener hambre!"
note: "Acabo de comerme un donut de chocolate glaseado 🍩😋"
@@ -2207,7 +2212,7 @@ _registry:
key: "Clave"
keys: "Clave"
domain: "Dominio"
- createKey: "Crear una llave"
+ createKey: "Crear una clave"
_aboutMisskey:
about: "Misskey es un software de código abierto, desarrollado por syuilo desde el 2014"
contributors: "Principales colaboradores"
@@ -2541,6 +2546,44 @@ _widgets:
clicker: "Cliqueador"
birthdayFollowings: "Hoy cumplen años"
chat: "Chatear"
+_widgetOptions:
+ showHeader: "Mostrar encabezados"
+ transparent: "Hacer fondo transparente"
+ height: "Altura"
+ _button:
+ colored: "Color"
+ _clock:
+ size: "Tamaño"
+ thickness: "Grosor de la aguja"
+ thicknessThin: "Delgada"
+ thicknessMedium: "Normal"
+ thicknessThick: "Gruesa"
+ graduations: "Marcas del dial"
+ graduationDots: "Puntos"
+ graduationArabic: "Números decimales"
+ fadeGraduations: "Desvanecer la escala"
+ sAnimation: "Animación de la manecilla de los segundos"
+ sAnimationElastic: "Real"
+ sAnimationEaseOut: "Suave"
+ twentyFour: "Formato 24 horas"
+ labelTime: "Hora"
+ labelTz: "Zona horaria"
+ labelTimeAndTz: "Hora y zona horaria"
+ timezone: "Zona horaria"
+ showMs: "Mostrar milisegundos"
+ showLabel: "Mostrar etiqueta"
+ _jobQueue:
+ sound: "Reproducir sonido"
+ _rss:
+ url: "URL del canal RSS"
+ refreshIntervalSec: "Intervalo de actualización (En segundos)"
+ maxEntries: "Número máximo de elementos a mostrar"
+ _rssTicker:
+ shuffle: "Orden de visualización aleatorio"
+ duration: "Velocidad de desplazamiento del baner (En segundos)"
+ reverse: "Desplázate en la dirección opuesta."
+ _birthdayFollowings:
+ period: "Duración"
_cw:
hide: "Ocultar"
show: "Ver más"
@@ -2816,6 +2859,14 @@ _deck:
usedAsMinWidthWhenFlexible: "Se usará el ancho mínimo cuando la opción \"Autoajustar ancho\" esté habilitada"
flexible: "Autoajustar ancho"
enableSyncBetweenDevicesForProfiles: "Activar la sincronización de la información de perfiles entre dispositivos."
+ showHowToUse: "Ver la descripción de la interfaz de usuario"
+ _howToUse:
+ addColumn_title: "Añadir columna"
+ addColumn_description: "Puede seleccionar y añadir tipos de columnas."
+ settings_title: "Configuración de la interfaz de usuario"
+ settings_description: "Puedes configurar la interfaz de usuario en detalle."
+ switchProfile_title: "Cambiar de perfil"
+ switchProfile_description: "Puedes guardar diseños de interfaz de usuario como perfiles y cambiar entre ellos en cualquier momento."
_columns:
main: "Principal"
widgets: "Widgets"
@@ -3145,8 +3196,8 @@ _selfXssPrevention:
description2: "Si no entiendes que estás pegando exactamente, %cdetente ahora mismo y cierra esta ventana"
description3: "Para más información visita esto {link}"
_followRequest:
- recieved: "Petición de seguimiento recibida"
- sent: "Petición de seguimiento enviada"
+ recieved: "Solicitud de seguimiento recibida"
+ sent: "Solicitud de seguimiento enviada"
_remoteLookupErrors:
_federationNotAllowed:
title: "Incapaz de comunicarse con este servidor."
@@ -3299,7 +3350,6 @@ _imageEffector:
title: "Efecto"
addEffect: "Añadir Efecto"
discardChangesConfirm: "¿Ignorar cambios y salir?"
- nothingToConfigure: "No hay opciones configurables disponibles."
failedToLoadImage: "Error al cargar la imagen"
_fxs:
chromaticAberration: "Aberración Cromática"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 5b3c8b75cb..63b8f3bb55 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -5,11 +5,12 @@ introMisskey: "Bienvenue ! Misskey est un service de microblogage décentralis
poweredByMisskeyDescription: "{name} est l'un des services propulsés par la plateforme ouverte <b>Misskey</b> (appelée \"instance Misskey\")."
monthAndDay: "{day}/{month}"
search: "Rechercher"
+reset: "Réinitialiser"
notifications: "Notifications"
username: "Nom d’utilisateur·rice"
password: "Mot de passe"
initialPasswordForSetup: "Mot de passe initial pour la configuration"
-initialPasswordIsIncorrect: "Mot de passe initial pour la configuration est incorrecte"
+initialPasswordIsIncorrect: "Le mot de passe initial pour la configuration est incorrect"
initialPasswordForSetupDescription: "Utilisez le mot de passe que vous avez entré pour le fichier de configuration si vous avez installé Misskey vous-même.\nSi vous utilisez un service d'hébergement Misskey, utilisez le mot de passe fourni.\nSi vous n'avez pas défini de mot de passe, laissez le champ vide pour continuer."
forgotPassword: "Mot de passe oublié"
fetchingAsApObject: "Récupération depuis le fédiverse …"
@@ -48,6 +49,7 @@ pin: "Épingler sur le profil"
unpin: "Désépingler"
copyContent: "Copier le contenu"
copyLink: "Copier le lien"
+copyRemoteLink: "Copier le lien de la note"
copyLinkRenote: "Copier le lien de la renote"
delete: "Supprimer"
deleteAndEdit: "Supprimer et réécrire"
@@ -62,8 +64,8 @@ copyNoteId: "Copier l'identifiant de la note"
copyFileId: "Copier l'identifiant du fichier"
copyFolderId: "Copier l'identifiant du dossier"
copyProfileUrl: "Copier l'URL du profil"
-searchUser: "Chercher un·e utilisateur·rice"
-searchThisUsersNotes: "Cherchez les notes de cet·te utilisateur·rice"
+searchUser: "Chercher un utilisateur"
+searchThisUsersNotes: "Cherchez les notes de cet utilisateur"
reply: "Répondre"
loadMore: "Afficher plus …"
showMore: "Voir plus"
@@ -81,6 +83,8 @@ files: "Fichiers"
download: "Télécharger"
driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier « {name} » ? Les notes avec ce fichier joint seront aussi supprimées."
unfollowConfirm: "Désirez-vous vous désabonner de {name} ?"
+cancelFollowRequestConfirm: "Est-te vous sur de vouloir annuler la demande de suivi de {name} ?"
+rejectFollowRequestConfirm: "Refuser la demande de suivi de {name} ?"
exportRequested: "Vous avez demandé une exportation. L’opération pourrait prendre un peu de temps. Une fois terminée, le fichier sera ajouté au Drive."
importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
lists: "Listes"
@@ -118,6 +122,8 @@ cantReRenote: "Impossible de renoter une Renote."
quote: "Citer"
inChannelRenote: "Renoter dans le canal"
inChannelQuote: "Citer dans le canal"
+renoteToChannel: "Renoter sur le canal"
+renoteToOtherChannel: "Renoter sur un autre canal"
pinnedNote: "Note épinglée"
pinned: "Épingler sur le profil"
you: "Vous"
@@ -212,6 +218,7 @@ blockThisInstance: "Bloquer cette instance"
silenceThisInstance: "Mettre cette instance en sourdine"
operations: "Opérations"
software: "Logiciel"
+softwareName: "Nom du logiciel"
version: "Version"
metadata: "Métadonnées"
withNFiles: "{n} fichier(s)"
@@ -231,6 +238,9 @@ blockedInstances: "Instances bloquées"
blockedInstancesDescription: "Listez les instances que vous désirez bloquer, une par ligne. Ces instances ne seront plus en capacité d'interagir avec votre instance."
silencedInstances: "Instances mises en sourdine"
silencedInstancesDescription: "Énumérer les noms d'hôte des instances à mettre en sourdine. Tous les comptes des instances énumérées seront traités comme mis en sourdine, ne peuvent faire que des demandes de suivi et ne peuvent pas mentionner les comptes locaux s'ils ne sont pas suivis. Cela n'affectera pas les instances bloquées."
+mediaSilencedInstances: "Médias silencieux sur ces instances"
+mediaSilencedInstancesDescription: "Liste des noms de serveurs où vous voulez que les médias soient silencieux, séparés par un retour à la ligne.\nTous les comptes des instances listées seront considérés comme sensibles, et ne peuvent pas utilisés d'émojis personnalisés. Ceci n'affectera pas les serveurs bloquées."
+federationAllowedHosts: "Serveurs qui autorisent la fédération"
muteAndBlock: "Masqué·e·s / Bloqué·e·s"
mutedUsers: "Utilisateur·rice·s en sourdine"
blockedUsers: "Utilisateur·rice·s bloqué·e·s"
@@ -2005,6 +2015,14 @@ _widgets:
_userList:
chooseList: "Sélectionner une liste"
birthdayFollowings: "Utilisateurs qui fêtent l'anniversaire aujourd'hui"
+_widgetOptions:
+ height: "Hauteur"
+ _button:
+ colored: "Coloré"
+ _clock:
+ size: "Taille"
+ _birthdayFollowings:
+ period: "Durée"
_cw:
hide: "Masquer"
show: "Afficher le contenu"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 9afa457ebd..dbb5d63da6 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -2209,6 +2209,14 @@ _widgets:
clicker: "Pengeklik"
birthdayFollowings: "Pengguna yang merayakan hari ulang tahunnya hari ini"
chat: "Obrolan pengguna"
+_widgetOptions:
+ height: "Tinggi"
+ _button:
+ colored: "Diwarnai"
+ _clock:
+ size: "Ukuran"
+ _birthdayFollowings:
+ period: "Durasi"
_cw:
hide: "Sembunyikan"
show: "Lihat konten"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 3b918e9c9f..db9bb481c7 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -127,7 +127,7 @@ renoteToOtherChannel: "Rinota a un altro canale"
pinnedNote: "Nota in primo piano"
pinned: "Fissa sul profilo"
you: "Tu"
-clickToShow: "Contenuto occultato, cliccare solo se si intende vedere"
+clickToShow: "Media nascosto, cliccare solo se si intende vedere"
sensitive: "Esplicito"
add: "Aggiungi"
reaction: "Reazioni"
@@ -543,6 +543,7 @@ regenerate: "Generare di nuovo"
fontSize: "Dimensione carattere"
mediaListWithOneImageAppearance: "Altezza dell'elenco media con una sola immagine "
limitTo: "Limita a {x}"
+showMediaListByGridInWideArea: "Quando la larghezza dello schermo è ampia, mostra i media affiancati"
noFollowRequests: "Non ci sono richieste di relazione"
openImageInNewTab: "Apri le immagini in un nuovo tab"
dashboard: "Pannello di controllo"
@@ -556,7 +557,7 @@ clientSettings: "Impostazioni client"
accountSettings: "Impostazioni profilo"
promotion: "Promossa"
promote: "Pubblicizza"
-numberOfDays: "Numero di giorni"
+numberOfDays: ""
hideThisNote: "Nasconda la nota"
showFeaturedNotesInTimeline: "Mostrare le note di tendenza nella tua timeline"
objectStorage: "Storage S3"
@@ -613,7 +614,7 @@ descendingOrder: "Diminuisce"
scratchpad: "ScratchPad"
scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScript. È possibile scrivere, eseguire e confermare i risultati dell'interazione del codice con Misskey."
uiInspector: "UI Inspector"
-uiInspectorDescription: "Puoi visualizzare un elenco di elementi UI presenti in memoria. I componenti dell'interfaccia utente vengono generati dalle funzioni Ui:C:."
+uiInspectorDescription: "Puoi visualizzare un elenco di elementi grafici presenti in memoria. I componenti dell'interfaccia grafica vengono generati dalle funzioni Ui:C:."
output: "Output"
script: "Script"
disablePagesScript: "Disabilitare AiScript nelle pagine"
@@ -701,7 +702,7 @@ hardWordMuteDescription: "Ignora le Note con la parola o la regola specificata.
regexpError: "errore regex"
regexpErrorDescription: "Si è verificato un errore nell'espressione regolare alla riga {line} della parola muta {tab}:"
instanceMute: "Silenziare l'istanza"
-userSaysSomething: "{name} ha detto qualcosa"
+userSaysSomething: "{name} ha scritto qualcosa"
userSaysSomethingAbout: "{name} ha anNotato qualcosa su \"{word}\""
makeActive: "Attiva"
display: "Visualizza"
@@ -1406,6 +1407,7 @@ youAreAdmin: "Sei un amministratore"
frame: "Cornice"
presets: "Preimpostato"
zeroPadding: "Al vivo"
+nothingToConfigure: "Niente da configurare"
_imageEditing:
_vars:
caption: "Didascalia dell'immagine"
@@ -1550,6 +1552,9 @@ _settings:
showPageTabBarBottom: "Visualizza le schede della pagina nella parte inferiore"
emojiPaletteBanner: "Puoi salvare i le emoji predefinite da appuntare in alto nel raccoglitore emoji come tavolozza e personalizzare in che modo visualizzare il raccoglitore."
enableAnimatedImages: "Attivare le immagini animate"
+ settingsPersistence_title: "Configurazione persistente"
+ settingsPersistence_description1: "Attivando le impostazioni persistenti si può evitare di riconfigurare il client successivamente."
+ settingsPersistence_description2: "Potrebbe non essere possibile attivare, dipende dall'ambiente."
_chat:
showSenderName: "Mostra il nome del mittente"
sendOnEnter: "Invio spedisce"
@@ -1737,7 +1742,7 @@ _serverSettings:
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Per prevenire SPAM, questa impostazione verrà disattivata automaticamente, se non si rileva alcuna attività di moderazione durante un certo periodo di tempo."
deliverSuspendedSoftware: "Software fuori produzione"
deliverSuspendedSoftwareDescription: "A causa di vulnerabilità o altri motivi, puoi interrompere la distribuzione di un software da un server specificandone il nome e la versione. Le informazioni sono fornite dall'altro server e l'autenticità non è garantita. Puoi indicare un intervallo di versione semantica, ma specificando >= 2024.3.1 non verranno incluse le versioni personalizzate come ad esempio 2024.3.1-custom.0, pertanto ti consigliamo di specificare una versione come >= 2024.3.1-0."
- singleUserMode: "Modalità utente singolo"
+ singleUserMode: "Modalità utenza singola"
singleUserMode_description: "Se sei l'unica persona a utilizzare questo server, l'abilitazione di questa modalità ottimizzerà le prestazioni."
signToActivityPubGet: "Firma delle richieste GET"
signToActivityPubGet_description: "Normalmente questa opzione dovrebbe essere abilitata. Se si verificano problemi con la comunicazione federata, disabilitarla potrebbe migliorare la situazione, ma d'altro canto potrebbe rendere impossibile la comunicazione, a seconda del server."
@@ -2066,7 +2071,7 @@ _role:
gtlAvailable: "Disponibilità della Timeline Federata"
ltlAvailable: "Disponibilità della Timeline Locale"
canPublicNote: "Scrivere Note con Visibilità Pubblica"
- mentionMax: "Numero massimo di menzioni in una nota"
+ mentionMax: ""
canInvite: "Generare codici di invito all'istanza"
inviteLimit: "Limite di codici invito"
inviteLimitCycle: "Intervallo di emissione del codice di invito"
@@ -2395,59 +2400,59 @@ _2fa:
backupCodesExhaustedWarning: "Hai esaurito i codici usa-e-getta. Se l'App che genera il codice OTP non è più disponibile, non potrai più accedere al tuo profilo. Ripeti la configurazione per l'autenticazione a due fattori."
moreDetailedGuideHere: "Informazioni dettagliate sull'autenticazione multi fattore (2FA/MFA)"
_permissions:
- "read:account": "Visualizza le informazioni sul profilo"
- "write:account": "Modifica le informazioni sul profilo"
- "read:blocks": "Visualizza i profili bloccati"
- "write:blocks": "Gestisci i profili bloccati"
- "read:drive": "Apri il Drive"
- "write:drive": "Gestisci il Drive"
- "read:favorites": "Visualizza i tuoi preferiti"
- "write:favorites": "Gestisci i tuoi preferiti"
- "read:following": "Vedi le informazioni di follow"
- "write:following": "Aggiungere e togliere Following"
- "read:messaging": "Visualizzare la chat"
- "write:messaging": "Gestire la chat"
- "read:mutes": "Vedi i profili silenziati"
- "write:mutes": "Gestione dei profili silenziati"
- "write:notes": "Creare / Eliminare note"
- "read:notifications": "Visualizzare notifiche"
- "write:notifications": "Gestione delle notifiche"
- "read:reactions": "Vedi reazioni"
- "write:reactions": "Gestione delle reazioni"
+ "read:account": "Vedere le informazioni sul profilo"
+ "write:account": "Modificare le informazioni sul profilo"
+ "read:blocks": "Vedere i profili bloccati"
+ "write:blocks": "Gestire il blocco profili"
+ "read:drive": "Leggere file nel Drive"
+ "write:drive": "Gestire file nel Drive"
+ "read:favorites": "Vedere le Note Preferite"
+ "write:favorites": "Gestire Note Preferite"
+ "read:following": "Vedere i Following"
+ "write:following": "Gestire i Following"
+ "read:messaging": "Vedere Messaggi Privati"
+ "write:messaging": "Modificare Messaggi Privati"
+ "read:mutes": "Vedere profili silenziati"
+ "write:mutes": "Gestire profili silenziati"
+ "write:notes": "Gestire le Note"
+ "read:notifications": "Vedere le notifiche"
+ "write:notifications": "Gestire le notifiche"
+ "read:reactions": "Vedere le reazioni"
+ "write:reactions": "Gestire le reazioni"
"write:votes": "Votare"
- "read:pages": "Visualizzare pagine"
- "write:pages": "Gestire pagine"
- "read:page-likes": "Visualizzare i \"Mi piace\" di pagine"
- "write:page-likes": "Gestire i \"Mi piace\" di pagine"
+ "read:pages": "Vedere le pagine"
+ "write:pages": "Gestire le pagine"
+ "read:page-likes": "Vedere le Pagine piaciute"
+ "write:page-likes": "Modificare le Pagine piaciute"
"read:user-groups": "Vedere i gruppi di utenti"
"write:user-groups": "Gestire i gruppi di utenti"
- "read:channels": "Visualizza canali"
- "write:channels": "Gestione dei canali"
- "read:gallery": "Visualizza la galleria."
- "write:gallery": "Gestione della galleria"
- "read:gallery-likes": "Visualizza i contenuti della galleria."
- "write:gallery-likes": "Manipolazione dei \"Mi piace\" della galleria."
- "read:flash": "Visualizza Play"
- "write:flash": "Modifica Play"
- "read:flash-likes": "Visualizza lista di Play piaciuti"
- "write:flash-likes": "Modifica lista di Play piaciuti"
- "read:admin:abuse-user-reports": "Mostra i report dai profili utente"
- "write:admin:delete-account": "Elimina l'account utente"
- "write:admin:delete-all-files-of-a-user": "Elimina i file dell'account utente"
- "read:admin:index-stats": "Visualizza informazioni sugli indici del database"
- "read:admin:table-stats": "Visualizza informazioni sulle tabelle del database"
- "read:admin:user-ips": "Visualizza indirizzi IP degli account"
- "read:admin:meta": "Visualizza i metadati dell'istanza"
- "write:admin:reset-password": "Ripristina la password dell'account utente"
- "write:admin:resolve-abuse-user-report": "Risolvere le segnalazioni dagli account utente"
+ "read:channels": "Vedere i canali"
+ "write:channels": "Gestire i canali"
+ "read:gallery": "Vedere le gallerie"
+ "write:gallery": "Gestire le gallerie"
+ "read:gallery-likes": "Vedere le Gallerie piaciute"
+ "write:gallery-likes": "Gestire le Gallerie piaciute"
+ "read:flash": "Vedere i Play"
+ "write:flash": "Gestire i Play"
+ "read:flash-likes": "Vedere la lista di Play piaciuti"
+ "write:flash-likes": "Modificare la lista di Play piaciuti"
+ "read:admin:abuse-user-reports": "Vedere le segnalazioni"
+ "write:admin:delete-account": "Eliminare profili"
+ "write:admin:delete-all-files-of-a-user": "Eliminare file dal Drive dei profili"
+ "read:admin:index-stats": "Vedere gli indici del database"
+ "read:admin:table-stats": "Vedere le statistiche database"
+ "read:admin:user-ips": "Vedere gli indirizzi IP dei profili"
+ "read:admin:meta": "Vedere i metadati dell'istanza"
+ "write:admin:reset-password": "Ripristinare la password del profilo"
+ "write:admin:resolve-abuse-user-report": "Risolvere le segnalazioni"
"write:admin:send-email": "Spedire email"
"read:admin:server-info": "Vedere le informazioni sul server"
"read:admin:show-moderation-log": "Vedere lo storico di moderazione"
- "read:admin:show-user": "Vedere le informazioni private degli account utente"
+ "read:admin:show-user": "Vedere le informazioni private dei profili"
"write:admin:suspend-user": "Sospendere i profili"
"write:admin:unset-user-avatar": "Rimuovere la foto profilo dai profili"
"write:admin:unset-user-banner": "Rimuovere l'immagine testata dai profili"
- "write:admin:unsuspend-user": "Togliere la sospensione ai profili"
+ "write:admin:unsuspend-user": "Rimuovere la sospensione ai profili"
"write:admin:meta": "Modificare i metadati dell'istanza"
"write:admin:user-note": "Scrivere annotazioni di moderazione"
"write:admin:roles": "Gestire i ruoli"
@@ -2475,11 +2480,11 @@ _permissions:
"read:admin:ad": "Vedere i banner pubblicitari"
"write:invite-codes": "Creare codici di invito"
"read:invite-codes": "Vedere i codici di invito"
- "write:clip-favorite": "Impostare Clip preferite"
+ "write:clip-favorite": "Modificare Clip preferite"
"read:clip-favorite": "Vedere Clip preferite"
"read:federation": "Vedere la federazione"
"write:report-abuse": "Inviare segnalazioni"
- "write:chat": "Gestire la chat"
+ "write:chat": "Modificare Messaggi Privati"
"read:chat": "Visualizzare le chat"
_auth:
shareAccessTitle: "Permessi dell'applicazione"
@@ -2528,19 +2533,57 @@ _widgets:
instanceCloud: "Nuvola di federazione"
postForm: "Finestra di pubblicazione"
slideshow: "Diapositive"
- button: "Pulsante"
+ button: "Bottone"
onlineUsers: "Persone attive adesso"
jobQueue: "Coda di lavoro"
serverMetric: "Statistiche server"
aiscript: "Console AiScript"
aiscriptApp: "App AiScript"
aichan: "Mascotte Ai"
- userList: "Elenco utenti"
+ userList: "Lista profili"
_userList:
chooseList: "Seleziona una lista"
clicker: "Cliccheria"
birthdayFollowings: "Compleanni del giorno"
- chat: "Chatta con questa persona"
+ chat: "Messaggi diretti"
+_widgetOptions:
+ showHeader: "Mostra la testata"
+ transparent: "Sfondo trasparente"
+ height: "Altezza"
+ _button:
+ colored: "Colorato"
+ _clock:
+ size: "Dimensioni"
+ thickness: "Spessore lancette"
+ thicknessThin: "Sottili"
+ thicknessMedium: "Medie"
+ thicknessThick: "Larghe"
+ graduations: "Quadrante"
+ graduationDots: "Punti"
+ graduationArabic: "Numeri"
+ fadeGraduations: "Sfumatura"
+ sAnimation: "Animazione dei secondi"
+ sAnimationElastic: "Realistica"
+ sAnimationEaseOut: "Morbida"
+ twentyFour: "Formato 24 ore"
+ labelTime: "Orario"
+ labelTz: "Fuso orario"
+ labelTimeAndTz: "Orario e fuso orario"
+ timezone: "Fuso orario"
+ showMs: "Millisecondi visibili"
+ showLabel: "Etichetta visibile"
+ _jobQueue:
+ sound: "Emetti un suono"
+ _rss:
+ url: "URL del Feed RSS"
+ refreshIntervalSec: "Intervallo di aggiornamento (in secondi)"
+ maxEntries: "Quantità massima visualizzabile"
+ _rssTicker:
+ shuffle: "Ordine casuale"
+ duration: "Velocità di scorrimento del ticker (in secondi)"
+ reverse: "Direzione inversa"
+ _birthdayFollowings:
+ period: "Durata"
_cw:
hide: "Nascondere"
show: "Continua la lettura..."
@@ -2618,7 +2661,7 @@ _profile:
metadataContent: "Contenuto"
changeAvatar: "Modifica immagine profilo"
changeBanner: "Cambia intestazione"
- verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo.\nPer verificare il profilo tramite la spunta di conferma, devi inserire la url alla pagina che contiene un link al tuo profilo Misskey. Deve avere attributo rel='me'."
+ verifiedLinkDescription: "Come avere i collegamenti verificati: inserisci la URL ad una pagina che contiene un collegamento al tuo profilo.\nVedrai una spunta di conferma se, in quella pagina, il collegamento al tuo profilo Misskey ha attributo rel='me'."
avatarDecorationMax: "Puoi aggiungere fino a {max} decorazioni."
followedMessage: "Messaggio, quando qualcuno ti segue"
followedMessageDescription: "Puoi impostare un breve messaggio da mostrare agli altri profili quando ti seguono."
@@ -2738,7 +2781,7 @@ _notification:
fileUploaded: "File caricato correttamente"
youGotMention: "{name} ti ha menzionato"
youGotReply: "{name} ti ha risposto"
- youGotQuote: "{name} ha citato la tua Nota e ha detto"
+ youGotQuote: "{name} ha scritto citando la tua Nota"
youRenoted: "{name} ha rinotato"
youWereFollowed: "Follower aggiuntivo"
youReceivedFollowRequest: "Hai ricevuto una richiesta di follow"
@@ -2764,7 +2807,7 @@ _notification:
exportOfXCompleted: "Abbiamo completato l'esportazione di {x}"
login: "Autenticazione avvenuta"
createToken: "È stato creato un token di accesso"
- createTokenDescription: "In caso contrario, eliminare il token di accesso tramite ({text})."
+ createTokenDescription: "Se non ne sai nulla, elimina il token di accesso: {text}."
_types:
all: "Tutte"
note: "Nuove Note"
@@ -2816,6 +2859,14 @@ _deck:
usedAsMinWidthWhenFlexible: "Se \"larghezza flessibile\" è abilitato, questa diventa la larghezza minima"
flexible: "Larghezza flessibile"
enableSyncBetweenDevicesForProfiles: "Abilita la sincronizzazione delle informazioni profilo tra dispositivi"
+ showHowToUse: "Guarda la spiegazione dell'interfaccia grafica"
+ _howToUse:
+ addColumn_title: "Aggiungere colonne"
+ addColumn_description: "Puoi selezionare un tipo di colonna e aggiungerlo."
+ settings_title: "Configurazione interfaccia grafica"
+ settings_description: "Puoi personalizzare i dettagli dell'interfaccia grafica."
+ switchProfile_title: "Selettore profilo"
+ switchProfile_description: "Puoi salvare la disposizione dell'interfaccia grafica nel tuo profilo, affinché cambi con comodità."
_columns:
main: "Principale"
widgets: "Riquadri"
@@ -3067,7 +3118,7 @@ _contextMenu:
title: "Menu contestuale"
app: "Applicazione"
appWithShift: "Applicazione Shift+Tasto"
- native: "Interfaccia utente del browser"
+ native: "Interfaccia grafica del browser"
_gridComponent:
_error:
requiredValue: "Campo obbligatorio"
@@ -3299,7 +3350,6 @@ _imageEffector:
title: "Effetto"
addEffect: "Aggiungi effetto"
discardChangesConfirm: "Scarta le modifiche ed esci?"
- nothingToConfigure: "Nessuna impostazione configurabile."
failedToLoadImage: "Impossibile caricare l'immagine"
_fxs:
chromaticAberration: "Aberrazione cromatica"
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 1eea745e0c..45d2efdf35 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -543,6 +543,7 @@ regenerate: "再生成"
fontSize: "フォントサイズ"
mediaListWithOneImageAppearance: "画像が1枚のみのメディアリストの高さ"
limitTo: "{x}を上限に"
+showMediaListByGridInWideArea: "画面幅が広いときはメディアリストを横並びで表示する"
noFollowRequests: "フォロー申請はありません"
openImageInNewTab: "画像を新しいタブで開く"
dashboard: "ダッシュボード"
@@ -1406,6 +1407,7 @@ youAreAdmin: "あなたは管理者です"
frame: "フレーム"
presets: "プリセット"
zeroPadding: "ゼロ埋め"
+nothingToConfigure: "設定項目はありません"
_imageEditing:
_vars:
@@ -2599,9 +2601,48 @@ _widgets:
_userList:
chooseList: "リストを選択"
clicker: "クリッカー"
- birthdayFollowings: "今日誕生日のユーザー"
+ birthdayFollowings: "もうすぐ誕生日のユーザー"
chat: "ダイレクトメッセージ"
+_widgetOptions:
+ showHeader: "ヘッダーを表示"
+ transparent: "背景を透明にする"
+ height: "高さ"
+ _button:
+ colored: "色付き"
+ _clock:
+ size: "サイズ"
+ thickness: "針の太さ"
+ thicknessThin: "細い"
+ thicknessMedium: "普通"
+ thicknessThick: "太い"
+ graduations: "文字盤の目盛り"
+ graduationDots: "ドット"
+ graduationArabic: "アラビア数字"
+ fadeGraduations: "目盛りをフェード"
+ sAnimation: "秒針のアニメーション"
+ sAnimationElastic: "リアル"
+ sAnimationEaseOut: "滑らか"
+ twentyFour: "24時間表示"
+ labelTime: "時刻"
+ labelTz: "タイムゾーン"
+ labelTimeAndTz: "時刻とタイムゾーン"
+ timezone: "タイムゾーン"
+ showMs: "ミリ秒を表示"
+ showLabel: "ラベルを表示"
+ _jobQueue:
+ sound: "音を鳴らす"
+ _rss:
+ url: "RSSフィードのURL"
+ refreshIntervalSec: "更新間隔(秒)"
+ maxEntries: "最大表示件数"
+ _rssTicker:
+ shuffle: "表示順をシャッフル"
+ duration: "ティッカーのスクロール速度(秒)"
+ reverse: "逆方向にスクロール"
+ _birthdayFollowings:
+ period: "期間"
+
_cw:
hide: "隠す"
show: "もっと見る"
@@ -3418,7 +3459,6 @@ _imageEffector:
title: "エフェクト"
addEffect: "エフェクトを追加"
discardChangesConfirm: "変更を破棄して終了しますか?"
- nothingToConfigure: "設定項目はありません"
failedToLoadImage: "画像の読み込みに失敗しました"
_fxs:
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 694965c03f..4a2cc3a9b8 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -2378,6 +2378,15 @@ _widgets:
clicker: "クリッカー"
birthdayFollowings: "今日誕生日のツレ"
chat: "チャットしよか"
+_widgetOptions:
+ showHeader: "ヘッダー出す"
+ height: "高さ"
+ _button:
+ colored: "色付き"
+ _clock:
+ size: "大きさ"
+ _birthdayFollowings:
+ period: "期間"
_cw:
hide: "隠す"
show: "続き見して!"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 5a70bffeef..bf8a851884 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -543,6 +543,7 @@ regenerate: "재생성"
fontSize: "글자 크기"
mediaListWithOneImageAppearance: "이미지가 1개 뿐인 미디어 목록의 높이"
limitTo: "{x}로 제한"
+showMediaListByGridInWideArea: "화면 폭이 넓을 때는 미디어 목록을 가로로 표시하기"
noFollowRequests: "처리되지 않은 팔로우 요청이 없습니다"
openImageInNewTab: "새 탭에서 이미지 열기"
dashboard: "대시보드"
@@ -1334,7 +1335,7 @@ markAsSensitiveConfirm: "이 미디어를 민감한 미디어로 설정하시겠
unmarkAsSensitiveConfirm: "이 미디어의 민감한 미디어 지정을 해제하시겠습니까?"
preferences: "환경설정"
accessibility: "접근성"
-preferencesProfile: "설정 프로필"
+preferencesProfile: "설정 프로파일"
copyPreferenceId: "설정한 ID를 복사"
resetToDefaultValue: "기본값으로 되돌리기"
overrideByAccount: "계정으로 덮어쓰기"
@@ -1347,7 +1348,7 @@ preferenceSyncConflictTitle: "서버에 설정값이 존재합니다."
preferenceSyncConflictText: "동기화를 활성화 한 항목의 설정 값은 서버에 저장되지만, 해당 항목은 이미 서버에 설정 값이 저장되어져 있습니다. 어느 쪽의 설정 값을 덮어씌울까요?"
preferenceSyncConflictChoiceMerge: "병합"
preferenceSyncConflictChoiceServer: "서버 설정값"
-preferenceSyncConflictChoiceDevice: "장치 설정값"
+preferenceSyncConflictChoiceDevice: "장치 설정 값"
preferenceSyncConflictChoiceCancel: "동기화 취소"
paste: "붙여넣기"
emojiPalette: "이모지 팔레트"
@@ -1406,6 +1407,7 @@ youAreAdmin: "당신은 관리자입니다."
frame: "프레임"
presets: "프리셋"
zeroPadding: "0으로 채우기"
+nothingToConfigure: "설정 항목이 없습니다."
_imageEditing:
_vars:
caption: "파일 설명"
@@ -1550,15 +1552,18 @@ _settings:
showPageTabBarBottom: "페이지의 탭 바를 아래쪽에 표시"
emojiPaletteBanner: "이모티콘 선택기에 고정 표시되는 프리셋을 팔레트로 등록하거나 선택기의 표시 방법을 커스터마이징할 수 있습니다."
enableAnimatedImages: "애니메이션 이미지 활성화"
+ settingsPersistence_title: "설정 영구화"
+ settingsPersistence_description1: "설정 영구화를 활성화하면 설정 정보를 잃어버리는 것을 방지할 수 있습니다."
+ settingsPersistence_description2: "환경에 따라 활성화되지 않을 수 있습니다."
_chat:
showSenderName: "발신자 이름 표시"
sendOnEnter: "엔터로 보내기"
_preferencesProfile:
- profileName: "프로필 이름"
+ profileName: "프로파일 이름"
profileNameDescription: "이 디바이스를 식별할 이름을 설정해 주세요."
profileNameDescription2: "예: '메인PC', '스마트폰' 등"
manageProfiles: "프로파일 관리"
- shareSameProfileBetweenDevicesIsNotRecommended: "여러 장치에서 동일한 프로필을 공유하는 것은 권장하지 않습니다."
+ shareSameProfileBetweenDevicesIsNotRecommended: "여러 장치에서 같은 프로파일을 공유하는 것은 권장하지 않습니다."
useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "여러 장치에서 동기화하고 싶은 설정 항목이 있는 경우에는 개별로 '여러 장치에서 동기화' 옵션을 활성화해 주십시오."
_preferencesBackup:
autoBackup: "자동 백업"
@@ -1566,7 +1571,7 @@ _preferencesBackup:
noBackupsFoundTitle: "백업을 찾을 수 없습니다"
noBackupsFoundDescription: "자동으로 생성된 백업은 찾을 수 없었지만, 수동으로 백업 파일을 저장한 경우 해당 파일을 가져와 복원할 수 있습니다."
selectBackupToRestore: "복원할 백업을 선택하세요"
- youNeedToNameYourProfileToEnableAutoBackup: "자동 백업을 활성화하려면 프로필 이름을 설정해야 합니다."
+ youNeedToNameYourProfileToEnableAutoBackup: "자동 백업을 활성화하려면 프로파일 이름을 설정해야 합니다."
autoPreferencesBackupIsNotEnabledForThisDevice: "이 장치에서 설정 자동 백업이 활성화되어 있지 않습니다."
backupFound: "설정 백업이 발견되었습니다"
forceBackup: "설정 강제 백업"
@@ -2541,6 +2546,44 @@ _widgets:
clicker: "클리커"
birthdayFollowings: "오늘이 생일인 유저"
chat: "채팅하기"
+_widgetOptions:
+ showHeader: "해더를 표시"
+ transparent: "배경을 투명하게 설정"
+ height: "높이"
+ _button:
+ colored: "색 입히기"
+ _clock:
+ size: "크기"
+ thickness: "시곗바늘의 두께"
+ thicknessThin: "얇게"
+ thicknessMedium: "보통"
+ thicknessThick: "굵게"
+ graduations: "문자반의 눈금"
+ graduationDots: "도트"
+ graduationArabic: "아라비아 숫자"
+ fadeGraduations: "눈금 페이드"
+ sAnimation: "초침 애니메이션"
+ sAnimationElastic: "사실적으로"
+ sAnimationEaseOut: "매끄럽게"
+ twentyFour: "24시간 표시"
+ labelTime: "시각"
+ labelTz: "시간대"
+ labelTimeAndTz: "시각과 시간대"
+ timezone: "시간대"
+ showMs: "밀리초 표시"
+ showLabel: "레이블 표시"
+ _jobQueue:
+ sound: "소리 재생"
+ _rss:
+ url: "RSS 필드의 URL"
+ refreshIntervalSec: "갱신 간격(초)"
+ maxEntries: "최대 표시 건수"
+ _rssTicker:
+ shuffle: "표시 순서 셔플"
+ duration: "티커 스크롤 속도(초)"
+ reverse: "역방향으로 스크롤"
+ _birthdayFollowings:
+ period: "기간"
_cw:
hide: "숨기기"
show: "더 보기"
@@ -2815,7 +2858,15 @@ _deck:
useSimpleUiForNonRootPages: "루트 이외의 페이지로 접속한 경우 UI 간략화하기"
usedAsMinWidthWhenFlexible: "'폭 자동 조정'이 활성화된 경우 최소 폭으로 사용됩니다"
flexible: "폭 자동 조정"
- enableSyncBetweenDevicesForProfiles: "프로파일 정보의 디바이스 간 동기화를 활성화"
+ enableSyncBetweenDevicesForProfiles: "프로파일 정보의 장치 간 동기화를 활성화"
+ showHowToUse: "UI 설명 보기"
+ _howToUse:
+ addColumn_title: "칼럼 추가"
+ addColumn_description: "칼럼의 종류를 선택해 추가할 수 있습니다."
+ settings_title: "UI 설정"
+ settings_description: "덱 UI의 상세 설정을 할 수 있습니다."
+ switchProfile_title: "프로파일 전환"
+ switchProfile_description: "UI의 레이아웃을 프로파일로 저장하고 언제든지 전환할 수 있습니다."
_columns:
main: "메인"
widgets: "위젯"
@@ -3299,7 +3350,6 @@ _imageEffector:
title: "이펙트"
addEffect: "이펙트를 추가"
discardChangesConfirm: "변경을 취소하고 종료하시겠습니까?"
- nothingToConfigure: "설정 항목이 없습니다."
failedToLoadImage: "이미지 로딩에 실패했습니다."
_fxs:
chromaticAberration: "색수차"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index 06856867b0..7017d81733 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -431,6 +431,8 @@ _widgets:
jobQueue: "ຄິວວຽກ"
_userList:
chooseList: "ເລືອກບັນຊີລາຍການ"
+_widgetOptions:
+ height: "ຄວາມສູງ"
_cw:
show: "ໂຫຼດເພີ່ມເຕີມ"
_visibility:
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index 27f782e611..32e021d1a1 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -1017,6 +1017,8 @@ _widgets:
jobQueue: "Job Queue"
_userList:
chooseList: "Kies een lijst."
+_widgetOptions:
+ height: "Hoogte"
_cw:
show: "Laad meer"
_visibility:
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 6f60223342..7bf3f387b8 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -639,6 +639,10 @@ _widgets:
userList: "Brukerliste"
_userList:
chooseList: "Velg liste"
+_widgetOptions:
+ height: "Høyde"
+ _clock:
+ size: "Størrelse"
_cw:
hide: "Skjul"
show: "Vis mer"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 18dd43e938..34540239fb 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -1361,6 +1361,14 @@ _widgets:
_userList:
chooseList: "Wybierz listę"
clicker: "Clicker"
+_widgetOptions:
+ height: "Wysokość"
+ _button:
+ colored: "Kolorowe"
+ _clock:
+ size: "Rozmiar"
+ _birthdayFollowings:
+ period: "Czas trwania"
_cw:
hide: "Ukryj"
show: "Załaduj więcej"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 2ee5b06ec2..7552852c4e 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -2489,6 +2489,15 @@ _widgets:
clicker: "Clicker"
birthdayFollowings: "Usuários de aniversário hoje"
chat: "Conversar com usuário"
+_widgetOptions:
+ showHeader: "Exibir cabeçalho"
+ height: "Altura"
+ _button:
+ colored: "Colorido"
+ _clock:
+ size: "Tamanho"
+ _birthdayFollowings:
+ period: "Duração"
_cw:
hide: "Esconder"
show: "Carregar mais"
@@ -3230,7 +3239,6 @@ _imageEffector:
title: "Efeitos"
addEffect: "Adicionar efeitos"
discardChangesConfirm: "Tem certeza que deseja sair? Há mudanças não salvas."
- nothingToConfigure: "Não há nada para configurar"
_fxs:
chromaticAberration: "Aberração cromática"
glitch: "Glitch"
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index d1ff2f1040..bf352b45c2 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -1301,6 +1301,12 @@ _widgets:
jobQueue: "coada de job-uri"
_userList:
chooseList: "Selectează o listă"
+_widgetOptions:
+ height: "Înălţime"
+ _button:
+ colored: "Colorat"
+ _clock:
+ size: "Dimensiune"
_cw:
show: "Incarcă mai mult"
_visibility:
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index c77487c41b..f14a0019e9 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -83,6 +83,8 @@ files: "Файлы"
download: "Скачать"
driveFileDeleteConfirm: "Удалить файл «{name}»? Заметки с ним также будут удалены."
unfollowConfirm: "Отписаться от {name} ?"
+cancelFollowRequestConfirm: "Вы уверены, что хотите отменить запрос на подписку пользователю {name}?"
+rejectFollowRequestConfirm: "Отклонить запрос на подписку от {name}?"
exportRequested: "Вы запросили экспорт. Это может занять некоторое время. Результат будет добавлен на «Диск»."
importRequested: "Вы запросили импорт. Это может занять некоторое время."
lists: "Списки"
@@ -199,7 +201,7 @@ searchWith: "Найденное «{q}»"
youHaveNoLists: "У вас нет ни одного списка"
followConfirm: "Подписаться на {name}?"
proxyAccount: "Учётная запись прокси"
-proxyAccountDescription: "Учетная запись прокси предназначена служить подписчиком на пользователей с других сайтов. Например, если пользователь добавит кого-то с другого сайта а список, деятельность того не отобразится, пока никто с этого же сайта не подписан на него. Чтобы это стало возможным, на него подписывается прокси."
+proxyAccountDescription: "Учетная запись прокси предназначена служить подписчиком на пользователей с других сайтов. Например: если пользователь добавит кого-то с другого сайта в список, то деятельность того не отобразится, пока никто с этого же сайта не подписан на него. Чтобы это стало возможным, на него подписывается прокси."
host: "Хост"
selectSelf: "Выбрать себя"
selectUser: "Выберите пользователя"
@@ -302,6 +304,7 @@ uploadFromUrlMayTakeTime: "Загрузка может занять некото
uploadNFiles: "Загрузить {n} файл"
explore: "Обзор"
messageRead: "Прочитали"
+readAllChatMessages: "Отметить прочитанным"
noMoreHistory: "История закончилась"
startChat: "Начать чат"
nUsersRead: "Прочитали {n}"
@@ -328,11 +331,13 @@ dark: "Тёмный"
lightThemes: "Светлые темы"
darkThemes: "Тёмные темы"
syncDeviceDarkMode: "Синхронизировать с тёмной темой системы"
+switchDarkModeManuallyWhenSyncEnabledConfirm: "Включена функция \"{x}\". Отключить синхронизацию, чтобы переключать режим вручную?"
drive: "Диск"
fileName: "Имя файла"
selectFile: "Выберите файл"
selectFiles: "Выберите файлы"
selectFolder: "Выберите папку"
+unselectFolder: "Снять выбор"
selectFolders: "Выберите папки"
fileNotSelected: "Файл не выбран"
renameFile: "Переименовать файл"
@@ -345,6 +350,7 @@ addFile: "Добавить файл"
showFile: "Посмотреть файл"
emptyDrive: "Диск пуст"
emptyFolder: "Папка пуста"
+dropHereToUpload: "Переместите файл сюда"
unableToDelete: "Удаление невозможно"
inputNewFileName: "Введите имя нового файла"
inputNewDescription: "Введите новую подпись"
@@ -458,7 +464,7 @@ moderator: "Модератор"
moderation: "Модерация"
moderationNote: "Примечания модератора"
moderationNoteDescription: "Вы можете заполнять заметки, которые будут доступны только модераторам."
-addModerationNote: ""
+addModerationNote: "Оставить заметку"
moderationLogs: "Журнал модерации"
nUsersMentioned: "Упомянуло пользователей: {n}"
securityKeyAndPasskey: "Ключ безопасности и парольная фраза"
@@ -602,7 +608,7 @@ installedDate: "Дата установки"
lastUsedDate: "Дата использования"
state: "Состояние"
sort: "Сортировать"
-ascendingOrder: "по возрастанию"
+ascendingOrder: "По возрастанию"
descendingOrder: "По убыванию"
scratchpad: "Когтеточка"
scratchpadDescription: "«Когтеточка» — это место для опытов с AiScript. Здесь можно писать программы, взаимодействующие с Misskey, запускать и смотреть что из этого получается."
@@ -623,9 +629,9 @@ removeAllFollowingDescription: "Отменить все подписки с до
userSuspended: "Эта учётная запись заморожена"
userSilenced: "Этот пользователь был заглушен"
yourAccountSuspendedTitle: "Эта учетная запись заблокирована"
-yourAccountSuspendedDescription: "Эта учетная запись была заблокирована из-за нарушения условий предоставления услуг сервера. Свяжитесь с администратором, если вы хотите узнать более подробную причину. Пожалуйста, не создавайте новую учетную запись."
+yourAccountSuspendedDescription: "Этот аккаунт нарушил ToS сервера, поэтому был заморожен. Свяжитесь с администратором, чтобы узнать подробности. Не пытайтесь создать новый аккаунт."
tokenRevoked: "Токен недействителен"
-tokenRevokedDescription: "Срок действия вашего токена входа истек. Пожалуйста, войдите снова."
+tokenRevokedDescription: "Токен входа устарел. Пожалуйста, войдите снова."
accountDeleted: "Учетная запись удалена"
accountDeletedDescription: "Эта учетная запись удалена"
menu: "Меню"
@@ -684,9 +690,9 @@ smtpPort: "Порт"
smtpUser: "Имя пользователя"
smtpPass: "Пароль"
emptyToDisableSmtpAuth: "Не заполняйте имя пользователя и пароль, чтобы отключить аутентификацию в SMTP."
-smtpSecure: "Использовать SSL/TLS для SMTP-соединений"
+smtpSecure: "Использовать SSL/TLS"
smtpSecureInfo: "Выключите при использовании STARTTLS."
-testEmail: "Проверка доставки электронной почты"
+testEmail: "Отправить тестовое письмо"
wordMute: "Скрытие слов"
wordMuteDescription: "Сведите к минимуму записи, содержащие указанное утверждение. Нажмите на свернутую запись, чтобы отобразить ее."
hardWordMute: "Строгое скрытие слов"
@@ -772,6 +778,7 @@ lockedAccountInfo: "Даже если вы вручную подтверждае
alwaysMarkSensitive: "Отмечать файлы как «содержимое не для всех» по умолчанию"
loadRawImages: "Сразу показывать изображения в полном размере"
disableShowingAnimatedImages: "Не проигрывать анимацию"
+disableShowingAnimatedImages_caption: "Если анимации всё равно не работают, проверьте настройки специальных возможностей и режимы экономии заряда в браузере или системе"
highlightSensitiveMedia: "Выделять содержимое не для всех"
verificationEmailSent: "Вам отправлено письмо для подтверждения. Пройдите, пожалуйста, по ссылке из письма, чтобы завершить проверку."
notSet: "Не настроено"
@@ -779,7 +786,7 @@ emailVerified: "Адрес электронной почты подтвержд
noteFavoritesCount: "Количество добавленного в избранное"
pageLikesCount: "Количество понравившихся страниц"
pageLikedCount: "Количество страниц, понравившихся другим"
-contact: "Как связаться"
+contact: "Почта для связи"
useSystemFont: "Использовать шрифт, предлагаемый системой"
clips: "Подборки"
experimentalFeatures: "Экспериментальные функции"
@@ -838,7 +845,7 @@ showingPastTimeline: "Отображается старая лента"
clear: "Очистить"
markAllAsRead: "Отметить всё как прочитанное"
goBack: "Выход"
-unlikeConfirm: "В самом деле отменить «нравится»?"
+unlikeConfirm: "В самом деле убрать «нравится»?"
fullView: "Полный вид"
quitFullView: "Закрыть полный вид"
addDescription: "Добавить описание"
@@ -883,7 +890,7 @@ priority: "Приоритет"
high: "Высокий"
middle: "Средне"
low: "Низкий"
-emailNotConfiguredWarning: "Не указан адрес электронной почты"
+emailNotConfiguredWarning: "Адрес почты пустует"
ratio: "Соотношение"
previewNoteText: "Предварительный просмотр"
customCss: "Индивидуальный CSS"
@@ -963,13 +970,13 @@ reflectMayTakeTime: "Изменения могут занять время дл
failedToFetchAccountInformation: "Не удалось получить информацию об аккаунте"
rateLimitExceeded: "Ограничение скорости превышено"
cropImage: "Кадрирование"
-cropImageAsk: "Нужно ли кадрировать изображение?"
+cropImageAsk: "Обрезать изображение?"
cropYes: "Обрезать"
cropNo: "Не обрезать"
file: "Файлы"
recentNHours: "Последние {n} ч"
recentNDays: "Последние {n} сут"
-noEmailServerWarning: "Почтовый сервер не установлен "
+noEmailServerWarning: "Отправка писем выключена"
thereIsUnresolvedAbuseReportWarning: "Остались нерешённые жалобы"
recommended: "Рекомендуем"
check: "Проверить"
@@ -983,7 +990,7 @@ document: "Документ"
numberOfPageCache: "Количество сохранённых страниц в кэше"
numberOfPageCacheDescription: "Описание количества страниц в кэше"
logoutConfirm: "Вы хотите выйти из аккаунта?"
-logoutWillClearClientData: "Когда вы выйдете из системы, информация о конфигурации клиента будет удалена из браузера.Чтобы иметь возможность восстановить информацию о вашей конфигурации при повторном входе в систему, пожалуйста, включите опцию автоматического резервного копирования в настройках."
+logoutWillClearClientData: "Выход из аккаунта удалит настройки клиента из этого браузера. Включите автоматическое резервное копирование, чтобы иметь возможность восстановить настройки при повторном входе."
lastActiveDate: "Последняя дата использования"
statusbar: "Статусбар"
pleaseSelect: "Пожалуйста, выберите"
@@ -1002,6 +1009,7 @@ failedToUpload: "Сбой выгрузки"
cannotUploadBecauseInappropriate: "Файл не может быть загружен, так как было установлено, что он может содержать неприемлемое содержимое."
cannotUploadBecauseNoFreeSpace: "Файл не может быть загружен, так как не осталось места на диске"
cannotUploadBecauseExceedsFileSizeLimit: "Файл не может быть загружен, так как он превышает лимит размера файла."
+cannotUploadBecauseUnallowedFileType: "Формат файла не подходит"
beta: "Бета"
enableAutoSensitive: "Автоматическое определение содержимого не для всех"
enableAutoSensitiveDescription: "Позволяет определять наличие содержимого не для всех при помощи искусственного интеллекта там, где это возможно. Даже если эту опцию отключить, она всё равно может быть включена на весь инстанс."
@@ -1017,6 +1025,9 @@ pushNotificationAlreadySubscribed: "Push-уведомления уже вклю
pushNotificationNotSupported: "Push-уведмления не поддерживаются инстансом или браузером"
sendPushNotificationReadMessage: "Удалять push-уведомления когда сообщение или прочитано"
sendPushNotificationReadMessageCaption: "На мгновение появится уведомление \"{emptyPushNotificationMessage}\". Расход заряда батареи может увеличиться "
+pleaseAllowPushNotification: "Пожалуйста, разрешите уведомление в браузере от сайта"
+browserPushNotificationDisabled: "Вы не дали разрешение на уведомления сайту"
+browserPushNotificationDisabledDescription: "Разрешите уведомления в настройках браузера от {serverName}, чтобы включить PUSH уведомления"
windowMaximize: "Развернуть"
windowMinimize: "Свернуть"
windowRestore: "Восстановить"
@@ -1038,7 +1049,7 @@ roles: "Роли"
role: "Роль"
noRole: "Нет роли"
normalUser: "Обычный пользователь"
-undefined: "неопределён"
+undefined: "неопределённо"
assign: "Назначить"
unassign: "Отменить назначение"
color: "Цвет"
@@ -1053,6 +1064,7 @@ permissionDeniedError: "Операция запрещена"
permissionDeniedErrorDescription: "У этой учетной записи нет разрешения на выполнение этой операции."
preset: "Шаблоны"
selectFromPresets: "Выбрать из шаблонов"
+custom: "Пользовательские"
achievements: "Достижения"
gotInvalidResponseError: "Сервер ответил ошибкой"
gotInvalidResponseErrorDescription: "Сервер временно не доступен. Возможно проводятся технические работы, или сервер отключен."
@@ -1091,6 +1103,7 @@ prohibitedWordsDescription2: "Разделение пробелом создаё
hiddenTags: "Скрытые хештеги"
hiddenTagsDescription: "Установленные теги не будут отображаться в тренде, можно установить несколько тегов."
notesSearchNotAvailable: "Поиск заметок недоступен"
+usersSearchNotAvailable: "Функция \"поиска пользователей\" отключена"
license: "Лицензия"
unfavoriteConfirm: "Удалить избранное?"
myClips: "Мои подборки"
@@ -1129,7 +1142,7 @@ vertical: "Вертикально"
horizontal: "Горизонтально"
position: "Позиция"
serverRules: "Правила сервера"
-pleaseConfirmBelowBeforeSignup: "Для регистрации на данном сервере, необходимо согласится с нижеследующими положениями."
+pleaseConfirmBelowBeforeSignup: "Прочитайте и согласитесь с информацией ниже, чтобы продолжить"
pleaseAgreeAllToContinue: "Чтобы продолжить, необходимо поставить отметки во всех полях \"согласен\"."
continue: "Продолжить"
preservedUsernames: "Зарезервированные имена пользователей"
@@ -1178,6 +1191,9 @@ expirationDate: "Дата истечения"
noExpirationDate: "Бессрочно"
inviteCodeUsedAt: "Дата и время, когда был использован пригласительный код"
registeredUserUsingInviteCode: "Пользователи, которые использовали пригласительный код"
+waitingForMailAuth: "Подтвердите вашу электронную почту"
+inviteCodeCreator: "Создатель приглашения"
+usedAt: "Использовано"
unused: "Неиспользованное"
used: "Использован"
expired: "Срок действия приглашения истёк"
@@ -1186,43 +1202,59 @@ beSureToReadThisAsItIsImportant: "Это важно, поэтому, пожал
iHaveReadXCarefullyAndAgree: "Я прочитал(а) и согласен(сна) с условиями \"{x}"
dialog: "Диалог"
icon: "Аватар"
+forYou: "Для вас"
currentAnnouncements: "Текущие новости"
pastAnnouncements: "Предыдущие новости"
youHaveUnreadAnnouncements: "У вас есть непрочитанные уведомления"
+useSecurityKey: "Используйте ключ безопасности или Passkey, следуя подсказкам браузера"
replies: "Ответы"
renotes: "Репост"
loadReplies: "Показать ответы"
loadConversation: "Загрузить беседу"
pinnedList: "Закреплённый список"
keepScreenOn: "Держать экран включённым"
+verifiedLink: "Эта ссылка принадлежит пользователю"
+notifyNotes: "Оповещать о публикациях"
unnotifyNotes: "Отписаться от сообщений"
authentication: "Аутентификация"
authenticationRequiredToContinue: "Пожалуйста, пройдите аутентификацию, чтобы продолжить"
dateAndTime: "Дата и время"
showRenotes: "Показывать репосты"
edited: "Изменено"
+notificationRecieveConfig: "Настроить оповещения"
mutualFollow: "Взаимные подписки"
followingOrFollower: "Подписки или подписчики"
fileAttachedOnly: "Только заметки с файлами"
showRepliesToOthersInTimeline: "Показывать ответы в ленте"
+hideRepliesToOthersInTimeline: "Скрыть чужие ответы в ленте"
showRepliesToOthersInTimelineAll: "Показывать в ленте ответы пользователей, на которых вы подписаны"
hideRepliesToOthersInTimelineAll: "Скрывать в ленте ответы пользователей, на которых вы подписаны"
+confirmShowRepliesAll: "Это нельзя будет отменить. Показать ответы от всех, на кого вы подписаны?"
+confirmHideRepliesAll: "Это нельзя будет отменить. Скрыть ответы от всех, на кого вы подписаны?"
+externalServices: "Интеграции"
sourceCode: "Исходный код"
sourceCodeIsNotYetProvided: "Исходный код пока не доступен. Свяжитесь с администратором, чтобы исправить эту проблему."
repositoryUrl: "Ссылка на репозиторий"
repositoryUrlDescription: "Если вы используете Misskey как есть (без изменений в исходном коде), введите https://github.com/misskey-dev/misskey"
+repositoryUrlOrTarballRequired: "Если репозиторий закрыт, необходимо предоставить ссылку на tarball. Подробности см. в файле \".config/example.yml\""
feedback: "Обратная связь"
+feedbackUrl: "Ссылка для обратной связи"
+impressum: "О владельце"
privacyPolicy: "Политика Конфиденциальности"
privacyPolicyUrl: "Ссылка на Политику Конфиденциальности"
tosAndPrivacyPolicy: "Условия использования и политика конфиденциальности"
avatarDecorations: "Украшения для аватара"
attach: "Прикрепить"
+detach: "Открепить"
detachAll: "Убрать всё"
angle: "Угол"
flip: "Переворот"
showAvatarDecorations: "Показать украшения для аватара"
+releaseToRefresh: "Отпустите, чтобы обновить"
+refreshing: "Обновление..."
pullDownToRefresh: "Опустите что бы обновить"
useGroupedNotifications: "Отображать уведомления сгруппировано"
+emailVerificationFailedError: "Не смогли подтвердить почту. Вероятно, истек срок письма"
cwNotationRequired: "Если включена опция «Скрыть содержимое», необходимо написать аннотацию."
doReaction: "Добавить реакцию"
code: "Код"
@@ -1232,34 +1264,49 @@ overwriteContentConfirm: "Текущее содержимое будет пер
seasonalScreenEffect: "Эффект времени года на экране"
decorate: "Украсить"
addMfmFunction: "Добавить MFM"
+enableQuickAddMfmFunction: "Показывать расширенный выбор MFM"
bubbleGame: "BubbleGame"
sfx: "Звуковые эффекты"
soundWillBePlayed: "Будет воспроизведен звук"
showReplay: "Показать повтор"
+replay: "Ответить"
endReplay: "Конец повтора"
lastNDays: "Последние {n} сут"
hemisphere: "Место проживания"
userSaysSomethingSensitive: "Сообщение, содержит конфиденциальные файлы от {name}"
enableHorizontalSwipe: "Смахните в сторону, чтобы сменить вкладки"
+loading: "Загрузка"
surrender: "Этот пост не может быть отменен."
gameRetry: "Повторить попытку"
notUsePleaseLeaveBlank: "Если не используется, оставьте пустым"
+useTotp: "Включить двухэтапную проверку"
+useBackupCode: "Использовать резервные коды"
+launchApp: "Запустить приложение"
useNativeUIForVideoAudioPlayer: "Использовать интерфейс браузера при проигрывании видео и звука"
keepOriginalFilename: "Сохранять исходное имя файла"
keepOriginalFilenameDescription: "Если вы выключите данную настройку, имена файлов будут автоматически заменены случайной строкой при загрузке."
+noDescription: "Нет описания"
alwaysConfirmFollow: "Всегда подтверждать подписку"
inquiry: "Связаться"
+tryAgain: "Попробуйте еще раз позже"
+confirmWhenRevealingSensitiveMedia: "Спрашивать перед открытием NSFW контента"
+sensitiveMediaRevealConfirm: "Возможно, это NSFW контент. Показать?"
+createdLists: "Созданные списки"
+createdAntennas: "Созданные антенны"
fromX: "Из {x}"
genEmbedCode: "Сгенерировать код для "
noteOfThisUser: "Список заметок этого пользователя"
clipNoteLimitExceeded: "К этому клипу больше нельзя добавить заметки"
performance: "Производительность"
modified: "Изменено"
+discard: "Отменить"
+thereAreNChanges: "Изменено: {n}"
signinWithPasskey: "Войдите в систему, используя свой пароль"
unknownWebAuthnKey: "Неизвестный ключ"
passkeyVerificationFailed: "Ошибка проверка ключа доступа "
+passkeyVerificationSucceededButPasswordlessLoginDisabled: "Проверка Passkey выполнена, но вход без пароля отключен"
messageToFollower: "Сообщение подписчикам"
-testCaptchaWarning: "Эта функция предназначена для тестирования CAPTCHA. <strong>Не использовать это в рабочей среде</strong>"
+testCaptchaWarning: "Эта тестовая CAPTCHA. <strong>Не используйте её!</strong>"
prohibitedWordsForNameOfUser: "Запрещенные слова (имя пользователя)"
prohibitedWordsForNameOfUserDescription: "Если имя пользователя содержит строку из этого списка, изменение имени пользователя будет запрещено. На пользователей с правами модератора это ограничение не распространяется. Имена пользователей также проверяются путём замены всех букв в нижнем регистре"
yourNameContainsProhibitedWords: "Имя, которое вы пытаетесь изменить, содержит запрещенную строку символов"
@@ -1268,24 +1315,65 @@ thisContentsAreMarkedAsSigninRequiredByAuthor: "Автор сообщения у
lockdown: "Доступ ограничен"
pleaseSelectAccount: "Выберите свой аккаунт"
availableRoles: "Доступные роли"
+federationSpecified: "Сервер работает через белый список федерации. Связь с другими серверами ограничена"
federationDisabled: "Федерация отключена для этого сервера. Вы не можете взаимодействовать с пользователями на других серверах."
draft: "Черновик"
+draftsAndScheduledNotes: "Черновики и отложенные публикации"
+confirmOnReact: "Подтверждать добавление реакции"
+reactAreYouSure: "Добавить {emoji}?"
markAsSensitiveConfirm: "Отметить контент как чувствительный?"
+unmarkAsSensitiveConfirm: "Снять пометку о NSFW контенте?"
preferences: "Основное"
+accessibility: "Специальные возможности"
+preferencesProfile: "Настройки профиля"
+copyPreferenceId: "Копировать ID настройки"
resetToDefaultValue: "Сбросить настройки до стандартных"
+overrideByAccount: "Переопределить этим аккаунтом"
+untitled: "Без названия"
+noName: "Имя не указано"
+skip: "Пропустить"
syncBetweenDevices: "Синхронизировать между устройствами"
postForm: "Форма отправки"
textCount: "Количество символов"
information: "Описание"
inMinutes: "мин"
inDays: "сут"
+schedule: "Отложить"
+scheduled: "Отложено"
widgets: "Виджеты"
+deviceInfo: "Об устройстве"
+deviceInfoDescription: "Эта информация может быть полезна при обращении в поддержку"
+youAreAdmin: "Вы администратор"
+frame: "Рамки"
presets: "Шаблоны"
+zeroPadding: "Без отступов"
+nothingToConfigure: "Нечего менять"
_imageEditing:
_vars:
+ caption: "Описание файла"
filename: "Имя файла"
+ filename_without_ext: "Имя файла без расширения"
+ year: "Год создания"
+ month: "Месяц создания"
+ day: "День создания"
+ hour: "Час создания"
+ minute: "Минуты создания"
+ second: "Секунды создания"
+ camera_model: "Модель камеры"
+ camera_lens_model: "Модель линзы"
+ camera_mm: "Фокусное расстояние"
+ camera_mm_35: "Фокусное расстояние (экв. 35 мм)"
+ camera_f: "Диафрагма"
+ camera_s: "Выдержка"
+ camera_iso: "ISO"
+ gps_lat: "Широта"
+ gps_long: "Долгота"
_imageFrameEditor:
+ title: "Редактировать рамку"
header: "Заголовок"
+ footer: "Нижняя часть"
+ borderThickness: "Толщина рамки"
+ labelThickness: "Толщина границ"
font: "Шрифт"
fontSerif: "Антиква (с засечками)"
fontSansSerif: "Гротеск (без засечек)"
@@ -1661,6 +1749,7 @@ _emailUnavailable:
disposable: "Временный адрес электронной почты не принимается"
mx: "Неверный почтовый сервер"
smtp: "Почтовый сервер не отвечает"
+ banned: "Этот адрес почты недоступен"
_ffVisibility:
public: "Общедоступны"
followers: "Показываются только подписчикам"
@@ -1921,6 +2010,7 @@ _permissions:
"read:gallery-likes": "Просмотр списка понравившегося в галерее"
"write:gallery-likes": "Изменение списка понравившегося в галерее"
"write:admin:reset-password": "Сбросить пароль пользователю"
+ "write:admin:send-email": "Отправить письмо"
"write:chat": "Писать и удалять сообщения"
_auth:
shareAccessTitle: "Разрешения для приложений"
@@ -1976,6 +2066,14 @@ _widgets:
chooseList: "Выберите список"
clicker: "Счётчик щелчков"
birthdayFollowings: "Пользователи, у которых сегодня день рождения"
+_widgetOptions:
+ height: "Высота"
+ _button:
+ colored: "Выделена цветом"
+ _clock:
+ size: "Размер"
+ _birthdayFollowings:
+ period: "Длительность"
_cw:
hide: "Спрятать"
show: "Показать"
@@ -2245,6 +2343,7 @@ _abuseReport:
mail: "Электронная почта"
webhook: "Вебхук"
_captions:
+ mail: "Уведомлять модераторов по почте (только при поступлении жалоб)"
webhook: "Отправить уведомление Системному Вебхуку при получении или разрешении жалоб."
notifiedWebhook: "Используемый Вебхук"
_moderationLogTypes:
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 937fbdfebf..b6289f4e04 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -1232,6 +1232,14 @@ _widgets:
aichan: "Ai"
_userList:
chooseList: "Vyberte zoznam"
+_widgetOptions:
+ height: "Výška"
+ _button:
+ colored: "Farebné"
+ _clock:
+ size: "Veľkosť"
+ _birthdayFollowings:
+ period: "Trvanie"
_cw:
hide: "Skryť"
show: "Zobraziť viac"
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index c0fd267546..560f4a187c 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -639,6 +639,9 @@ _widgets:
jobQueue: "Jobbkö"
_userList:
chooseList: "Välj lista"
+_widgetOptions:
+ _clock:
+ size: "Storlek"
_cw:
hide: "Dölj"
show: "Ladda mer"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index e4c30c0101..4a58abe521 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -83,6 +83,8 @@ files: "ไฟล์"
download: "ดาวน์โหลด"
driveFileDeleteConfirm: "ต้องการลบไฟล์ “{name}” ใช่ไหม? โน้ตที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย"
unfollowConfirm: "ต้องการเลิกติดตาม {name} ใช่ไหม?"
+cancelFollowRequestConfirm: "ยกเลิกคำขอติดตาม {name} ใช่ไหม?"
+rejectFollowRequestConfirm: "ปฏิเสธคำขอติดตามจาก {name} ใช่ไหม?"
exportRequested: "คุณได้ร้องขอการส่งออก อาจใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว"
importRequested: "คุณได้ร้องขอการนำเข้า การดำเนินการนี้อาจใช้เวลาสักครู่"
lists: "รายชื่อ"
@@ -204,7 +206,7 @@ host: "โฮสต์"
selectSelf: "เลือกตัวเอง"
selectUser: "เลือกผู้ใช้งาน"
recipient: "ผู้รับ"
-annotation: "หมายเหตุประกอบ"
+annotation: "ข้อความเกริ่น"
federation: "สหพันธ์"
instances: "เซิร์ฟเวอร์"
registeredAt: "วันที่ลงทะเบียน"
@@ -222,7 +224,7 @@ operations: "ดำเนินการ"
software: "ซอฟต์แวร์"
softwareName: "ชื่อซอฟต์แวร์"
version: "เวอร์ชั่น"
-metadata: "Metadata"
+metadata: "เมทาเดต้า"
withNFiles: "{n} ไฟล์"
monitor: "มอนิเตอร์"
jobQueue: "คิวงาน"
@@ -302,6 +304,7 @@ uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เ
uploadNFiles: "อัปโหลด {n} ไฟล์"
explore: "สำรวจ"
messageRead: "อ่านแล้ว"
+readAllChatMessages: "ทำเครื่องหมายใส่ข้อความทั้งหมดว่าอ่านแล้ว"
noMoreHistory: "ไม่มีประวัติเพิ่มเติม"
startChat: "เริ่มแชต"
nUsersRead: "อ่านโดย {n}"
@@ -334,6 +337,7 @@ fileName: "ชื่อไฟล์"
selectFile: "เลือกไฟล์"
selectFiles: "เลือกไฟล์"
selectFolder: "เลือกโฟลเดอร์"
+unselectFolder: "ยกเลิกการเลือกโฟลเดอร์"
selectFolders: "เลือกโฟลเดอร์"
fileNotSelected: "ยังไม่ได้เลือกไฟล์"
renameFile: "เปลี่ยนชื่อไฟล์"
@@ -346,6 +350,7 @@ addFile: "เพิ่มไฟล์"
showFile: "แสดงไฟล์"
emptyDrive: "ไดรฟ์ของคุณว่างเปล่านะ"
emptyFolder: "โฟลเดอร์นี้ว่างเปล่า"
+dropHereToUpload: "ดรอปไฟล์ลงที่นี่เพื่ออัปโหลด"
unableToDelete: "ไม่สามารถลบออกได้"
inputNewFileName: "ป้อนชื่อไฟล์ใหม่"
inputNewDescription: "กรุณาใส่แคปชั่นใหม่"
@@ -428,7 +433,7 @@ antennaKeywordsDescription: "คั่นด้วยเว้นวรรคส
notifyAntenna: "แจ้งเตือนเกี่ยวกับโน้ตใหม่"
withFileAntenna: "เฉพาะโน้ตที่มีไฟล์"
excludeNotesInSensitiveChannel: "ไม่รวมโน้ตจากช่องเนื้อหาละเอียดอ่อน"
-enableServiceworker: "เปิดใช้งานการแจ้งเตือนแบบพุชไปยังเบราว์เซอร์ของคุณ"
+enableServiceworker: "เปิดใช้งานการแจ้งเตือนแบบพุชไปยังเบราว์เซอร์"
antennaUsersDescription: "ระบุหนึ่งชื่อผู้ใช้ต่อบรรทัด"
caseSensitive: "อักษรพิมพ์ใหญ่-พิมพ์เล็กความหมายต่างกัน"
withReplies: "รวมตอบกลับ"
@@ -538,6 +543,7 @@ regenerate: "สร้างอีกครั้ง"
fontSize: "ขนาดตัวอักษร"
mediaListWithOneImageAppearance: "ความสูงของรายการสื่อที่มีเพียงรูปเดียว"
limitTo: "จำกัดไว้ที่ {x}"
+showMediaListByGridInWideArea: "เมื่อหน้าจอกว้างยาวขึ้น ให้เรียงรายการสื่อเป็นแนวนอน"
noFollowRequests: "คุณไม่มีคำขอติดตามที่รอดำเนินการ"
openImageInNewTab: "เปิดรูปภาพในแท็บใหม่"
dashboard: "หน้ากระดานหลัก"
@@ -612,7 +618,7 @@ uiInspectorDescription: "คุณสามารถตรวจสอบรา
output: "เอาท์พุต"
script: "สคริปต์"
disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ"
-updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล"
+updateRemoteUser: "อัปเดตข้อมูลผู้ใช้ระยะไกล"
unsetUserAvatar: "เลิกตั้งไอคอน"
unsetUserAvatarConfirm: "ต้องการเลิกตั้งไอคอนประจำตัวหรือไม่?"
unsetUserBanner: "เลิกตั้งแบนเนอร์"
@@ -773,6 +779,7 @@ lockedAccountInfo: "แม้ว่าการอนุมัติการต
alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนเป็นค่าเริ่มต้น"
loadRawImages: "โหลดภาพต้นฉบับแทนการแสดงภาพขนาดย่อ"
disableShowingAnimatedImages: "ไม่ต้องเล่นภาพเคลื่อนไหว"
+disableShowingAnimatedImages_caption: "หากภาพเคลื่อนไหวไม่เล่นแม่จะปิดตั้งค่านี้ไปแล้ว อาจเป็นกรณีที่การตั้งค่าการช่วยการเข้าถึงหรือการประหยัดพลังงาน ของเบราว์เซอร์/OS เข้าแทรกแซง"
highlightSensitiveMedia: "ไฮไลท์สื่อที่มีเนื้อหาละเอียดอ่อน"
verificationEmailSent: "ได้ส่งอีเมลยืนยันแล้ว กรุณาเข้าลิงก์ที่ระบุในอีเมลเพื่อทำการตั้งค่าให้เสร็จสิ้น"
notSet: "ไม่ได้ตั้งค่า"
@@ -887,7 +894,7 @@ low: "ต่ำ"
emailNotConfiguredWarning: "ยังไม่ได้ตั้งค่าที่อยู่อีเมล"
ratio: "อัตราส่วน"
previewNoteText: "แสดงตัวอย่าง"
-customCss: "CSS ที่กำหนดเอง"
+customCss: "CSS แบบกำหนดเอง"
customCssWarn: "ควรใช้การตั้งค่านี้เฉพาะต่อเมื่อคุณรู้มันใช้ทำอะไร การตั้งค่าที่ไม่เหมาะสมอาจทำให้ไคลเอ็นต์ไม่สามารถใช้งานได้อย่างถูกต้อง"
global: "ทั่วโลก"
squareAvatars: "แสดงไอคอนประจำตัวเป็นสี่เหลี่ยม"
@@ -930,7 +937,7 @@ unmuteThread: "เลิกปิดเสียงเธรด"
followingVisibility: "การมองเห็นที่เรากำลังติดตาม"
followersVisibility: "การมองเห็นผู้ที่กำลังติดตามเรา"
continueThread: "ดูความต่อเนื่องเธรด"
-deleteAccountConfirm: "การดำเนินการนี้จะลบบัญชีของคุณอย่างถาวรเลยนะ แน่ใจหรอดำเนินการ?"
+deleteAccountConfirm: "บัญชีจะถูกลบ ดำเนินการต่อใช่ไหม?"
incorrectPassword: "รหัสผ่านไม่ถูกต้อง"
incorrectTotp: "รหัสยืนยันตัวตนแบบใช้ครั้งเดียวที่ท่านได้ระบุมานั้น ไม่ถูกต้องหรือหมดอายุลงแล้วค่ะ"
voteConfirm: "ต้องการโหวต “{choice}” ใช่ไหม?"
@@ -991,7 +998,7 @@ pleaseSelect: "ตัวเลือก"
reverse: "พลิก"
colored: "สี"
refreshInterval: "ความถี่ในการอัปเดต"
-label: "ป้ายชื่อ"
+label: "ป้าย"
type: "รูปแบบ"
speed: "ความเร็ว"
slow: "ช้า"
@@ -1019,6 +1026,9 @@ pushNotificationAlreadySubscribed: "การแจ้งเตือนแบ
pushNotificationNotSupported: "เบราว์เซอร์หรือเซิร์ฟเวอร์ไม่รองรับการแจ้งเตือนแบบพุช"
sendPushNotificationReadMessage: "ลบการแจ้งเตือนแบบพุชเมื่ออ่านการแจ้งเตือนหรือข้อความที่เกี่ยวข้องแล้ว"
sendPushNotificationReadMessageCaption: "อาจทำให้อุปกรณ์ของคุณใช้พลังงานมากขึ้น"
+pleaseAllowPushNotification: "โปรดอนุญาตการตั้งค่าการแจ้งเตือนของเบราว์เซอร์"
+browserPushNotificationDisabled: "ขอสิทธิ์ส่งการแจ้งเตือนล้มเหลว"
+browserPushNotificationDisabledDescription: "ไม่มีสิทธิ์ในการส่งการแจ้งเตือนจาก {serverName} โปรดอนุญาตการแจ้งเตือนในตั้งค่าของเบราว์เซอร์ แล้วลองอีกครั้ง"
windowMaximize: "ขยายใหญ่สุด"
windowMinimize: "ย่อเล็กที่สุด"
windowRestore: "เลิกทำ"
@@ -1099,8 +1109,8 @@ license: "ใบอนุญาต"
unfavoriteConfirm: "ลบออกจากรายการโปรดแน่ใจหรอ?"
myClips: "คลิปของฉัน"
drivecleaner: "ทำความสะอาดไดรฟ์"
-retryAllQueuesNow: "ลองเรียกใช้คิวทั้งหมดอีกครั้ง"
-retryAllQueuesConfirmTitle: "ลองใหม่ทั้งหมดจริงๆหรอแน่ใจนะ?"
+retryAllQueuesNow: "ลองใหม่ทุกคิวทันที"
+retryAllQueuesConfirmTitle: "ลองใหม่ทันทีเลยไหม?"
retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มการโหลดเซิร์ฟเวอร์ชั่วคราวนะ"
enableChartsForRemoteUser: "สร้างแผนภูมิข้อมูลผู้ใช้ระยะไกล"
enableChartsForFederatedInstances: "สร้างแผนภูมิของเซิร์ฟเวอร์ระยะไกล"
@@ -1150,7 +1160,7 @@ initialAccountSetting: "ตั้งค่าโปรไฟล์"
youFollowing: "ติดตามแล้ว"
preventAiLearning: "ปฏิเสธการเรียนรู้ด้วย generative AI"
preventAiLearningDescription: "ส่งคำร้องขอไม่ให้ใช้ ข้อความในโน้ตที่โพสต์, หรือเนื้อหารูปภาพ ฯลฯ ในการเรียนรู้ของเครื่อง(machine learning) / Predictive AI / Generative AI โดยการเพิ่มแฟล็ก “noai” ลง HTML-Response ให้กับเนื้อหาที่เกี่ยวข้อง แต่ทั้งนี้ ไม่ได้ป้องกัน AI จากการเรียนรู้ได้อย่างสมบูรณ์ เนื่องจากมี AI บางตัวเท่านั้นที่จะเคารพคำขอดังกล่าว"
-options: "ตัวเลือกบทบาท"
+options: "ตัวเลือก"
specifyUser: "ผู้ใช้เฉพาะ"
lookupConfirm: "ต้องการเรียกดูข้อมูลใช่ไหม?"
openTagPageConfirm: "ต้องการเปิดหน้าแฮชแท็กใช่ไหม?"
@@ -1169,6 +1179,7 @@ installed: "ติดตั้งแล้ว"
branding: "แบรนดิ้ง"
enableServerMachineStats: "เผยแพร่สถานะฮาร์ดแวร์ของเซิร์ฟเวอร์"
enableIdenticonGeneration: "เปิดใช้งานผู้ใช้สร้างตัวระบุ"
+showRoleBadgesOfRemoteUsers: "แสดงตราบทบาทที่มอบให้กับผู้ใช้ระยะไกล"
turnOffToImprovePerformance: "การปิดส่วนนี้สามารถเพิ่มประสิทธิภาพได้"
createInviteCode: "สร้างรหัสเชิญ"
createWithOptions: "สร้างด้วยตัวเลือก"
@@ -1247,7 +1258,7 @@ refreshing: "กำลังรีเฟรช..."
pullDownToRefresh: "ดึงลงเพื่อรีเฟรช"
useGroupedNotifications: "แสดงผลการแจ้งเตือนแบบกลุ่มแล้ว"
emailVerificationFailedError: "เกิดปัญหาในขณะตรวจสอบอีเมล อาจเป็นไปได้ว่าลิงก์หมดอายุแล้ว"
-cwNotationRequired: "หากเปิดใช้งาน “ซ่อนเนื้อหา” จะต้องระบุคำอธิบาย"
+cwNotationRequired: "หากเปิดใช้งาน “ซ่อนเนื้อหา” จะต้องระบุข้อความเกริ่น"
doReaction: "เพิ่มรีแอคชั่น"
code: "โค้ด"
reloadRequiredToApplySettings: "จำเป็นต้องมีการโหลดซ้ำเพื่อให้การตั้งค่ามีผล"
@@ -1351,7 +1362,7 @@ migrateOldSettings: "ย้ายข้อมูลการตั้งค่
migrateOldSettings_description: "โดยปกติจะทำโดยอัตโนมัติ แต่หากด้วยเหตุผลบางประการที่ไม่สามารถย้ายได้สำเร็จ สามารถสั่งย้ายด้วยตนเองได้ การตั้งค่าปัจจุบันจะถูกเขียนทับ"
compress: "บีบอัด"
right: "ขวา"
-bottom: "ภายใต้"
+bottom: "ล่าง"
top: "บน"
embed: "ฝัง"
settingsMigrating: "กำลังย้ายการตั้งค่า กรุณารอสักครู่... (สามารถย้ายด้วยตนเองภายหลังได้ที่ การตั้งค่า → อื่นๆ → ย้ายข้อมูลการตั้งค่าเก่า)"
@@ -1390,17 +1401,53 @@ scheduledToPostOnX: "มีการกำหนดเวลาให้โพ
schedule: "กำหนดเวลา"
scheduled: "กำหนดเวลา"
widgets: "วิดเจ็ต"
+deviceInfo: "รายละเอียดอุปกรณ์"
+deviceInfoDescription: "เมื่อต้องการรับความช่วยเหลือทางเทคนิค กรุณาระบุข้อมูลต่อไปนี้ซึ่งอาจช่วยแก้ไขปัญหาได้"
+youAreAdmin: "คุณคือผู้ดูแลระบบ"
+frame: "เฟรม"
presets: "พรีเซ็ต"
+zeroPadding: "ห่างเป็น 0"
+nothingToConfigure: "ไม่มีอะไรให้ต้ังค่า"
_imageEditing:
_vars:
+ caption: "แคปชั่นของไฟล์"
filename: "ชื่อไฟล์"
+ filename_without_ext: "ชื่อไฟล์ที่ไม่มีนามสกุล"
+ year: "ปีที่ถ่าย"
+ month: "เดือนที่ถ่าย"
+ day: "วันที่ถ่าย"
+ hour: "เวลาที่ถ่าย (ชั่วโมง)"
+ minute: "เวลาที่ถ่าย (นาที)"
+ second: "เวลาที่ถ่าย (วินาที)"
+ camera_model: "ชื่อกล้อง"
+ camera_lens_model: "ชื่อเลนส์"
+ camera_mm: "ความยาวโฟกัส"
+ camera_mm_35: "ทางยาวโฟกัส (เทียบเท่า 35 มม.)"
+ camera_f: "รูรับแสง"
+ camera_s: "ความเร็วชัตเตอร์"
+ camera_iso: "ความไวแสง ISO"
+ gps_lat: "ละติจูด"
+ gps_long: "ลองจิจูด"
_imageFrameEditor:
+ title: "แก้ไขเฟรม"
+ tip: "สามารถตกแต่งภาพโดยการเพิ่มป้ายที่มีเฟรมหรือเมทาเดต้าได้"
header: "ส่วนหัว"
+ footer: "ท้ายกระดาษ"
+ borderThickness: "ความกว้างขอบ"
+ labelThickness: "ความกว้างป้าย"
+ labelScale: "สเกลของป้าย"
+ centered: "จัดกึ่งกลาง"
+ captionMain: "แคปชั่น (ใหญ่)"
+ captionSub: "แคปชั่น (เล็ก)"
+ availableVariables: "ตัวแปรที่สามารถใช้ได้"
withQrCode: "QR โค้ด"
+ backgroundColor: "สีพื้นหลัง"
+ textColor: "สีตัวอักษร"
font: "แบบอักษร"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
quitWithoutSaveConfirm: "ต้องการออกโดยไม่บันทึกหรือไม่?"
+ failedToLoadImage: "โหลดภาพล้มเหลว"
_compression:
_quality:
high: "คุณภาพสูง"
@@ -1503,6 +1550,11 @@ _settings:
showUrlPreview: "แสดงตัวอย่าง URL"
showAvailableReactionsFirstInNote: "แสดงรีแอคชั่นที่ใช้ได้ไว้หน้าสุด"
showPageTabBarBottom: "แสดงแท็บบาร์ของเพจที่ด้านล่าง"
+ emojiPaletteBanner: "สามารถบันทึกพรีเซ็ตเป็นจานสีเพื่อตรึงไว้ในตัวจิ้มเอโมจิ หรือปรับแต่งวิธีการแสดงผลของตัวจิ้มเอโมจิได้"
+ enableAnimatedImages: "เปิดใช้งานภาพเคลื่อนไหว"
+ settingsPersistence_title: "คงสภาพการตั้งค่า"
+ settingsPersistence_description1: "เมื่อเปิดใช้งานการคงสภาพการตั้งค่า จะช่วยป้องกันไม่ให้ข้อมูลการตั้งค่าสูญหายได้"
+ settingsPersistence_description2: "แต่ในบางสภาพแวดล้อม อาจไม่สามารถเปิดใช้งานได้"
_chat:
showSenderName: "แสดงชื่อผู้ส่ง"
sendOnEnter: "กด Enter เพื่อส่ง"
@@ -1511,6 +1563,8 @@ _preferencesProfile:
profileNameDescription: "กรุณาตั้งชื่อเพื่อระบุอุปกรณ์นี้"
profileNameDescription2: "เช่น: “คอมเครื่องหลัก”, “มือถือ” ฯลฯ"
manageProfiles: "จัดการโปรไฟล์"
+ shareSameProfileBetweenDevicesIsNotRecommended: "ไม่แนะนำให้ใช้โปรไฟล์เดียวกันร่วมกันบนหลายอุปกรณ์"
+ useSyncBetweenDevicesOptionIfYouWantToSyncSetting: "หากมีรายการตั้งค่าที่ต้องการซิงก์ระหว่างหลายอุปกรณ์ โปรดเปิดใช้งานตัวเลือก “ซิงก์ระหว่างหลายอุปกรณ์” ในอุปกรณ์นั้นๆ ด้วย"
_preferencesBackup:
autoBackup: "สำรองโดยอัตโนมัติ"
restoreFromBackup: "คืนค่าจากข้อมูลสำรอง"
@@ -1518,8 +1572,9 @@ _preferencesBackup:
noBackupsFoundDescription: "ไม่พบข้อมูลสำรองที่สร้างโดยอัตโนมัติ แต่หากมีข้อมูลสำรองที่บันทึกด้วยตนเอง สามารถนำเข้ามาเพื่อกู้คืนได้"
selectBackupToRestore: "กรุณาเลือกข้อมูลสำรองที่ต้องการกู้คืน"
youNeedToNameYourProfileToEnableAutoBackup: "จำเป็นต้องตั้งชื่อโปรไฟล์ก่อนจึงจะเปิดใช้งานการสำรองข้อมูลอัตโนมัติได้"
- autoPreferencesBackupIsNotEnabledForThisDevice: "ยังไม่ได้เปิดใช้งานการสำรองข้อมูลอัตโนมัติบนอุปกรณ์นี้"
+ autoPreferencesBackupIsNotEnabledForThisDevice: "ยังไม่ได้เปิดใช้งานการสำรองการตั้งค่าแบบอัตโนมัติบนอุปกรณ์นี้"
backupFound: "พบข้อมูลสำรองของการตั้งค่าแล้ว"
+ forceBackup: "บังคับสำรองการตั้งค่า"
_accountSettings:
requireSigninToViewContents: "ต้องเข้าสู่ระบบเพื่อดูเนื้อหา"
requireSigninToViewContentsDescription1: "กำหนดให้ต้องเข้าสู่ระบบก่อนจึงจะสามารถดูโน้ตหรือเนื้อหาทั้งหมดที่สร้างไว้ได้ ซึ่งช่วยป้องกันไม่ให้ข้อมูลถูกเก็บโดยบอตหรือ Crawler (โปรแกรมรวบรวมข้อมูล)"
@@ -1587,7 +1642,7 @@ _initialAccountSetting:
theseSettingsCanEditLater: "คุณสามารถเปลี่ยนการตั้งค่าเหล่านี้ได้ในภายหลังได้ตลอดเวลานะ"
youCanEditMoreSettingsInSettingsPageLater: "สามารถตั้งค่าเพิ่มเติมได้ที่หน้า “การตั้งค่า” อย่าลืมไปเยี่ยมชมภายหลังด้วย"
followUsers: "ลองติดตามผู้ใช้ที่สนใจเพื่อสร้างไทม์ไลน์ดูสิ"
- pushNotificationDescription: "กำลังเปิดใช้งานการแจ้งเตือนแบบพุชจะช่วยให้คุณได้รับการแจ้งเตือนจาก {name} โดยตรงบนอุปกรณ์ของคุณนะ"
+ pushNotificationDescription: "เมื่อเปิดใช้งานการแจ้งเตือนแบบพุช จะสามารถรับการแจ้งเตือนจาก {name} บนอุปกรณ์ที่ใช้งานอยู่ได้"
initialAccountSettingCompleted: "ตั้งค่าโปรไฟล์เสร็จสมบูรณ์แล้ว!"
haveFun: "ขอให้สนุกกับ {name}!"
youCanContinueTutorial: "คุณสามารถดำเนินการต่อด้วยบทช่วยสอนเกี่ยวกับวิธีใช้ {name} (Misskey) หรือออกจากบทช่วยสอนแล้วเริ่มใช้งานได้ทันที"
@@ -1639,7 +1694,7 @@ _initialTutorial:
localOnly: "การโพสต์ด้วย flag นี้จะไม่รวมโน้ตไปยังเซิร์ฟเวอร์อื่น ผู้ใช้บนเซิร์ฟเวอร์อื่นจะไม่สามารถดูโน้ตเหล่านี้ได้โดยตรง โดยไม่คำนึงถึงการตั้งค่าการแสดงผลข้างต้น"
_cw:
title: "คำเตือนเกี่ยวกับเนื้อหา"
- description: "เนื้อหาที่เขียนใน “คำอธิบายประกอบ” จะแสดงแทนเนื้อหาหลัก ต้องคลิก “ดูเพิ่มเติม” เพื่อให้เนื้อหาหลักแสดง"
+ description: "เนื้อหาที่เขียนใน “ข้อความเกริ่น” จะแสดงแทนเนื้อหาหลัก ต้องกด “ดูเพิ่มเติม” เพื่อให้เนื้อหาหลักแสดง"
_exampleNote:
cw: " ห้ามดู ระวังหิว"
note: "เพิ่งไปกินโดนัทเคลือบช็อคโกแลตมา 🍩😋"
@@ -1991,7 +2046,7 @@ _role:
isConditionalRole: "นี่คือบทบาทที่มีเงื่อนไข"
isPublic: "ทำให้บทบาทเปิดเผยต่อสาธารณะ"
descriptionOfIsPublic: "บทบาทจะปรากฏบนโปรไฟล์ของผู้ใช้และเปิดเผยต่อสาธารณะ (ทุกคนสามารถเห็นได้ว่าผู้ใช้รายนี้มีบทบาทนี้)"
- options: "ตัวเลือกบทบาท"
+ options: "ตัวเลือก"
policies: "นโยบาย"
baseRole: "แม่แบบบทบาท"
useBaseValue: "ใช้ตามแม่แบบบทบาท"
@@ -2025,6 +2080,7 @@ _role:
canManageAvatarDecorations: "จัดการตกแต่งอวตาร"
driveCapacity: "ความจุของไดรฟ์"
maxFileSize: "ขนาดไฟล์สูงสุดที่สามารถอัปโหลดได้"
+ maxFileSize_caption: "รีเวิร์สพร็อกซี, CDN และคอมโพเนนต์หน้าบ้านอื่นๆ อาจมีค่าการตั้งค่าของตนเอง"
alwaysMarkNsfw: "ทำเครื่องหมายไฟล์ว่าเป็น NSFW เสมอ"
canUpdateBioMedia: "อนุญาตให้เปลี่ยนไอคอนประจำตัวและแบนเนอร์"
pinMax: "จํานวนสูงสุดของโน้ตที่ปักหมุดไว้"
@@ -2386,7 +2442,7 @@ _permissions:
"read:admin:index-stats": "ดูข้อมูลเกี่ยวกับดัชนีฐานข้อมูล"
"read:admin:table-stats": "ดูข้อมูลเกี่ยวกับตารางในฐานข้อมูล"
"read:admin:user-ips": "ดูที่อยู่ IP ของผู้ใช้"
- "read:admin:meta": "ดูข้อมูลอภิพันธุ์ของอินสแตนซ์"
+ "read:admin:meta": "ดูเมทาเดต้าของอินสแตนซ์"
"write:admin:reset-password": "รีเซ็ตรหัสผ่านของผู้ใช้"
"write:admin:resolve-abuse-user-report": "แก้ไขรายงานจากผู้ใช้"
"write:admin:send-email": "ส่งอีเมล"
@@ -2397,7 +2453,7 @@ _permissions:
"write:admin:unset-user-avatar": "ลบอวตารผู้ใช้"
"write:admin:unset-user-banner": "ลบแบนเนอร์ผู้ใช้"
"write:admin:unsuspend-user": "ยกเลิกการระงับผู้ใช้"
- "write:admin:meta": "จัดการข้อมูลอภิพันธุ์ของอินสแตนซ์"
+ "write:admin:meta": "จัดการเมทาเดต้าของอินสแตนซ์"
"write:admin:user-note": "จัดการโน้ตการกลั่นกรอง"
"write:admin:roles": "จัดการบทบาท"
"read:admin:roles": "ดูบทบาท"
@@ -2443,6 +2499,7 @@ _auth:
scopeUser: "กำลังดำเนินการในฐานะผู้ใช้ต่อไปนี้"
pleaseLogin: "กรุณาเข้าสู่ระบบเพื่ออนุมัติแอปพลิเคชัน"
byClickingYouWillBeRedirectedToThisUrl: "หากอนุญาตการเข้าถึง ระบบจะเปลี่ยนเส้นทางไปยัง URL ด้านล่างโดยอัตโนมัติ"
+ alreadyAuthorized: "แอปพลิเคชันนี้ได้รับอนุญาตให้เข้าถึงแล้ว"
_antennaSources:
all: "โน้ตทั้งหมด"
homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม"
@@ -2489,6 +2546,44 @@ _widgets:
clicker: "คลิกเกอร์"
birthdayFollowings: "วันเกิดผู้ใช้ในวันนี้"
chat: "แชตเลย"
+_widgetOptions:
+ showHeader: "แสดงส่วนหัว"
+ transparent: "ทำพื้นหลังโปรงใส"
+ height: "ความสูง"
+ _button:
+ colored: "สี"
+ _clock:
+ size: "ขนาด"
+ thickness: "ความหนาเข็ม"
+ thicknessThin: "บาง"
+ thicknessMedium: "ปานกลาง"
+ thicknessThick: "หนา"
+ graduations: "ขีดบอกค่าบนหน้าปัด"
+ graduationDots: "จุด"
+ graduationArabic: "เลขอารบิก"
+ fadeGraduations: "เฟดหน้าปัด"
+ sAnimation: "การเคลื่อนไหวของเข็มวินาที"
+ sAnimationElastic: "สมจริง"
+ sAnimationEaseOut: "ลื่นๆ"
+ twentyFour: "ระบบ 24 ชั่วโมง"
+ labelTime: "เวลา"
+ labelTz: "เขตเวลา"
+ labelTimeAndTz: "เวลาและเขตเวลา"
+ timezone: "เขตเวลา"
+ showMs: "แสดงมิลลิวินาที"
+ showLabel: "แสดงป้าย"
+ _jobQueue:
+ sound: "เล่นเสียง"
+ _rss:
+ url: "URL ของฟีด RSS"
+ refreshIntervalSec: "ห้วงอัปเดต (วินาที)"
+ maxEntries: "จำนวนที่แสดงได้สูงสุด"
+ _rssTicker:
+ shuffle: "สุ่มลำดับ"
+ duration: "ความเร็วทิกเกอร์ (วินาที)"
+ reverse: "วิ่งไปอีกทาง"
+ _birthdayFollowings:
+ period: "ระยะเวลา"
_cw:
hide: "ซ่อน"
show: "โหลดเพิ่มเติม"
@@ -2533,9 +2628,20 @@ _postForm:
replyPlaceholder: "ตอบกลับโน้ตนี้..."
quotePlaceholder: "อ้างโน้ตนี้..."
channelPlaceholder: "โพสต์ลงช่อง..."
+ showHowToUse: "แสดงวิธีใช้ฟอร์ม"
_howToUse:
+ content_title: "เนื้อความ"
+ content_description: "ป้อนเนื้อหาที่จะโพสต์"
+ toolbar_title: "แถบเครื่องมือ"
+ toolbar_description: "สามารถแนบไฟล์หรือแบบสอบถาม ตั้งข้อความเกริ่นหรือแฮชแท็ก แทรกเอโมจิหรือการกล่าวถึง เป็นต้น"
+ account_title: "เมนูบัญชี"
+ account_description: "สามารถสลับบัญชีที่ใช้โพสต์ หรือดูรายการฉบับร่างและโพสต์กำหนดเวลาไว้ซึ่งบันทึกไว้ในบัญชีได้"
visibility_title: "การมองเห็น"
+ visibility_description: "สามารถตั้งค่าขอบเขตการเผยแพร่โน้ตได้"
menu_title: "เมนู"
+ menu_description: "สามารถบันทึกเป็นฉบับร่าง ตั้งเวลาการโพสต์ ตั้งค่ารีแอคชั่น และดำเนินการอื่นๆ ได้"
+ submit_title: "ปุ่มโพสต์"
+ submit_description: "กดปุ่มนั้นเพื่อโพสต์โน้ต หรือกด Ctrl + Enter / Cmd + Return เพื่อโพสต์ก็ได้เช่นกัน"
_placeholders:
a: "ตอนนี้เป็นยังไงบ้าง?"
b: "มีอะไรเกิดขึ้นหรือเปล่า?"
@@ -2551,7 +2657,7 @@ _profile:
metadata: "ข้อมูลเพิ่มเติม"
metadataEdit: "แก้ไขข้อมูลเพิ่มเติม"
metadataDescription: "ใช้สิ่งเหล่านี้ คุณสามารถแสดงฟิลด์ข้อมูลเพิ่มเติมในโปรไฟล์ของคุณ"
- metadataLabel: "ป้ายชื่อ"
+ metadataLabel: "ป้าย"
metadataContent: "เนื้อหา"
changeAvatar: "เปลี่ยนไอคอนประจำตัว"
changeBanner: "เปลี่ยนแบนเนอร์"
@@ -2712,6 +2818,8 @@ _notification:
quote: "อ้างอิง"
reaction: "รีแอคชั่น"
pollEnded: "โพลสิ้นสุดแล้ว"
+ scheduledNotePosted: "โพสต์กำหนดเวลาสำเร็จ"
+ scheduledNotePostFailed: "โพสต์กำหนดเวลาล้มเหลว"
receiveFollowRequest: "ได้รับคำร้องขอติดตาม"
followRequestAccepted: "อนุมัติให้ติดตามแล้ว"
roleAssigned: "ให้บทบาท"
@@ -2751,6 +2859,14 @@ _deck:
usedAsMinWidthWhenFlexible: "ความกว้างขั้นต่ำนั้นจะถูกใช้งานสำหรับสิ่งนี้เมื่อเปิดใช้งานตัวเลือก \"ปรับความกว้างอัตโนมัติ\" หากเลือกเปิดใช้งานแล้ว"
flexible: "ปรับความกว้างอัตโนมัติ"
enableSyncBetweenDevicesForProfiles: "เปิดใช้งานการซิงค์ข้อมูลโปรไฟล์ระหว่างอุปกรณ์"
+ showHowToUse: "แสดงวิธีใช้ UI"
+ _howToUse:
+ addColumn_title: "เพิ่มคอลัมน์"
+ addColumn_description: "สามารถเลือกประเภทของคอลัมน์แล้วเพิ่มได้"
+ settings_title: "ตั้งค่า UI"
+ settings_description: "สามารถตั้งค่ารายละเอียดของ UI แบบเด็คได้"
+ switchProfile_title: "สลับโปรไฟล์"
+ switchProfile_description: "สามารถบันทึกเลย์เอาต์ของ UI เป็นโปรไฟล์ และสลับใช้งานได้ทุกเมื่อ"
_columns:
main: "หลัก"
widgets: "วิดเจ็ต"
@@ -2811,6 +2927,8 @@ _abuseReport:
notifiedWebhook: "Webhook ที่ใช้"
deleteConfirm: "ต้องการลบปลายทางการแจ้งเตือนใช่ไหม?"
_moderationLogTypes:
+ clearQueue: "ล้างคิว"
+ promoteQueue: "ดันงานในคิวอีกครั้ง"
createRole: "สร้างบทบาทแล้ว"
deleteRole: "ลบบทบาทแล้ว"
updateRole: "อัปเดตบทบาทแล้ว"
@@ -2869,7 +2987,7 @@ _fileViewer:
uploadedAt: "วันที่เข้าร่วม"
attachedNotes: "โน้ตที่แนบมาด้วย"
usage: "ใช้แล้ว"
- thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปรากฏได้โดยผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้น"
+ thisPageCanBeSeenFromTheAuthor: "เฉพาะผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้นที่สามารถดูหน้าเพจนี้ได้"
_externalResourceInstaller:
title: "ติดตั้งจากไซต์ภายนอก"
checkVendorBeforeInstall: "โปรดตรวจสอบให้แน่ใจว่าแหล่งแจกหน่ายมีความน่าเชื่อถือก่อนทำการติดตั้ง"
@@ -3054,7 +3172,7 @@ _customEmojisManager:
uploadSettingDescription: "สามารถกำหนดพฤติกรรมขณะอัปโหลดเอโมจิจากหน้าจอนี้ได้"
directoryToCategoryLabel: "ป้อนชื่อไดเรกทอรีเป็น \"category\""
directoryToCategoryCaption: "เมื่อทำการลากและวางไดเรกทอรี ชื่อจะถูกป้อนเป็น \"category\""
- confirmRegisterEmojisDescription: "จะลงทะเบียนเอโมจิที่แสดงในรายการเป็นเอโมจิแบบกำหนดเองใหม่\nดำเนินการต่อหรือไม่? (เพื่อหลีกเลี่ยงภาระโหลดหนัก ระบบจะสามารถลงทะเบียนอีโมจิได้สูงสุด {count} รายการต่อครั้ง)"
+ confirmRegisterEmojisDescription: "จะลงทะเบียนเอโมจิที่แสดงในรายการเป็นเอโมจิแบบกำหนดเองใหม่\nดำเนินการต่อหรือไม่? (เพื่อหลีกเลี่ยงภาระโหลดหนัก ระบบจะสามารถลงทะเบียนเอโมจิได้สูงสุด {count} รายการต่อครั้ง)"
confirmClearEmojisDescription: "ต้องการยกเลิกการแก้ไขและล้างรายการเอโมจิที่แสดงอยู่หรือไม่?"
confirmUploadEmojisDescription: "จะอัปโหลดไฟล์ {count} รายการที่ลากและวางไปยังไดรฟ์ ดำเนินการหรือไม่?"
_embedCodeGen:
@@ -3205,6 +3323,7 @@ _watermarkEditor:
title: "แก้ไขลายน้ำ"
cover: "ซ้อนทับทั่วทั้งพื้นที่"
repeat: "ปูให้เต็มพื้นที่"
+ preserveBoundingRect: "ปรับไม่ให้ล้นขอบเมื่อหมุน"
opacity: "ความทึบแสง"
scale: "ขนาด"
text: "ข้อความ"
@@ -3226,11 +3345,12 @@ _watermarkEditor:
polkadotSubDotRadius: "ขนาดของจุดรอง"
polkadotSubDotDivisions: "จำนวนจุดรอง"
leaveBlankToAccountUrl: "เว้นว่างไว้หากต้องการใช้ URL ของบัญชีแทน"
+ failedToLoadImage: "โหลดภาพล้มเหลว"
_imageEffector:
title: "เอฟเฟกต์"
addEffect: "เพิ่มเอฟเฟกต์"
discardChangesConfirm: "ต้องการทิ้งการเปลี่ยนแปลงแล้วออกหรือไม่?"
- nothingToConfigure: "ไม่มีอะไรให้ตั้งค่า"
+ failedToLoadImage: "โหลดภาพล้มเหลว"
_fxs:
chromaticAberration: "ความคลาดสี"
glitch: "กลิตช์"
diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml
index 208022a6d9..94e01df0fb 100644
--- a/locales/tr-TR.yml
+++ b/locales/tr-TR.yml
@@ -2466,6 +2466,15 @@ _widgets:
clicker: "Tıklayıcı"
birthdayFollowings: "Bugünün Doğum Günleri"
chat: "Sohbet"
+_widgetOptions:
+ showHeader: "Başlığı göster"
+ height: "Yükseklik"
+ _button:
+ colored: "Renkli"
+ _clock:
+ size: "Boyut"
+ _birthdayFollowings:
+ period: "Süre"
_cw:
hide: "Gizle"
show: "İçeriği göster"
@@ -3202,7 +3211,6 @@ _imageEffector:
title: "Effektler"
addEffect: "Efektler Ekle"
discardChangesConfirm: "Cidden çıkmak istiyor musun? Kaydedilmemiş değişikliklerin var."
- nothingToConfigure: "Yapılandırılabilir seçenekler mevcut değildir."
_fxs:
chromaticAberration: "Renk Sapması"
glitch: "Bozulma"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index c399e4c29c..273ccfb078 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -1430,6 +1430,14 @@ _widgets:
userList: "Список користувачів"
_userList:
chooseList: "Виберіть список"
+_widgetOptions:
+ height: "Висота"
+ _button:
+ colored: "Кольоровий"
+ _clock:
+ size: "Розмір"
+ _birthdayFollowings:
+ period: "Тривалість"
_cw:
hide: "Сховати"
show: "Показати більше"
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index 5d7c63dd15..11db034823 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -945,6 +945,12 @@ _widgets:
jobQueue: "Vazifalar navbati"
_userList:
chooseList: "Ro'yxat tanlash"
+_widgetOptions:
+ height: "balandligi"
+ _button:
+ colored: "rangli"
+ _birthdayFollowings:
+ period: "Davomiylik"
_cw:
show: "Ko‘proq ko‘rish"
chars: "{count} ta belgi(lar)"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index e0204ae4f8..0bb1d54ae8 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1826,6 +1826,14 @@ _widgets:
_userList:
chooseList: "Chọn danh sách"
clicker: "clicker"
+_widgetOptions:
+ height: "Chiều cao"
+ _button:
+ colored: "Với màu"
+ _clock:
+ size: "Kích thước"
+ _birthdayFollowings:
+ period: "Thời hạn"
_cw:
hide: "Ẩn"
show: "Tải thêm"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index caae928605..b2f700379d 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -10,7 +10,7 @@ notifications: "通知"
username: "用户名"
password: "密码"
initialPasswordForSetup: "初始化密码"
-initialPasswordIsIncorrect: "初始化密码不正确"
+initialPasswordIsIncorrect: "初始化密码不正确。"
initialPasswordForSetupDescription: "如果是自己安装的 Misskey,请输入配置文件里设好的密码。\n如果使用的是 Misskey 的托管服务等,请输入服务商提供的密码。\n如果没有设置密码,请留空并继续。"
forgotPassword: "忘记密码"
fetchingAsApObject: "在联邦宇宙查询中..."
@@ -146,11 +146,11 @@ markAsSensitive: "标记为敏感内容"
unmarkAsSensitive: "取消标记为敏感内容"
enterFileName: "输入文件名"
mute: "屏蔽"
-unmute: "取消屏蔽"
-renoteMute: "屏蔽转帖"
-renoteUnmute: "取消屏蔽转帖"
-block: "拉黑"
-unblock: "取消拉黑"
+unmute: "取消隐藏"
+renoteMute: "隐藏转帖"
+renoteUnmute: "取消隐藏转帖"
+block: "屏蔽"
+unblock: "取消屏蔽"
suspend: "冻结"
unsuspend: "解除冻结"
blockConfirm: "确定要屏蔽吗?"
@@ -164,7 +164,7 @@ selectAntenna: "选择天线"
editAntenna: "编辑天线"
createAntenna: "创建天线"
selectWidget: "选择小工具"
-editWidgets: "编辑部件"
+editWidgets: "编辑小工具"
editWidgetsExit: "完成编辑"
customEmojis: "自定义表情符号"
emoji: "表情符号"
@@ -241,13 +241,13 @@ clearCachedFilesConfirm: "确定要清除所有缓存的远程文件吗?"
blockedInstances: "被屏蔽的服务器"
blockedInstancesDescription: "设定要屏蔽的服务器,以换行分隔。被屏蔽的服务器将无法与本服务器进行交换通讯。子域名也同样会被屏蔽。"
silencedInstances: "被静音的服务器"
-silencedInstancesDescription: "设置要静音的服务器,以换行分隔。被静音的服务器内所有的账户都被视为「静音」状态,且关注操作均需要被批准。被阻止的实例不受影响。"
+silencedInstancesDescription: "设置要静音的服务器,以换行分隔。被静音的服务器内所有的账户都被视为「静音」状态,且关注操作均需要被批准。已被屏蔽的实例不受影响。"
mediaSilencedInstances: "已隐藏媒体文件的服务器"
-mediaSilencedInstancesDescription: "设置要隐藏媒体文件的服务器,以换行分隔。被设置的服务器内所有账号的文件均按照「敏感内容」处理,且将无法使用自定义表情符号。被阻止的实例不受影响。"
+mediaSilencedInstancesDescription: "设置要隐藏媒体文件的服务器,以换行分隔。被设置的服务器内所有账号的文件均按照「敏感内容」处理,且将无法使用自定义表情符号。已被屏蔽的实例不受影响。"
federationAllowedHosts: "允许联合的服务器"
federationAllowedHostsDescription: "设定允许联合的服务器,以换行分隔。"
-muteAndBlock: "屏蔽/拉黑"
-mutedUsers: "已静音的用户"
+muteAndBlock: "隐藏/屏蔽"
+mutedUsers: "已隐藏的用户"
blockedUsers: "已屏蔽的用户"
noUsers: "无用户"
editProfile: "编辑资料"
@@ -262,7 +262,7 @@ defaultValueIs: "默认值: {value}"
noCustomEmojis: "没有自定义表情符号"
noJobs: "没有任务"
federating: "联合中"
-blocked: "已拉黑"
+blocked: "已屏蔽"
suspended: "停止投递"
all: "全部"
subscribing: "已订阅"
@@ -543,6 +543,7 @@ regenerate: "重新生成"
fontSize: "字体大小"
mediaListWithOneImageAppearance: "仅一张图片的媒体列表高度"
limitTo: "上限为 {x}"
+showMediaListByGridInWideArea: "在大屏幕上并排显示媒体列表"
noFollowRequests: "没有关注请求"
openImageInNewTab: "在新标签页中打开图片"
dashboard: "管理面板"
@@ -645,7 +646,7 @@ addedRelays: "已添加的中继"
serviceworkerInfo: "您需要启用推送通知"
deletedNote: "已删除的帖子"
invisibleNote: "隐藏的帖子"
-enableInfiniteScroll: "启用自动滚动页面模式"
+enableInfiniteScroll: "自动加载更多内容"
visibility: "可见性"
poll: "投票"
useCw: "隐藏内容"
@@ -693,13 +694,13 @@ emptyToDisableSmtpAuth: "用户名和密码留空可以禁用 SMTP 验证"
smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS"
smtpSecureInfo: "使用 STARTTLS 时关闭。"
testEmail: "邮件发送测试"
-wordMute: "屏蔽关键词"
+wordMute: "折叠关键词"
wordMuteDescription: "折叠包含指定关键词的帖子。被折叠的帖子可单击展开。"
-hardWordMute: "强屏蔽关键词"
-showMutedWord: "显示屏蔽关键词"
-hardWordMuteDescription: "隐藏包含指定关键词的帖子。与隐藏关键词不同,帖子将完全不会显示。"
+hardWordMute: "屏蔽关键词"
+showMutedWord: "显示已折叠的关键词"
+hardWordMuteDescription: "屏蔽包含指定关键词的帖子。与折叠关键词不同,帖子将完全不会显示。"
regexpError: "正则表达式错误"
-regexpErrorDescription: "{tab} 隐藏文字的第 {line} 行的正则表达式有错误:"
+regexpErrorDescription: "{tab} 折叠关键词的第 {line} 行的正则表达式有错误:"
instanceMute: "已隐藏的服务器"
userSaysSomething: "{name} 说了什么,但是被屏蔽词过滤了"
userSaysSomethingAbout: "{name} 说了关于「{word}」的什么"
@@ -831,7 +832,7 @@ youAreRunningUpToDateClient: "您所使用的客户端已经是最新的。"
newVersionOfClientAvailable: "新版本的客户端可用。"
usageAmount: "使用量"
capacity: "容量"
-inUse: "使用中"
+inUse: "已使用"
editCode: "编辑代码"
apply: "应用"
receiveAnnouncementFromInstance: "从服务器接收通知"
@@ -912,7 +913,7 @@ accountDeletionInProgress: "正在删除账户"
usernameInfo: "在服务器上唯一标识您的帐户的名称。您可以使用字母 (a ~ z, A ~ Z)、数字 (0 ~ 9) 和下划线 (_)。用户名以后不能更改。"
aiChanMode: "小蓝模式"
devMode: "开发者模式"
-keepCw: "回复时维持隐藏内容"
+keepCw: "始终开启内容警告"
pubSub: "Pub/Sub 账户"
lastCommunication: "最近通信"
resolved: "已解决"
@@ -931,8 +932,8 @@ manageAccounts: "管理账户"
makeReactionsPublic: "将回应设置为公开"
makeReactionsPublicDescription: "将您发表过的回应设置成公开可见。"
classic: "经典"
-muteThread: "屏蔽帖文串"
-unmuteThread: "取消屏蔽帖文串"
+muteThread: "静音帖文串"
+unmuteThread: "取消帖文串静音"
followingVisibility: "关注的人的公开范围"
followersVisibility: "关注者的公开范围"
continueThread: "查看更多帖子"
@@ -955,7 +956,7 @@ searchByGoogle: "Google"
instanceDefaultLightTheme: "服务器默认浅色主题"
instanceDefaultDarkTheme: "服务器默认深色主题"
instanceDefaultThemeDescription: "以对象格式输入主题代码"
-mutePeriod: "屏蔽期限"
+mutePeriod: "隐藏时长"
period: "截止时间"
indefinitely: "永久"
tenMinutes: "10分钟"
@@ -1293,7 +1294,7 @@ useNativeUIForVideoAudioPlayer: "使用浏览器的 UI 播放动画及音频"
keepOriginalFilename: "保持原文件名"
keepOriginalFilenameDescription: "若关闭此设置,上传文件时文件名将被替换为随机字符。"
noDescription: "没有描述"
-alwaysConfirmFollow: "总是确认关注"
+alwaysConfirmFollow: "在关注时始终确认"
inquiry: "联系我们"
tryAgain: "请再试一次"
confirmWhenRevealingSensitiveMedia: "显示敏感内容前需要确认"
@@ -1334,7 +1335,7 @@ markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
preferences: "偏好设置"
accessibility: "辅助功能"
-preferencesProfile: "设置的配置"
+preferencesProfile: "设置的配置文件"
copyPreferenceId: "复制设置 ID"
resetToDefaultValue: "重置为默认值"
overrideByAccount: "覆盖账号"
@@ -1351,7 +1352,7 @@ preferenceSyncConflictChoiceDevice: "设备上的设定值"
preferenceSyncConflictChoiceCancel: "取消同步"
paste: "粘贴"
emojiPalette: "表情符号调色板"
-postForm: "投稿窗口"
+postForm: "发帖窗口"
textCount: "字数"
information: "关于"
chat: "聊天"
@@ -1374,10 +1375,10 @@ advice: "建议"
realtimeMode: "实时模式"
turnItOn: "开启"
turnItOff: "关闭"
-emojiMute: "屏蔽表情符号"
-emojiUnmute: "取消屏蔽表情符号"
-muteX: "屏蔽{x}"
-unmuteX: "取消屏蔽{x}"
+emojiMute: "打码表情符号"
+emojiUnmute: "取消表情符号打码"
+muteX: "隐藏{x}"
+unmuteX: "取消对{x}的隐藏"
abort: "中止"
tip: "提示和技巧"
redisplayAllTips: "重新显示所有的提示和技巧"
@@ -1406,6 +1407,7 @@ youAreAdmin: "你是管理员"
frame: "边框"
presets: "预设值"
zeroPadding: "填充 0"
+nothingToConfigure: "没有项目"
_imageEditing:
_vars:
caption: "文件标题"
@@ -1485,7 +1487,7 @@ _chat:
home: "首页"
send: "发送"
newline: "换行"
- muteThisRoom: "屏蔽该群聊"
+ muteThisRoom: "消息免打扰"
deleteRoom: "删除群聊"
chatNotAvailableForThisAccountOrServer: "此服务器或者账户还未开启聊天功能。"
chatIsReadOnlyForThisAccountOrServer: "此服务器或者账户内的聊天为只读。无法发布新信息或创建及加入群聊。"
@@ -1548,13 +1550,16 @@ _settings:
showUrlPreview: "显示 URL 预览"
showAvailableReactionsFirstInNote: "在顶部显示可用的回应"
showPageTabBarBottom: "在下方显示页面标签栏"
- emojiPaletteBanner: "可以将固定显示表情符号选择器的预设注册至调色板,也可以自定义表情符号选择器的显示方式。"
+ emojiPaletteBanner: "可以将固定显示在表情符号选择器中的预设注册为调色板,也可以自定义表情符号选择器的显示方式。"
enableAnimatedImages: "启用动画图像"
+ settingsPersistence_title: "设置持久化"
+ settingsPersistence_description1: "启用设置持久化可防止设置信息丢失。"
+ settingsPersistence_description2: "根据环境不同,有可能无法开启。"
_chat:
showSenderName: "显示发送者的名字"
sendOnEnter: "回车键发送"
_preferencesProfile:
- profileName: "配置名"
+ profileName: "配置文件名"
profileNameDescription: "请指定用于识别此设备的名称"
profileNameDescription2: "如「PC」、「手机」等"
manageProfiles: "管理配置文件"
@@ -2080,7 +2085,7 @@ _role:
canUpdateBioMedia: "可以更新头像和横幅"
pinMax: "帖子置顶数量限制"
antennaMax: "可创建的最大天线数量"
- wordMuteMax: "屏蔽词的字数限制"
+ wordMuteMax: "折叠词的字数限制"
webhookMax: "Webhook 创建数量限制"
clipMax: "便签创建数量限制"
noteEachClipsMax: "便签内贴文的最大数量"
@@ -2251,14 +2256,14 @@ _menuDisplay:
top: "顶部"
hide: "隐藏"
_wordMute:
- muteWords: "要隐藏的词"
+ muteWords: "要折叠的词"
muteWordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。"
muteWordsDescription2: "正则表达式用斜线包裹"
_instanceMute:
- instanceMuteDescription: "屏蔽服务器中所有的帖子和转帖,包括该服务器内用户的回复。"
+ instanceMuteDescription: "隐藏来自这些服务器的所有帖子和转贴,包括这些服务器上用户的回复。"
instanceMuteDescription2: "通过换行符分隔进行设置"
title: "下面实例中的帖子将被隐藏。"
- heading: "已屏蔽的服务器"
+ heading: "已隐藏的服务器"
_theme:
explore: "寻找主题"
install: "安装主题"
@@ -2397,8 +2402,8 @@ _2fa:
_permissions:
"read:account": "查看账户信息"
"write:account": "更改帐户信息"
- "read:blocks": "查看黑名单"
- "write:blocks": "编辑黑名单"
+ "read:blocks": "查看屏蔽列表"
+ "write:blocks": "编辑屏蔽列表"
"read:drive": "查看网盘"
"write:drive": "管理网盘文件"
"read:favorites": "查看收藏夹"
@@ -2407,8 +2412,8 @@ _permissions:
"write:following": "关注/取消关注"
"read:messaging": "查看私信"
"write:messaging": "撰写或删除消息"
- "read:mutes": "查看屏蔽列表"
- "write:mutes": "编辑屏蔽列表"
+ "read:mutes": "查看已隐藏用户列表"
+ "write:mutes": "编辑已隐藏用户列表"
"write:notes": "撰写或删除帖子"
"read:notifications": "查看通知"
"write:notifications": "管理通知"
@@ -2512,7 +2517,7 @@ _weekday:
_widgets:
profile: "个人资料"
instanceInfo: "服务器信息"
- memo: "便签"
+ memo: "便利贴"
notifications: "通知"
timeline: "时间线"
calendar: "日历"
@@ -2525,11 +2530,11 @@ _widgets:
digitalClock: "数字时钟"
unixClock: "UNIX 时钟"
federation: "联合"
- instanceCloud: "服务器云"
- postForm: "投稿窗口"
+ instanceCloud: "服务器球状列表"
+ postForm: "发帖窗口"
slideshow: "幻灯片展示"
button: "按钮"
- onlineUsers: "在线用户"
+ onlineUsers: "在线用户数"
jobQueue: "作业队列"
serverMetric: "服务器指标"
aiscript: "AiScript 控制台"
@@ -2541,6 +2546,44 @@ _widgets:
clicker: "点击器"
birthdayFollowings: "今天是他们的生日"
chat: "私信"
+_widgetOptions:
+ showHeader: "显示标题"
+ transparent: "使背景透明"
+ height: "高度"
+ _button:
+ colored: "彩色"
+ _clock:
+ size: "大小"
+ thickness: "指针宽度"
+ thicknessThin: "细"
+ thicknessMedium: "普通"
+ thicknessThick: "粗"
+ graduations: "表盘刻度"
+ graduationDots: "点"
+ graduationArabic: "阿拉伯数字"
+ fadeGraduations: "淡化表盘"
+ sAnimation: "秒针动画"
+ sAnimationElastic: "跳动"
+ sAnimationEaseOut: "平滑"
+ twentyFour: "24 小时制"
+ labelTime: "时间"
+ labelTz: "时区"
+ labelTimeAndTz: "时间和时区"
+ timezone: "时区"
+ showMs: "显示毫秒"
+ showLabel: "显示标签"
+ _jobQueue:
+ sound: "播放音效"
+ _rss:
+ url: "RSS feed 的 URL"
+ refreshIntervalSec: "更新间隔(秒)"
+ maxEntries: "最大显示个数"
+ _rssTicker:
+ shuffle: "随机顺序"
+ duration: "滚动速度(秒)"
+ reverse: "反方向滚动"
+ _birthdayFollowings:
+ period: "期限"
_cw:
hide: "隐藏"
show: "查看更多"
@@ -2628,10 +2671,10 @@ _exportOrImport:
favoritedNotes: "收藏的帖子"
clips: "便签"
followingList: "关注中"
- muteList: "屏蔽"
- blockingList: "拉黑"
+ muteList: "隐藏"
+ blockingList: "屏蔽"
userLists: "列表"
- excludeMutingUsers: "排除屏蔽用户"
+ excludeMutingUsers: "排除已隐藏用户"
excludeInactiveUsers: "排除不活跃用户"
withReplies: "在时间线中包含导入用户的回复"
_charts:
@@ -2812,10 +2855,18 @@ _deck:
introduction: "将各列进行组合以创建您自己的界面!"
introduction2: "可以随时通过屏幕右侧的 + 来添加列"
widgetsIntroduction: "从列菜单中,选择“小工具编辑”来添加小工具"
- useSimpleUiForNonRootPages: "用简易UI表示非根页面"
+ useSimpleUiForNonRootPages: "使用简易UI显示导航页面"
usedAsMinWidthWhenFlexible: "「自适应宽度」被启用的时候,这就是最小的宽度"
flexible: "自适应宽度"
- enableSyncBetweenDevicesForProfiles: "启用个人资料信息跨设备同步"
+ enableSyncBetweenDevicesForProfiles: "启用配置文件跨设备同步"
+ showHowToUse: "查看用户界面说明"
+ _howToUse:
+ addColumn_title: "添加列"
+ addColumn_description: "可以选择要添加的列的类型。"
+ settings_title: "用户界面设置"
+ settings_description: "可以配置 Deck UI 的详细设置,"
+ switchProfile_title: "切换配置文件"
+ switchProfile_description: "将用户界面布局保存为配置文件,以便随时切换。"
_columns:
main: "主列"
widgets: "小工具"
@@ -3064,10 +3115,10 @@ _mediaControls:
playbackRate: "播放速度"
loop: "循环播放"
_contextMenu:
- title: "上下文菜单"
- app: "应用"
- appWithShift: "Shift 键应用"
- native: "浏览器的用户界面"
+ title: "右键菜单"
+ app: "使用"
+ appWithShift: "按住 Shift 键使用"
+ native: "浏览器的原生界面"
_gridComponent:
_error:
requiredValue: "此值为必填项"
@@ -3299,7 +3350,6 @@ _imageEffector:
title: "效果"
addEffect: "添加效果"
discardChangesConfirm: "丢弃当前设置并退出?"
- nothingToConfigure: "还没有设置"
failedToLoadImage: "图片加载失败"
_fxs:
chromaticAberration: "色差"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 5227423d84..57612abd36 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -297,7 +297,7 @@ keepOriginalUploading: "保留原圖"
keepOriginalUploadingDescription: "上傳圖片時保留原始圖片。關閉時,瀏覽器會在上傳時生成適用於網路傳送的版本。"
fromDrive: "從雲端空間中選擇"
fromUrl: "從 URL 上傳"
-uploadFromUrl: "從網址上傳"
+uploadFromUrl: "從 URL 上傳"
uploadFromUrlDescription: "您要上傳的檔案網址"
uploadFromUrlRequested: "已請求上傳"
uploadFromUrlMayTakeTime: "還需要一些時間才能完成上傳。"
@@ -543,6 +543,7 @@ regenerate: "再次生成"
fontSize: "字體大小"
mediaListWithOneImageAppearance: "只有一張圖片時的檔案列表高度"
limitTo: "上限為 {x}"
+showMediaListByGridInWideArea: "當畫面寬度較寬時,將媒體清單以橫向排列顯示"
noFollowRequests: "沒有追隨您的請求"
openImageInNewTab: "於新分頁中開啟圖片"
dashboard: "儀表板"
@@ -824,7 +825,7 @@ saveConfirm: "您要儲存變更嗎?"
deleteConfirm: "你確定要刪除嗎?"
invalidValue: "輸入值無效。"
registry: "登錄表"
-closeAccount: "停用帳戶"
+closeAccount: "刪除帳戶"
currentVersion: "目前版本"
latestVersion: "最新版本"
youAreRunningUpToDateClient: "您所使用的客戶端已經是最新的。"
@@ -1089,9 +1090,9 @@ postToTheChannel: "發佈到頻道"
cannotBeChangedLater: "之後不能變更。"
reactionAcceptance: "接受表情反應"
likeOnly: "僅限讚"
-likeOnlyForRemote: "遠端僅限讚"
+likeOnlyForRemote: "全部(遠端僅限讚)"
nonSensitiveOnly: "僅限非敏感"
-nonSensitiveOnlyForLocalLikeOnlyForRemote: "僅限非敏感(遠端僅限按讚)"
+nonSensitiveOnlyForLocalLikeOnlyForRemote: "僅限非敏感(遠端僅限讚)"
rolesAssignedToMe: "指派給自己的角色"
resetPasswordConfirm: "重設密碼?"
sensitiveWords: "敏感詞"
@@ -1366,7 +1367,7 @@ top: "上"
embed: "嵌入"
settingsMigrating: "正在移轉設定。請稍候……(之後也可以到「設定 → 其他 → 舊設定資訊移轉」中手動進行移轉)"
readonly: "唯讀"
-goToDeck: "回去甲板"
+goToDeck: "回到多欄模式"
federationJobs: "聯邦通訊作業"
driveAboutTip: "在「雲端硬碟」中,會顯示過去上傳的檔案列表。<br>\n可以在附加到貼文時重新利用,或者事先上傳之後再用於發布。<br>\n<b>請注意,刪除檔案後,之前使用過該檔案的所有地方(貼文、頁面、大頭貼、橫幅等)也會一併無法顯示。</b><br>\n也可以建立資料夾來整理檔案。"
scrollToClose: "用滾輪關閉"
@@ -1393,7 +1394,7 @@ pluginsAreDisabledBecauseSafeMode: "由於啟用安全模式,所有的外掛
customCssIsDisabledBecauseSafeMode: "由於啟用安全模式,所有的客製 CSS 都被停用。"
themeIsDefaultBecauseSafeMode: "在安全模式啟用期間將使用預設主題。關閉安全模式後會恢復原本的設定。"
thankYouForTestingBeta: "感謝您協助驗證 beta 版!"
-createUserSpecifiedNote: "建立使用者指定的筆記"
+createUserSpecifiedNote: "建立指定使用者的貼文"
schedulePost: "排定發布"
scheduleToPostOnX: "排定在 {x} 發布"
scheduledToPostOnX: "已排定在 {x} 發布貼文"
@@ -1406,6 +1407,7 @@ youAreAdmin: "您是管理員"
frame: "邊框"
presets: "預設值"
zeroPadding: "補零"
+nothingToConfigure: "無可設定的項目"
_imageEditing:
_vars:
caption: "檔案標題"
@@ -1550,6 +1552,9 @@ _settings:
showPageTabBarBottom: "在底部顯示頁面的標籤列"
emojiPaletteBanner: "可以將固定顯示在表情符號選擇器的預設項目註冊為調色盤,或者自訂選擇器的顯示方式。"
enableAnimatedImages: "啟用動畫圖片"
+ settingsPersistence_title: "設定的持久化"
+ settingsPersistence_description1: "啟用「設定的持久化」後,可以防止設定資訊遺失。"
+ settingsPersistence_description2: "依環境不同,可能無法啟用。"
_chat:
showSenderName: "顯示發送者的名稱"
sendOnEnter: "按下 Enter 發送訊息"
@@ -2152,7 +2157,7 @@ _accountDelete:
accountDelete: "刪除帳戶"
mayTakeTime: "刪除帳戶的處理負荷較大,如果帳戶發佈的內容以及上傳的檔案數量較多,則需要一段時間才能完成。"
sendEmail: "帳戶刪除完成後,將向其電子郵件地址發送通知。"
- requestAccountDelete: "刪除帳戶請求"
+ requestAccountDelete: "請求刪除帳戶"
started: "已開始刪除作業。"
inProgress: "正在刪除"
_ad:
@@ -2356,7 +2361,7 @@ _timeIn:
minutes: "{n}分鐘後"
hours: "{n}小時後"
days: "{n}天後"
- weeks: "{n}周後"
+ weeks: "{n}週後"
months: "{n}個月後"
years: "{n}年後"
_time:
@@ -2541,6 +2546,44 @@ _widgets:
clicker: "點擊器"
birthdayFollowings: "今天生日的使用者"
chat: "聊天"
+_widgetOptions:
+ showHeader: "檢視標頭 "
+ transparent: "使背景透明"
+ height: "高度"
+ _button:
+ colored: "彩色"
+ _clock:
+ size: "尺寸"
+ thickness: "指針粗細"
+ thicknessThin: "細"
+ thicknessMedium: "普通"
+ thicknessThick: "粗"
+ graduations: "刻度盤"
+ graduationDots: "圓點"
+ graduationArabic: "阿拉伯數字"
+ fadeGraduations: "刻度淡出"
+ sAnimation: "秒針的動畫效果"
+ sAnimationElastic: "真實的"
+ sAnimationEaseOut: "滑順"
+ twentyFour: "24 小時制"
+ labelTime: "時間"
+ labelTz: "時區"
+ labelTimeAndTz: "時間與時區"
+ timezone: "時區"
+ showMs: "顯示毫秒"
+ showLabel: "顯示標記"
+ _jobQueue:
+ sound: "播放音效"
+ _rss:
+ url: "RSS 訂閱網址"
+ refreshIntervalSec: "更新間隔(秒)"
+ maxEntries: "最大顯示數量"
+ _rssTicker:
+ shuffle: "顯示順序隨機排列"
+ duration: "RSS 跑馬燈的捲動速度(秒)"
+ reverse: "反方向滾動"
+ _birthdayFollowings:
+ period: "時長"
_cw:
hide: "隱藏"
show: "顯示內容"
@@ -2816,6 +2859,14 @@ _deck:
usedAsMinWidthWhenFlexible: "如果啟用「自動調整寬度」,此為最小寬度"
flexible: "自動調整寬度"
enableSyncBetweenDevicesForProfiles: "啟用裝置與裝置之間的設定檔資料同步化"
+ showHowToUse: "檢視使用者介面說明"
+ _howToUse:
+ addColumn_title: "新增欄位"
+ addColumn_description: "您可以選擇要新增的欄位類型。"
+ settings_title: "使用者介面設定"
+ settings_description: "您可以對多欄模式使用者介面做詳細設定。"
+ switchProfile_title: "切換設定檔"
+ switchProfile_description: "將使用者介面佈局儲存為設定檔,就可以隨時切換使用。"
_columns:
main: "主列"
widgets: "小工具"
@@ -3299,7 +3350,6 @@ _imageEffector:
title: "特效"
addEffect: "新增特效"
discardChangesConfirm: "捨棄更改並退出嗎?"
- nothingToConfigure: "無可設定的項目"
failedToLoadImage: "圖片載入失敗"
_fxs:
chromaticAberration: "色差"
diff --git a/package.json b/package.json
index 1cfa3d9565..7c6e4452d5 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,12 @@
{
"name": "misskey",
- "version": "2025.12.2",
+ "version": "2026.3.0",
"codename": "nasubi",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
- "packageManager": "pnpm@10.25.0",
+ "packageManager": "pnpm@10.30.1",
"workspaces": [
"packages/misskey-js",
"packages/i18n",
@@ -23,12 +23,12 @@
"private": true,
"scripts": {
"compile-config": "cd packages/backend && pnpm compile-config",
- "build-pre": "node ./scripts/build-pre.js",
+ "build-pre": "node scripts/build-pre.mjs",
"build-assets": "node ./scripts/build-assets.mjs",
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
"build-storybook": "pnpm --filter frontend build-storybook",
"build-misskey-js-with-types": "pnpm build-pre && pnpm --filter backend... --filter=!misskey-js build && pnpm --filter backend generate-api-json --no-build && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
- "start": "pnpm check:connect && cd packages/backend && pnpm compile-config && node ./built/boot/entry.js",
+ "start": "cd packages/backend && pnpm compile-config && node ./built/boot/entry.js",
"start:inspect": "cd packages/backend && pnpm compile-config && node --inspect ./built/boot/entry.js",
"start:test": "ncp ./.github/misskey/test.yml ./.config/test.yml && cd packages/backend && cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
"cli": "cd packages/backend && pnpm cli",
@@ -39,7 +39,7 @@
"migrateandstart": "pnpm migrate && pnpm start",
"watch": "pnpm dev",
"dev": "node scripts/dev.mjs",
- "lint": "pnpm -r lint",
+ "lint": "pnpm --no-bail -r lint",
"cy:open": "pnpm cypress open --config-file=cypress.config.ts",
"cy:run": "pnpm cypress run",
"e2e": "pnpm start-server-and-test start:test http://localhost:61812 cy:run",
@@ -48,46 +48,45 @@
"jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage",
"test": "pnpm -r test",
"test-and-coverage": "pnpm -r test-and-coverage",
- "clean": "node ./scripts/clean.js",
- "clean-all": "node ./scripts/clean-all.js",
+ "clean": "node scripts/clean.mjs",
+ "clean-all": "node scripts/clean-all.mjs",
"cleanall": "pnpm clean-all"
},
- "resolutions": {
- "chokidar": "5.0.0",
- "lodash": "4.17.21"
- },
"dependencies": {
"cssnano": "7.1.2",
- "esbuild": "0.27.1",
+ "esbuild": "0.27.3",
"execa": "9.6.1",
"ignore-walk": "8.0.0",
"js-yaml": "4.1.1",
"postcss": "8.5.6",
- "tar": "7.5.2",
- "terser": "5.44.1",
- "typescript": "5.9.3"
+ "tar": "7.5.9",
+ "terser": "5.46.0"
},
"devDependencies": {
- "@eslint/js": "9.39.1",
- "@misskey-dev/eslint-plugin": "2.2.0",
+ "@eslint/js": "9.39.3",
+ "@misskey-dev/eslint-plugin": "2.1.0",
"@types/js-yaml": "4.0.9",
- "@types/node": "24.10.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
+ "@types/node": "24.10.13",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "@typescript/native-preview": "7.0.0-dev.20260116.1",
"cross-env": "10.1.0",
- "cypress": "15.7.1",
- "eslint": "9.39.1",
- "globals": "16.5.0",
+ "cypress": "15.10.0",
+ "eslint": "9.39.3",
+ "globals": "17.3.0",
"ncp": "2.0.0",
- "pnpm": "10.25.0",
- "start-server-and-test": "2.1.3"
+ "pnpm": "10.30.1",
+ "start-server-and-test": "2.1.3",
+ "typescript": "5.9.3"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.22.0"
},
"pnpm": {
"overrides": {
- "@aiscript-dev/aiscript-languageserver": "-"
+ "@aiscript-dev/aiscript-languageserver": "-",
+ "chokidar": "5.0.0",
+ "lodash": "4.17.23"
},
"ignoredBuiltDependencies": [
"@sentry-internal/node-cpu-profiler",
diff --git a/packages/backend/assets/misc/bios.js b/packages/backend/assets/misc/bios.js
index 9ff5dca72a..f9716d8f00 100644
--- a/packages/backend/assets/misc/bios.js
+++ b/packages/backend/assets/misc/bios.js
@@ -9,7 +9,7 @@ window.onload = async () => {
const account = JSON.parse(localStorage.getItem('account'));
const i = account.token;
- const api = (endpoint, data = {}) => {
+ const _api = (endpoint, data = {}) => {
const promise = new Promise((resolve, reject) => {
// Append a credential
if (i) data.i = i;
diff --git a/packages/backend/build.js b/packages/backend/build.js
new file mode 100644
index 0000000000..52ca09b7a8
--- /dev/null
+++ b/packages/backend/build.js
@@ -0,0 +1,121 @@
+import fs from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname, join } from 'node:path';
+import { build } from 'esbuild';
+import { swcPlugin } from 'esbuild-plugin-swc';
+
+const _filename = fileURLToPath(import.meta.url);
+const _dirname = dirname(_filename);
+const _package = JSON.parse(fs.readFileSync(_dirname + '/package.json', 'utf-8'));
+
+const resolveTsPathsPlugin = {
+ name: 'resolve-ts-paths',
+ setup(build) {
+ build.onResolve({ filter: /^\.{1,2}\/.*\.js$/ }, (args) => {
+ if (args.importer) {
+ const absPath = join(args.resolveDir, args.path);
+ const tsPath = absPath.slice(0, -3) + '.ts';
+ if (fs.existsSync(tsPath)) return { path: tsPath };
+ const tsxPath = absPath.slice(0, -3) + '.tsx';
+ if (fs.existsSync(tsxPath)) return { path: tsxPath };
+ }
+ });
+ },
+};
+
+const externalIpaddrPlugin = {
+ name: 'external-ipaddr',
+ setup(build) {
+ build.onResolve({ filter: /^ipaddr\.js$/ }, (args) => {
+ return { path: args.path, external: true };
+ });
+ },
+};
+
+/** @type {import('esbuild').BuildOptions} */
+const options = {
+ entryPoints: ['./src/boot/entry.ts'],
+ minify: true,
+ keepNames: true,
+ bundle: true,
+ outdir: './built/boot',
+ target: 'node22',
+ platform: 'node',
+ format: 'esm',
+ sourcemap: 'linked',
+ packages: 'external',
+ banner: {
+ js: 'import { createRequire as topLevelCreateRequire } from "module";' +
+ 'import ___url___ from "url";' +
+ 'const require = topLevelCreateRequire(import.meta.url);' +
+ 'const __filename = ___url___.fileURLToPath(import.meta.url);' +
+ 'const __dirname = ___url___.fileURLToPath(new URL(".", import.meta.url));',
+ },
+ plugins: [
+ externalIpaddrPlugin,
+ resolveTsPathsPlugin,
+ swcPlugin({
+ jsc: {
+ parser: {
+ syntax: 'typescript',
+ decorators: true,
+ dynamicImport: true,
+ },
+ transform: {
+ legacyDecorator: true,
+ decoratorMetadata: true,
+ },
+ experimental: {
+ keepImportAssertions: true,
+ },
+ baseUrl: join(_dirname, 'src'),
+ paths: {
+ '@/*': ['*'],
+ },
+ target: 'esnext',
+ keepClassNames: true,
+ },
+ }),
+ externalIpaddrPlugin,
+ ],
+ // external: [
+ // 'slacc-*',
+ // 'class-transformer',
+ // 'class-validator',
+ // '@sentry/*',
+ // '@nestjs/websockets/socket-module',
+ // '@nestjs/microservices/microservices-module',
+ // '@nestjs/microservices',
+ // '@napi-rs/canvas-win32-x64-msvc',
+ // 'mock-aws-s3',
+ // 'aws-sdk',
+ // 'nock',
+ // 'sharp',
+ // 'jsdom',
+ // 're2',
+ // '@napi-rs/canvas',
+ // ],
+};
+
+const args = process.argv.slice(2).map(arg => arg.toLowerCase());
+
+if (!args.includes('--no-clean')) {
+ fs.rmSync('./built', { recursive: true, force: true });
+}
+
+await buildSrc();
+
+async function buildSrc() {
+ console.log(`[${_package.name}] start building...`);
+
+ await build(options)
+ .then(() => {
+ console.log(`[${_package.name}] build succeeded.`);
+ })
+ .catch((err) => {
+ process.stderr.write(err.stderr || err.message || err);
+ process.exit(1);
+ });
+
+ console.log(`[${_package.name}] finish building.`);
+}
diff --git a/packages/backend/eslint.config.js b/packages/backend/eslint.config.js
index ba7c705def..d15a703ba2 100644
--- a/packages/backend/eslint.config.js
+++ b/packages/backend/eslint.config.js
@@ -25,7 +25,6 @@ export default [
},
},
rules: {
- '@typescript-eslint/no-unused-vars': 'off',
'import/order': ['warn', {
groups: [
'builtin',
diff --git a/packages/backend/migration/1767169026317-birthday-index.js b/packages/backend/migration/1767169026317-birthday-index.js
new file mode 100644
index 0000000000..972fc08c9b
--- /dev/null
+++ b/packages/backend/migration/1767169026317-birthday-index.js
@@ -0,0 +1,20 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class BirthdayIndex1767169026317 {
+ name = 'BirthdayIndex1767169026317'
+
+ async up(queryRunner) {
+ await queryRunner.query(`DROP INDEX "public"."IDX_de22cd2b445eee31ae51cdbe99"`);
+ await queryRunner.query(`CREATE OR REPLACE FUNCTION get_birthday_date(birthday TEXT) RETURNS SMALLINT AS $$ BEGIN RETURN CAST((SUBSTR(birthday, 6, 2) || SUBSTR(birthday, 9, 2)) AS SMALLINT); END; $$ LANGUAGE plpgsql IMMUTABLE;`);
+ await queryRunner.query(`CREATE INDEX "IDX_USERPROFILE_BIRTHDAY_DATE" ON "user_profile" (get_birthday_date("birthday"))`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`CREATE INDEX "IDX_de22cd2b445eee31ae51cdbe99" ON "user_profile" (substr("birthday", 6, 5))`);
+ await queryRunner.query(`DROP INDEX "public"."IDX_USERPROFILE_BIRTHDAY_DATE"`);
+ await queryRunner.query(`DROP FUNCTION IF EXISTS get_birthday_date(birthday TEXT)`);
+ }
+}
diff --git a/packages/backend/ormconfig.js b/packages/backend/ormconfig.js
index dabc0893f4..1a8c146451 100644
--- a/packages/backend/ormconfig.js
+++ b/packages/backend/ormconfig.js
@@ -1,6 +1,6 @@
import { DataSource } from 'typeorm';
-import { loadConfig } from './built/config.js';
-import { entities } from './built/postgres.js';
+import { loadConfig } from './src-js/config.js';
+import { entities } from './src-js/postgres.js';
const isConcurrentIndexMigrationEnabled = process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1';
diff --git a/packages/backend/package.json b/packages/backend/package.json
index c7a8a6c223..f5270ea554 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -12,17 +12,17 @@
"start:test": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./built/boot/entry.js",
"migrate": "pnpm compile-config && pnpm typeorm migration:run -d ormconfig.js",
"revert": "pnpm compile-config && pnpm typeorm migration:revert -d ormconfig.js",
- "cli": "pnpm compile-config && node ./built/boot/cli.js",
+ "cli": "pnpm compile-config && node ./src-js/boot/cli.js",
"check:connect": "pnpm compile-config && node ./scripts/check_connect.js",
"compile-config": "node ./scripts/compile_config.js",
- "build": "swc src -d built -D --strip-leading-paths",
+ "build": "swc src -d src-js -D --strip-leading-paths && node ./build.js",
"build:test": "swc test-server -d built-test -D --config-file test-server/.swcrc --strip-leading-paths",
"watch:swc": "swc src -d built -D -w --strip-leading-paths",
- "build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
+ "build:tsc": "tsgo -p tsconfig.json && tsc-alias -p tsconfig.json",
"watch": "pnpm compile-config && node ./scripts/watch.mjs",
"restart": "pnpm build && pnpm start",
"dev": "pnpm compile-config && node ./scripts/dev.mjs",
- "typecheck": "tsc --noEmit && tsc -p test --noEmit && tsc -p test-federation --noEmit",
+ "typecheck": "tsgo --noEmit && tsgo -p test --noEmit && tsgo -p test-federation --noEmit",
"eslint": "eslint --quiet \"{src,test-federation}/**/*.ts\"",
"lint": "pnpm typecheck && pnpm eslint",
"jest": "cross-env NODE_ENV=test pnpm compile-config && cross-env NODE_ENV=test node ./jest.js --forceExit --config jest.config.unit.cjs",
@@ -41,20 +41,20 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
- "@swc/core-darwin-arm64": "1.15.3",
- "@swc/core-darwin-x64": "1.15.3",
+ "@swc/core-darwin-arm64": "1.15.11",
+ "@swc/core-darwin-x64": "1.15.11",
"@swc/core-freebsd-x64": "1.3.11",
- "@swc/core-linux-arm-gnueabihf": "1.15.3",
- "@swc/core-linux-arm64-gnu": "1.15.3",
- "@swc/core-linux-arm64-musl": "1.15.3",
- "@swc/core-linux-x64-gnu": "1.15.3",
- "@swc/core-linux-x64-musl": "1.15.3",
- "@swc/core-win32-arm64-msvc": "1.15.3",
- "@swc/core-win32-ia32-msvc": "1.15.3",
- "@swc/core-win32-x64-msvc": "1.15.3",
+ "@swc/core-linux-arm-gnueabihf": "1.15.11",
+ "@swc/core-linux-arm64-gnu": "1.15.11",
+ "@swc/core-linux-arm64-musl": "1.15.11",
+ "@swc/core-linux-x64-gnu": "1.15.11",
+ "@swc/core-linux-x64-musl": "1.15.11",
+ "@swc/core-win32-arm64-msvc": "1.15.11",
+ "@swc/core-win32-ia32-msvc": "1.15.11",
+ "@swc/core-win32-x64-msvc": "1.15.11",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
- "bufferutil": "4.0.9",
+ "bufferutil": "4.1.0",
"slacc-android-arm-eabi": "0.0.10",
"slacc-android-arm64": "0.0.10",
"slacc-darwin-arm64": "0.0.10",
@@ -68,43 +68,42 @@
"slacc-linux-x64-musl": "0.0.10",
"slacc-win32-arm64-msvc": "0.0.10",
"slacc-win32-x64-msvc": "0.0.10",
- "utf-8-validate": "6.0.5"
+ "utf-8-validate": "6.0.6"
},
"dependencies": {
- "@aws-sdk/client-s3": "3.948.0",
- "@aws-sdk/lib-storage": "3.948.0",
+ "@aws-sdk/client-s3": "3.995.0",
+ "@aws-sdk/lib-storage": "3.995.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.4",
"@fastify/cors": "11.2.0",
- "@fastify/express": "4.0.2",
+ "@fastify/express": "4.0.4",
"@fastify/http-proxy": "11.4.1",
- "@fastify/multipart": "9.3.0",
- "@fastify/static": "8.3.0",
- "@kitajs/html": "4.2.11",
+ "@fastify/multipart": "9.4.0",
+ "@fastify/static": "9.0.0",
+ "@kitajs/html": "4.2.13",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.5",
- "@napi-rs/canvas": "0.1.84",
- "@nestjs/common": "11.1.9",
- "@nestjs/core": "11.1.9",
- "@nestjs/testing": "11.1.9",
+ "@napi-rs/canvas": "0.1.94",
+ "@nestjs/common": "11.1.14",
+ "@nestjs/core": "11.1.14",
+ "@nestjs/testing": "11.1.14",
"@peertube/http-signature": "1.7.0",
- "@sentry/node": "10.29.0",
- "@sentry/profiling-node": "10.29.0",
+ "@sentry/node": "10.39.0",
+ "@sentry/profiling-node": "10.39.0",
"@simplewebauthn/server": "13.2.2",
- "@sinonjs/fake-timers": "15.0.0",
- "@smithy/node-http-handler": "4.4.5",
- "@swc/cli": "0.7.9",
- "@swc/core": "1.15.3",
+ "@sinonjs/fake-timers": "15.1.0",
+ "@smithy/node-http-handler": "4.4.10",
+ "@swc/cli": "0.8.0",
+ "@swc/core": "1.15.11",
"@twemoji/parser": "16.0.0",
- "@types/redis-info": "3.0.3",
"accepts": "1.3.8",
- "ajv": "8.17.1",
+ "ajv": "8.18.0",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"bcryptjs": "3.0.3",
"blurhash": "2.0.5",
- "body-parser": "2.2.1",
- "bullmq": "5.65.1",
+ "body-parser": "2.2.2",
+ "bullmq": "5.69.4",
"cacheable-lookup": "7.0.0",
"chalk": "5.6.2",
"chalk-template": "1.1.2",
@@ -113,24 +112,24 @@
"content-disposition": "1.0.1",
"date-fns": "4.1.0",
"deep-email-validator": "0.1.21",
- "fastify": "5.6.2",
+ "fastify": "5.7.4",
"fastify-raw-body": "5.0.0",
- "feed": "5.1.0",
- "file-type": "21.1.1",
+ "feed": "5.2.0",
+ "file-type": "21.3.0",
"fluent-ffmpeg": "2.1.3",
"form-data": "4.0.5",
- "got": "14.6.5",
+ "got": "14.6.6",
"hpagent": "1.2.0",
"http-link-header": "1.1.3",
"i18n": "workspace:*",
- "ioredis": "5.8.2",
+ "ioredis": "5.9.3",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.3.0",
"is-svg": "6.1.0",
"json5": "2.2.3",
"jsonld": "9.0.0",
- "juice": "11.0.3",
- "meilisearch": "0.54.0",
+ "juice": "11.1.1",
+ "meilisearch": "0.55.0",
"mfm-js": "0.25.0",
"mime-types": "3.0.2",
"misskey-js": "workspace:*",
@@ -139,50 +138,48 @@
"nanoid": "5.1.6",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
- "node-html-parser": "7.0.1",
- "nodemailer": "7.0.11",
+ "node-html-parser": "7.0.2",
+ "nodemailer": "8.0.1",
"nsfwjs": "4.2.0",
"oauth2orize": "1.12.0",
"oauth2orize-pkce": "0.1.2",
"os-utils": "0.0.14",
- "otpauth": "9.4.1",
- "pg": "8.16.3",
- "pkce-challenge": "5.0.1",
+ "otpauth": "9.5.0",
+ "pg": "8.18.0",
+ "pkce-challenge": "6.0.0",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
"qrcode": "1.5.4",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
- "re2": "1.22.3",
- "redis-info": "3.1.0",
+ "re2": "1.23.3",
"reflect-metadata": "0.2.2",
"rename": "1.0.4",
"rss-parser": "3.13.0",
"rxjs": "7.8.2",
- "sanitize-html": "2.17.0",
+ "sanitize-html": "2.17.1",
"secure-json-parse": "4.1.0",
- "semver": "7.7.3",
+ "semver": "7.7.4",
"sharp": "0.33.5",
"slacc": "0.0.10",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
- "systeminformation": "5.27.14",
+ "systeminformation": "5.31.1",
"tinycolor2": "1.6.0",
"tmp": "0.2.5",
"tsc-alias": "1.8.16",
"typeorm": "0.3.28",
- "typescript": "5.9.3",
"ulid": "3.0.2",
"vary": "1.1.2",
"web-push": "3.6.7",
- "ws": "8.18.3",
+ "ws": "8.19.0",
"xev": "3.0.2"
},
"devDependencies": {
"@jest/globals": "29.7.0",
- "@kitajs/ts-html-plugin": "4.1.3",
- "@nestjs/platform-express": "11.1.9",
- "@sentry/vue": "10.29.0",
+ "@kitajs/ts-html-plugin": "4.1.4",
+ "@nestjs/platform-express": "11.1.14",
+ "@sentry/vue": "10.39.0",
"@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39",
"@types/accepts": "1.3.7",
@@ -196,11 +193,11 @@
"@types/jsonld": "1.5.15",
"@types/mime-types": "3.0.1",
"@types/ms": "2.1.0",
- "@types/node": "24.10.2",
- "@types/nodemailer": "7.0.4",
+ "@types/node": "24.10.13",
+ "@types/nodemailer": "7.0.11",
"@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2",
- "@types/pg": "8.15.6",
+ "@types/pg": "8.16.0",
"@types/qrcode": "1.5.6",
"@types/random-seed": "0.3.5",
"@types/ratelimiter": "3.4.6",
@@ -215,21 +212,22 @@
"@types/vary": "1.1.3",
"@types/web-push": "3.6.4",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
"aws-sdk-client-mock": "4.1.0",
"cbor": "10.0.11",
"cross-env": "10.1.0",
+ "esbuild-plugin-swc": "1.0.1",
"eslint-plugin-import": "2.32.0",
"execa": "9.6.1",
- "fkill": "10.0.1",
+ "fkill": "10.0.3",
"jest": "29.7.0",
"jest-mock": "29.7.0",
"js-yaml": "4.1.1",
- "nodemon": "3.1.11",
- "pid-port": "2.0.0",
+ "nodemon": "3.1.14",
+ "pid-port": "2.0.1",
"simple-oauth2": "5.1.0",
- "supertest": "7.1.4",
- "vite": "7.2.7"
+ "supertest": "7.2.2",
+ "vite": "7.3.1"
}
}
diff --git a/packages/backend/scripts/check_connect.js b/packages/backend/scripts/check_connect.js
index 96c4549ccb..a1cb839303 100644
--- a/packages/backend/scripts/check_connect.js
+++ b/packages/backend/scripts/check_connect.js
@@ -4,8 +4,8 @@
*/
import Redis from 'ioredis';
-import { loadConfig } from '../built/config.js';
-import { createPostgresDataSource } from '../built/postgres.js';
+import { loadConfig } from '../src-js/config.js';
+import { createPostgresDataSource } from '../src-js/postgres.js';
const config = loadConfig();
@@ -16,26 +16,22 @@ async function connectToPostgres() {
}
async function connectToRedis(redisOptions) {
- return await new Promise(async (resolve, reject) => {
- const redis = new Redis({
+ let redis;
+ try {
+ redis = new Redis({
...redisOptions,
lazyConnect: true,
reconnectOnError: false,
showFriendlyErrorStack: true,
});
- redis.on('error', e => reject(e));
- try {
- await redis.connect();
- resolve();
-
- } catch (e) {
- reject(e);
-
- } finally {
- redis.disconnect(false);
- }
- });
+ await Promise.race([
+ new Promise((_, reject) => redis.on('error', e => reject(e))),
+ redis.connect(),
+ ]);
+ } finally {
+ redis.disconnect(false);
+ }
}
// If not all of these are defined, the default one gets reused.
@@ -50,7 +46,7 @@ const promises = Array
]))
.map(connectToRedis)
.concat([
- connectToPostgres()
+ connectToPostgres(),
]);
await Promise.all(promises);
diff --git a/packages/backend/scripts/generate_api_json.js b/packages/backend/scripts/generate_api_json.js
index 798e243004..237f63a4d3 100644
--- a/packages/backend/scripts/generate_api_json.js
+++ b/packages/backend/scripts/generate_api_json.js
@@ -3,8 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import { writeFileSync, existsSync } from 'node:fs';
import { execa } from 'execa';
-import { writeFileSync, existsSync } from "node:fs";
async function main() {
if (!process.argv.includes('--no-build')) {
@@ -19,10 +19,10 @@ async function main() {
}
/** @type {import('../src/config.js')} */
- const { loadConfig } = await import('../built/config.js');
+ const { loadConfig } = await import('../src-js/config.js');
/** @type {import('../src/server/api/openapi/gen-spec.js')} */
- const { genOpenapiSpec } = await import('../built/server/api/openapi/gen-spec.js');
+ const { genOpenapiSpec } = await import('../src-js/server/api/openapi/gen-spec.js');
const config = loadConfig();
const spec = genOpenapiSpec(config, true);
diff --git a/packages/backend/scripts/measure-memory.mjs b/packages/backend/scripts/measure-memory.mjs
index 017252d7ec..3f30e24fb4 100644
--- a/packages/backend/scripts/measure-memory.mjs
+++ b/packages/backend/scripts/measure-memory.mjs
@@ -14,24 +14,56 @@ import { fork } from 'node:child_process';
import { setTimeout } from 'node:timers/promises';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
+import * as http from 'node:http';
+import * as fs from 'node:fs/promises';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
+const SAMPLE_COUNT = 3; // Number of samples to measure
const STARTUP_TIMEOUT = 120000; // 120 seconds timeout for server startup
const MEMORY_SETTLE_TIME = 10000; // Wait 10 seconds after startup for memory to settle
-async function measureMemory() {
- const startTime = Date.now();
+const keys = {
+ VmPeak: 0,
+ VmSize: 0,
+ VmHWM: 0,
+ VmRSS: 0,
+ VmData: 0,
+ VmStk: 0,
+ VmExe: 0,
+ VmLib: 0,
+ VmPTE: 0,
+ VmSwap: 0,
+};
+
+async function getMemoryUsage(pid) {
+ const status = await fs.readFile(`/proc/${pid}/status`, 'utf-8');
+
+ const result = {};
+ for (const key of Object.keys(keys)) {
+ const match = status.match(new RegExp(`${key}:\\s+(\\d+)\\s+kB`));
+ if (match) {
+ result[key] = parseInt(match[1], 10);
+ } else {
+ throw new Error(`Failed to parse ${key} from /proc/${pid}/status`);
+ }
+ }
+ return result;
+}
+
+async function measureMemory() {
// Start the Misskey backend server using fork to enable IPC
- const serverProcess = fork(join(__dirname, '../built/boot/entry.js'), [], {
+ const serverProcess = fork(join(__dirname, '../built/boot/entry.js'), ['expose-gc'], {
cwd: join(__dirname, '..'),
env: {
...process.env,
- NODE_ENV: 'test',
+ NODE_ENV: 'production',
+ MK_DISABLE_CLUSTERING: '1',
},
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
+ execArgv: [...process.execArgv, '--expose-gc'],
});
let serverReady = false;
@@ -57,6 +89,40 @@ async function measureMemory() {
process.stderr.write(`[server error] ${err}\n`);
});
+ async function triggerGc() {
+ const ok = new Promise((resolve) => {
+ serverProcess.once('message', (message) => {
+ if (message === 'gc ok') resolve();
+ });
+ });
+
+ serverProcess.send('gc');
+
+ await ok;
+
+ await setTimeout(1000);
+ }
+
+ function createRequest() {
+ return new Promise((resolve, reject) => {
+ const req = http.request({
+ host: 'localhost',
+ port: 61812,
+ path: '/api/meta',
+ method: 'POST',
+ }, (res) => {
+ res.on('data', () => { });
+ res.on('end', () => {
+ resolve();
+ });
+ });
+ req.on('error', (err) => {
+ reject(err);
+ });
+ req.end();
+ });
+ }
+
// Wait for server to be ready or timeout
const startupStartTime = Date.now();
while (!serverReady) {
@@ -73,46 +139,23 @@ async function measureMemory() {
// Wait for memory to settle
await setTimeout(MEMORY_SETTLE_TIME);
- // Get memory usage from the server process via /proc
const pid = serverProcess.pid;
- let memoryInfo;
- try {
- const fs = await import('node:fs/promises');
+ const beforeGc = await getMemoryUsage(pid);
- // Read /proc/[pid]/status for detailed memory info
- const status = await fs.readFile(`/proc/${pid}/status`, 'utf-8');
- const vmRssMatch = status.match(/VmRSS:\s+(\d+)\s+kB/);
- const vmDataMatch = status.match(/VmData:\s+(\d+)\s+kB/);
- const vmSizeMatch = status.match(/VmSize:\s+(\d+)\s+kB/);
+ await triggerGc();
- memoryInfo = {
- rss: vmRssMatch ? parseInt(vmRssMatch[1], 10) * 1024 : null,
- heapUsed: vmDataMatch ? parseInt(vmDataMatch[1], 10) * 1024 : null,
- vmSize: vmSizeMatch ? parseInt(vmSizeMatch[1], 10) * 1024 : null,
- };
- } catch (err) {
- // Fallback: use ps command
- process.stderr.write(`Warning: Could not read /proc/${pid}/status: ${err}\n`);
+ const afterGc = await getMemoryUsage(pid);
- const { execSync } = await import('node:child_process');
- try {
- const ps = execSync(`ps -o rss= -p ${pid}`, { encoding: 'utf-8' });
- const rssKb = parseInt(ps.trim(), 10);
- memoryInfo = {
- rss: rssKb * 1024,
- heapUsed: null,
- vmSize: null,
- };
- } catch {
- memoryInfo = {
- rss: null,
- heapUsed: null,
- vmSize: null,
- error: 'Could not measure memory',
- };
- }
- }
+ // create some http requests to simulate load
+ const REQUEST_COUNT = 10;
+ await Promise.all(
+ Array.from({ length: REQUEST_COUNT }).map(() => createRequest()),
+ );
+
+ await triggerGc();
+
+ const afterRequest = await getMemoryUsage(pid);
// Stop the server
serverProcess.kill('SIGTERM');
@@ -135,15 +178,51 @@ async function measureMemory() {
const result = {
timestamp: new Date().toISOString(),
- startupTimeMs: startupTime,
- memory: memoryInfo,
+ beforeGc,
+ afterGc,
+ afterRequest,
+ };
+
+ return result;
+}
+
+async function main() {
+ // 直列の方が時間的に分散されて正確そうだから直列でやる
+ const results = [];
+ for (let i = 0; i < SAMPLE_COUNT; i++) {
+ const res = await measureMemory();
+ results.push(res);
+ }
+
+ // Calculate averages
+ const beforeGc = structuredClone(keys);
+ const afterGc = structuredClone(keys);
+ const afterRequest = structuredClone(keys);
+ for (const res of results) {
+ for (const key of Object.keys(keys)) {
+ beforeGc[key] += res.beforeGc[key];
+ afterGc[key] += res.afterGc[key];
+ afterRequest[key] += res.afterRequest[key];
+ }
+ }
+ for (const key of Object.keys(keys)) {
+ beforeGc[key] = Math.round(beforeGc[key] / SAMPLE_COUNT);
+ afterGc[key] = Math.round(afterGc[key] / SAMPLE_COUNT);
+ afterRequest[key] = Math.round(afterRequest[key] / SAMPLE_COUNT);
+ }
+
+ const result = {
+ timestamp: new Date().toISOString(),
+ beforeGc,
+ afterGc,
+ afterRequest,
};
// Output as JSON to stdout
console.log(JSON.stringify(result, null, 2));
}
-measureMemory().catch((err) => {
+main().catch((err) => {
console.error(JSON.stringify({
error: err.message,
timestamp: new Date().toISOString(),
diff --git a/packages/backend/scripts/watch.mjs b/packages/backend/scripts/watch.mjs
index a0ccea3b16..9d608b233c 100644
--- a/packages/backend/scripts/watch.mjs
+++ b/packages/backend/scripts/watch.mjs
@@ -21,7 +21,7 @@ import { execa } from 'execa';
});
}, 3000);
- execa('tsc', ['-w', '-p', 'tsconfig.json'], {
+ execa('tsgo', ['-w', '-p', 'tsconfig.json'], {
stdout: process.stdout,
stderr: process.stderr,
});
diff --git a/packages/backend/src/boot/entry.ts b/packages/backend/src/boot/entry.ts
index da585ad68d..3a33d198a5 100644
--- a/packages/backend/src/boot/entry.ts
+++ b/packages/backend/src/boot/entry.ts
@@ -86,6 +86,18 @@ if (!envOption.disableClustering) {
ev.mount();
}
+process.on('message', msg => {
+ if (msg === 'gc') {
+ if (global.gc != null) {
+ logger.info('Manual GC triggered');
+ global.gc();
+ if (process.send != null) process.send('gc ok');
+ } else {
+ logger.warn('Manual GC requested but gc is not available. Start the process with --expose-gc to enable this feature.');
+ }
+ }
+});
+
readyRef.value = true;
// ユニットテスト時にMisskeyが子プロセスで起動された時のため
diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts
index 4776d0d412..041f58e509 100644
--- a/packages/backend/src/boot/master.ts
+++ b/packages/backend/src/boot/master.ts
@@ -4,8 +4,6 @@
*/
import * as fs from 'node:fs';
-import { fileURLToPath } from 'node:url';
-import { dirname } from 'node:path';
import * as os from 'node:os';
import cluster from 'node:cluster';
import chalk from 'chalk';
@@ -17,20 +15,15 @@ import { showMachineInfo } from '@/misc/show-machine-info.js';
import { envOption } from '@/env.js';
import { jobQueue, server } from './common.js';
-const _filename = fileURLToPath(import.meta.url);
-const _dirname = dirname(_filename);
-
-const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
-
const logger = new Logger('core', 'cyan');
const bootLogger = logger.createSubLogger('boot', 'magenta');
const themeColor = chalk.hex('#86b300');
-function greet() {
+function greet(props: { version: string }) {
if (!envOption.quiet) {
//#region Misskey logo
- const v = `v${meta.version}`;
+ const v = `v${props.version}`;
console.log(themeColor(' _____ _ _ '));
console.log(themeColor(' | |_|___ ___| |_ ___ _ _ '));
console.log(themeColor(' | | | | |_ -|_ -| \'_| -_| | |'));
@@ -46,7 +39,7 @@ function greet() {
}
bootLogger.info('Welcome to Misskey!');
- bootLogger.info(`Misskey v${meta.version}`, null, true);
+ bootLogger.info(`Misskey v${props.version}`, null, true);
}
/**
@@ -57,15 +50,15 @@ export async function masterMain() {
// initialize app
try {
- greet();
+ config = loadConfigBoot();
+ greet({ version: config.version });
showEnvironment();
await showMachineInfo(bootLogger);
showNodejsVersion();
- config = loadConfigBoot();
//await connectDb();
if (config.pidFile) fs.writeFileSync(config.pidFile, process.pid.toString());
} catch (e) {
- bootLogger.error('Fatal error occurred during initialization', null, true);
+ bootLogger.error('Fatal error occurred during initialization: ' + e, null, true);
process.exit(1);
}
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 657d7869fa..4cd82bed87 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -219,24 +219,42 @@ export type FulltextSearchProvider = 'sqlLike' | 'sqlPgroonga' | 'meilisearch';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
-const compiledConfigFilePathForTest = resolve(_dirname, '../../../built/._config_.json');
+/** Path of repository root directory */
+let rootDir = _dirname;
+// 見つかるまで上に遡る
+while (!fs.existsSync(resolve(rootDir, 'packages'))) {
+ const parentDir = dirname(rootDir);
+ if (parentDir === rootDir) {
+ throw new Error('Cannot find root directory');
+ }
+ rootDir = parentDir;
+}
+
+/** Path of configuration directory */
+const configDir = resolve(rootDir, '.config');
+/** Path of built directory */
+const projectBuiltDir = resolve(rootDir, 'built');
+
+const compiledConfigFilePathForTest = resolve(projectBuiltDir, '._config_.json');
-export const compiledConfigFilePath = fs.existsSync(compiledConfigFilePathForTest) ? compiledConfigFilePathForTest : resolve(_dirname, '../../../built/.config.json');
+export const compiledConfigFilePath = fs.existsSync(compiledConfigFilePathForTest)
+ ? compiledConfigFilePathForTest
+ : resolve(projectBuiltDir, '.config.json');
export function loadConfig(): Config {
if (!fs.existsSync(compiledConfigFilePath)) {
throw new Error('Compiled configuration file not found. Try running \'pnpm compile-config\'.');
}
- const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8'));
+ const meta = JSON.parse(fs.readFileSync(resolve(projectBuiltDir, 'meta.json'), 'utf-8'));
- const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json');
- const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json');
+ const frontendManifestExists = fs.existsSync(resolve(projectBuiltDir, '_frontend_vite_/manifest.json'));
+ const frontendEmbedManifestExists = fs.existsSync(resolve(projectBuiltDir, '_frontend_embed_vite_/manifest.json'));
const frontendManifest = frontendManifestExists ?
- JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
+ JSON.parse(fs.readFileSync(resolve(projectBuiltDir, '_frontend_vite_/manifest.json'), 'utf-8'))
: { 'src/_boot_.ts': { file: null } };
const frontendEmbedManifest = frontendEmbedManifestExists ?
- JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8'))
+ JSON.parse(fs.readFileSync(resolve(projectBuiltDir, '_frontend_embed_vite_/manifest.json'), 'utf-8'))
: { 'src/boot.ts': { file: null } };
const config = JSON.parse(fs.readFileSync(compiledConfigFilePath, 'utf-8')) as Source;
@@ -334,7 +352,7 @@ export function loadConfig(): Config {
function tryCreateUrl(url: string) {
try {
return new URL(url);
- } catch (e) {
+ } catch (_) {
throw new Error(`url="${url}" is not a valid URL.`);
}
}
diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts
index f8e3eaf01f..5d668bc582 100644
--- a/packages/backend/src/core/AccountMoveService.ts
+++ b/packages/backend/src/core/AccountMoveService.ts
@@ -75,7 +75,7 @@ export class AccountMoveService {
*/
@bindThis
public async moveFromLocal(src: MiLocalUser, dst: MiLocalUser | MiRemoteUser): Promise<unknown> {
- const srcUri = this.userEntityService.getUserUri(src);
+ const _srcUri = this.userEntityService.getUserUri(src);
const dstUri = this.userEntityService.getUserUri(dst);
// add movedToUri to indicate that the user has moved
diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts
index a9f6731977..f750ca212a 100644
--- a/packages/backend/src/core/AnnouncementService.ts
+++ b/packages/backend/src/core/AnnouncementService.ts
@@ -205,7 +205,7 @@ export class AnnouncementService {
announcementId: announcementId,
userId: user.id,
});
- } catch (e) {
+ } catch (_) {
return;
}
diff --git a/packages/backend/src/core/AvatarDecorationService.ts b/packages/backend/src/core/AvatarDecorationService.ts
index 4efd6122b1..70a50a0175 100644
--- a/packages/backend/src/core/AvatarDecorationService.ts
+++ b/packages/backend/src/core/AvatarDecorationService.ts
@@ -39,7 +39,7 @@ export class AvatarDecorationService implements OnApplicationShutdown {
const obj = JSON.parse(data);
if (obj.channel === 'internal') {
- const { type, body } = obj.message as GlobalEvents['internal']['payload'];
+ const { type, body: _ } = obj.message as GlobalEvents['internal']['payload'];
switch (type) {
case 'avatarDecorationCreated':
case 'avatarDecorationUpdated':
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 87575ca59a..f075671d93 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -141,7 +141,7 @@ import { ApLoggerService } from './activitypub/ApLoggerService.js';
import { ApMfmService } from './activitypub/ApMfmService.js';
import { ApRendererService } from './activitypub/ApRendererService.js';
import { ApRequestService } from './activitypub/ApRequestService.js';
-import { ApResolverService } from './activitypub/ApResolverService.js';
+import { ApResolverService, Resolver } from './activitypub/ApResolverService.js';
import { JsonLdService } from './activitypub/JsonLdService.js';
import { RemoteLoggerService } from './RemoteLoggerService.js';
import { RemoteUserResolveService } from './RemoteUserResolveService.js';
@@ -447,6 +447,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ApRendererService,
ApRequestService,
ApResolverService,
+ Resolver,
JsonLdService,
RemoteLoggerService,
RemoteUserResolveService,
@@ -745,6 +746,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ApRendererService,
ApRequestService,
ApResolverService,
+ Resolver,
JsonLdService,
RemoteLoggerService,
RemoteUserResolveService,
diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index c7be0f7843..384704b252 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -366,7 +366,7 @@ export class EmailService {
valid: true,
reason: null,
};
- } catch (error) {
+ } catch (_) {
return {
valid: false,
reason: 'network',
diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts
index af4d0b8c6b..c7c9f8037d 100644
--- a/packages/backend/src/core/FileInfoService.ts
+++ b/packages/backend/src/core/FileInfoService.ts
@@ -484,25 +484,13 @@ export class FileInfoService {
* Calculate blurhash string of image
*/
@bindThis
- private getBlurhash(path: string, type: string): Promise<string> {
- return new Promise(async (resolve, reject) => {
- (await sharpBmp(path, type))
- .raw()
- .ensureAlpha()
- .resize(64, 64, { fit: 'inside' })
- .toBuffer((err, buffer, info) => {
- if (err) return reject(err);
-
- let hash;
-
- try {
- hash = blurhash.encode(new Uint8ClampedArray(buffer), info.width, info.height, 5, 5);
- } catch (e) {
- return reject(e);
- }
-
- resolve(hash);
- });
- });
+ private async getBlurhash(path: string, type: string): Promise<string> {
+ const sharp = await sharpBmp(path, type);
+ const { data: buffer, info } = await sharp
+ .raw()
+ .ensureAlpha()
+ .resize(64, 64, { fit: 'inside' })
+ .toBuffer({ resolveWithObject: true });
+ return blurhash.encode(new Uint8ClampedArray(buffer), info.width, info.height, 5, 5);
}
}
diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index f4c747b139..da5982abf6 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -38,11 +38,7 @@ export interface BroadcastTypes {
emojis: Packed<'EmojiDetailed'>[];
};
emojiDeleted: {
- emojis: {
- id?: string;
- name: string;
- [other: string]: any;
- }[];
+ emojis: Packed<'EmojiDetailed'>[];
};
announcementCreated: {
announcement: Packed<'Announcement'>;
diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index b9f1c62d9d..274966d921 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -308,7 +308,7 @@ export class MfmService {
try {
const date = new Date(parseInt(text, 10) * 1000);
return `<time datetime="${escapeHtml(date.toISOString())}">${escapeHtml(date.toISOString())}</time>`;
- } catch (err) {
+ } catch (_) {
return fnDefault(node);
}
}
@@ -376,7 +376,7 @@ export class MfmService {
try {
const url = new URL(node.props.url);
return `<a href="${escapeHtml(url.href)}">${toHtml(node.children)}</a>`;
- } catch (err) {
+ } catch (_) {
return `[${toHtml(node.children)}](${escapeHtml(node.props.url)})`;
}
},
@@ -390,7 +390,7 @@ export class MfmService {
try {
const url = new URL(href);
return `<a href="${escapeHtml(url.href)}" class="u-url mention">${escapeHtml(acct)}</a>`;
- } catch (err) {
+ } catch (_) {
return escapeHtml(acct);
}
},
@@ -419,7 +419,7 @@ export class MfmService {
try {
const url = new URL(node.props.url);
return `<a href="${escapeHtml(url.href)}">${escapeHtml(node.props.url)}</a>`;
- } catch (err) {
+ } catch (_) {
return escapeHtml(node.props.url);
}
},
diff --git a/packages/backend/src/core/NoteDraftService.ts b/packages/backend/src/core/NoteDraftService.ts
index a346ff7618..e144138c2c 100644
--- a/packages/backend/src/core/NoteDraftService.ts
+++ b/packages/backend/src/core/NoteDraftService.ts
@@ -187,9 +187,9 @@ export class NoteDraftService {
}
//#region visibleUsers
- let visibleUsers: MiUser[] = [];
+ let _visibleUsers: MiUser[] = [];
if (data.visibleUserIds != null && data.visibleUserIds.length > 0) {
- visibleUsers = await this.usersRepository.findBy({
+ _visibleUsers = await this.usersRepository.findBy({
id: In(data.visibleUserIds),
});
}
diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index 42782167bb..f90ae80731 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -6,7 +6,6 @@
import { randomUUID } from 'node:crypto';
import { Inject, Injectable } from '@nestjs/common';
import { MetricsTime, type JobType } from 'bullmq';
-import { parse as parseRedisInfo } from 'redis-info';
import type { IActivity } from '@/core/activitypub/type.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import type { MiWebhook, WebhookEventTypes } from '@/models/Webhook.js';
@@ -86,6 +85,19 @@ const REPEATABLE_SYSTEM_JOB_DEF = [{
pattern: '0 4 * * *',
}];
+function parseRedisInfo(infoText: string): Record<string, string> {
+ const fields = infoText
+ .split('\n')
+ .filter(line => line.length > 0 && !line.startsWith('#'))
+ .map(line => line.trim().split(':'));
+
+ const result: Record<string, string> = {};
+ for (const [key, value] of fields) {
+ result[key] = value;
+ }
+ return result;
+}
+
@Injectable()
export class QueueService {
constructor(
@@ -890,7 +902,7 @@ export class QueueService {
},
db: {
version: db.redis_version,
- mode: db.redis_mode,
+ mode: db.redis_mode as 'cluster' | 'standalone' | 'sentinel',
runId: db.run_id,
processId: db.process_id,
port: parseInt(db.tcp_port),
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index f2f7480dfa..2ffee69c21 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -314,7 +314,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
default:
return false;
}
- } catch (err) {
+ } catch (_) {
// TODO: log error
return false;
}
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
index 71dc718916..87097ada93 100644
--- a/packages/backend/src/core/SearchService.ts
+++ b/packages/backend/src/core/SearchService.ts
@@ -190,8 +190,7 @@ export class SearchService {
return this.searchNoteByMeiliSearch(q, me, opts, pagination);
}
default: {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const typeCheck: never = this.provider;
+ const _: never = this.provider;
return [];
}
}
diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts
index 7920e58e36..3ecb912a64 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(_ => {});
+ await this.unFollowAll(user).catch(_ => {});
})();
}
@@ -67,7 +67,7 @@ export class UserSuspendService {
});
(async () => {
- await this.postUnsuspend(user).catch(e => {});
+ await this.postUnsuspend(user).catch(_ => {});
})();
}
diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts
index 21ea9b9983..e3ceebccae 100644
--- a/packages/backend/src/core/UtilityService.ts
+++ b/packages/backend/src/core/UtilityService.ts
@@ -98,7 +98,7 @@ export class UtilityService {
try {
// TODO: RE2インスタンスをキャッシュ
return new RE2(regexp[1], regexp[2]).test(text);
- } catch (err) {
+ } catch (_) {
// This should never happen due to input sanitisation.
return false;
}
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 81637580e3..ff47ca930d 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -95,7 +95,7 @@ export class ApInboxService {
if (isCollectionOrOrderedCollection(activity)) {
const results = [] as [string, string | void][];
// eslint-disable-next-line no-param-reassign
- resolver ??= this.apResolverService.createResolver();
+ resolver ??= await this.apResolverService.createResolver();
const items = toArray(isCollection(activity) ? activity.items : activity.orderedItems);
if (items.length >= resolver.getRecursionLimit()) {
@@ -221,7 +221,7 @@ export class ApInboxService {
this.logger.info(`Accept: ${uri}`);
// eslint-disable-next-line no-param-reassign
- resolver ??= this.apResolverService.createResolver();
+ resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(err => {
this.logger.error(`Resolution failed: ${err}`);
@@ -284,7 +284,7 @@ export class ApInboxService {
this.logger.info(`Announce: ${uri}`);
// eslint-disable-next-line no-param-reassign
- resolver ??= this.apResolverService.createResolver();
+ resolver ??= await this.apResolverService.createResolver();
if (!activity.object) return 'skip: activity has no object property';
const targetUri = getApId(activity.object);
@@ -406,7 +406,7 @@ export class ApInboxService {
}
// eslint-disable-next-line no-param-reassign
- resolver ??= this.apResolverService.createResolver();
+ resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`);
@@ -575,7 +575,7 @@ export class ApInboxService {
this.logger.info(`Reject: ${uri}`);
// eslint-disable-next-line no-param-reassign
- resolver ??= this.apResolverService.createResolver();
+ resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`);
@@ -642,7 +642,7 @@ export class ApInboxService {
this.logger.info(`Undo: ${uri}`);
// eslint-disable-next-line no-param-reassign
- resolver ??= this.apResolverService.createResolver();
+ resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`);
@@ -774,7 +774,7 @@ export class ApInboxService {
this.logger.debug('Update');
// eslint-disable-next-line no-param-reassign
- resolver ??= this.apResolverService.createResolver();
+ resolver ??= await this.apResolverService.createResolver();
const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`);
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 4570977c5d..8c461b6031 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -515,7 +515,7 @@ export class ApRendererService {
const restPart = maybeUrl.slice(match[0].length);
return `<a href="${urlPartParsed.href}" rel="me nofollow noopener" target="_blank">${urlPart}</a>${restPart}`;
- } catch (e) {
+ } catch (_) {
return maybeUrl;
}
};
diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts
index 49298a1d22..d14b82dc92 100644
--- a/packages/backend/src/core/activitypub/ApRequestService.ts
+++ b/packages/backend/src/core/activitypub/ApRequestService.ts
@@ -226,7 +226,7 @@ export class ApRequestService {
return await this.signedGet(href, user, allowSoftfail, false);
}
}
- } catch (e) {
+ } catch (_) {
// something went wrong parsing the HTML, ignore the whole thing
}
}
diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts
index 646150455b..0f51b1ce8d 100644
--- a/packages/backend/src/core/activitypub/ApResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApResolverService.ts
@@ -3,10 +3,17 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Inject, Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { IsNull, Not } from 'typeorm';
import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
-import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository, MiMeta } from '@/models/_.js';
+import type {
+ FollowRequestsRepository,
+ MiMeta,
+ NoteReactionsRepository,
+ NotesRepository,
+ PollsRepository,
+ UsersRepository
+} from '@/models/_.js';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { DI } from '@/di-symbols.js';
@@ -16,26 +23,43 @@ import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
+import type { ICollection, IObject, IOrderedCollection } from './type.js';
import { isCollectionOrOrderedCollection } from './type.js';
import { ApDbResolverService } from './ApDbResolverService.js';
import { ApRendererService } from './ApRendererService.js';
import { ApRequestService } from './ApRequestService.js';
import { FetchAllowSoftFailMask } from './misc/check-against-url.js';
-import type { IObject, ICollection, IOrderedCollection } from './type.js';
+import { ModuleRef } from '@nestjs/core';
+@Injectable({ scope: Scope.TRANSIENT })
export class Resolver {
private history: Set<string>;
private user?: MiLocalUser;
private logger: Logger;
+ private recursionLimit = 256;
constructor(
+ @Inject(DI.config)
private config: Config,
+
+ @Inject(DI.meta)
private meta: MiMeta,
+
+ @Inject(DI.usersRepository)
private usersRepository: UsersRepository,
+
+ @Inject(DI.notesRepository)
private notesRepository: NotesRepository,
+
+ @Inject(DI.pollsRepository)
private pollsRepository: PollsRepository,
+
+ @Inject(DI.noteReactionsRepository)
private noteReactionsRepository: NoteReactionsRepository,
+
+ @Inject(DI.followRequestsRepository)
private followRequestsRepository: FollowRequestsRepository,
+
private utilityService: UtilityService,
private systemAccountService: SystemAccountService,
private apRequestService: ApRequestService,
@@ -43,7 +67,6 @@ export class Resolver {
private apRendererService: ApRendererService,
private apDbResolverService: ApDbResolverService,
private loggerService: LoggerService,
- private recursionLimit = 256,
) {
this.history = new Set();
this.logger = this.loggerService.getLogger('ap-resolve');
@@ -180,54 +203,12 @@ export class Resolver {
@Injectable()
export class ApResolverService {
constructor(
- @Inject(DI.config)
- private config: Config,
-
- @Inject(DI.meta)
- private meta: MiMeta,
-
- @Inject(DI.usersRepository)
- private usersRepository: UsersRepository,
-
- @Inject(DI.notesRepository)
- private notesRepository: NotesRepository,
-
- @Inject(DI.pollsRepository)
- private pollsRepository: PollsRepository,
-
- @Inject(DI.noteReactionsRepository)
- private noteReactionsRepository: NoteReactionsRepository,
-
- @Inject(DI.followRequestsRepository)
- private followRequestsRepository: FollowRequestsRepository,
-
- private utilityService: UtilityService,
- private systemAccountService: SystemAccountService,
- private apRequestService: ApRequestService,
- private httpRequestService: HttpRequestService,
- private apRendererService: ApRendererService,
- private apDbResolverService: ApDbResolverService,
- private loggerService: LoggerService,
+ private moduleRef: ModuleRef,
) {
}
@bindThis
- public createResolver(): Resolver {
- return new Resolver(
- this.config,
- this.meta,
- this.usersRepository,
- this.notesRepository,
- this.pollsRepository,
- this.noteReactionsRepository,
- this.followRequestsRepository,
- this.utilityService,
- this.systemAccountService,
- this.apRequestService,
- this.httpRequestService,
- this.apRendererService,
- this.apDbResolverService,
- this.loggerService,
- );
+ public async createResolver(): Promise<Resolver> {
+ return await this.moduleRef.create(Resolver);
}
}
diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts
index e7ece87b01..0496774c19 100644
--- a/packages/backend/src/core/activitypub/models/ApImageService.ts
+++ b/packages/backend/src/core/activitypub/models/ApImageService.ts
@@ -46,7 +46,7 @@ export class ApImageService {
throw new Error('actor has been suspended');
}
- const image = await this.apResolverService.createResolver().resolve(value);
+ const image = await (await this.apResolverService.createResolver()).resolve(value);
if (!isDocument(image)) return null;
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 214d32f67f..1fc5728c98 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -128,7 +128,7 @@ export class ApNoteService {
@bindThis
public async createNote(value: string | IObject, actor?: MiRemoteUser, resolver?: Resolver, silent = false): Promise<MiNote | null> {
// eslint-disable-next-line no-param-reassign
- if (resolver == null) resolver = this.apResolverService.createResolver();
+ if (resolver == null) resolver = await this.apResolverService.createResolver();
const object = await resolver.resolve(value);
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index e52078ed0f..ebe8e9c964 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -310,7 +310,7 @@ export class ApPersonService implements OnModuleInit {
}
// eslint-disable-next-line no-param-reassign
- if (resolver == null) resolver = this.apResolverService.createResolver();
+ if (resolver == null) resolver = await this.apResolverService.createResolver();
const object = await resolver.resolve(uri);
if (object.id == null) throw new Error('invalid object.id: ' + object.id);
@@ -500,7 +500,7 @@ export class ApPersonService implements OnModuleInit {
//#endregion
// eslint-disable-next-line no-param-reassign
- if (resolver == null) resolver = this.apResolverService.createResolver();
+ if (resolver == null) resolver = await this.apResolverService.createResolver();
const object = hint ?? await resolver.resolve(uri);
@@ -678,7 +678,7 @@ export class ApPersonService implements OnModuleInit {
// リモートサーバーからフェッチしてきて登録
// eslint-disable-next-line no-param-reassign
- if (resolver == null) resolver = this.apResolverService.createResolver();
+ if (resolver == null) resolver = await this.apResolverService.createResolver();
return await this.createPerson(uri, resolver);
}
@@ -707,7 +707,7 @@ export class ApPersonService implements OnModuleInit {
this.logger.info(`Updating the featured: ${user.uri}`);
- const _resolver = resolver ?? this.apResolverService.createResolver();
+ const _resolver = resolver ?? await this.apResolverService.createResolver();
// Resolve to (Ordered)Collection Object
const collection = await _resolver.resolveCollection(user.featured);
diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
index a2cdaf02ca..8ac2f21e26 100644
--- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts
+++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts
@@ -45,7 +45,7 @@ export class ApQuestionService {
@bindThis
public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> {
// eslint-disable-next-line no-param-reassign
- if (resolver == null) resolver = this.apResolverService.createResolver();
+ if (resolver == null) resolver = await this.apResolverService.createResolver();
const question = await resolver.resolve(source);
if (!isQuestion(question)) throw new Error('invalid type');
@@ -91,7 +91,7 @@ export class ApQuestionService {
// resolve new Question object
// eslint-disable-next-line no-param-reassign
- if (resolver == null) resolver = this.apResolverService.createResolver();
+ if (resolver == null) resolver = await this.apResolverService.createResolver();
const question = await resolver.resolve(value);
this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);
diff --git a/packages/backend/src/core/entities/ChatEntityService.ts b/packages/backend/src/core/entities/ChatEntityService.ts
index cfa983e766..f69a484398 100644
--- a/packages/backend/src/core/entities/ChatEntityService.ts
+++ b/packages/backend/src/core/entities/ChatEntityService.ts
@@ -138,7 +138,7 @@ export class ChatEntityService {
const reactions: { reaction: string; }[] = [];
for (const record of message.reactions) {
- const [userId, reaction] = record.split('/');
+ const [, reaction] = record.split('/');
reactions.push({
reaction,
});
diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts
index a6f7f369a6..1865d494c4 100644
--- a/packages/backend/src/core/entities/DriveFileEntityService.ts
+++ b/packages/backend/src/core/entities/DriveFileEntityService.ts
@@ -17,6 +17,7 @@ import { deepClone } from '@/misc/clone.js';
import { bindThis } from '@/decorators.js';
import { isMimeImage } from '@/misc/is-mime-image.js';
import { IdService } from '@/core/IdService.js';
+import { uniqueByKey } from '@/misc/unique-by-key.js';
import { UtilityService } from '../UtilityService.js';
import { VideoProcessingService } from '../VideoProcessingService.js';
import { UserEntityService } from './UserEntityService.js';
@@ -226,6 +227,7 @@ export class DriveFileEntityService {
options?: PackOptions,
hint?: {
packedUser?: Packed<'UserLite'>
+ packedFolder?: Packed<'DriveFolder'>
},
): Promise<Packed<'DriveFile'> | null> {
const opts = Object.assign({
@@ -250,9 +252,9 @@ export class DriveFileEntityService {
thumbnailUrl: this.getThumbnailUrl(file),
comment: file.comment,
folderId: file.folderId,
- folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, {
+ folder: opts.detail && file.folderId ? (hint?.packedFolder ?? this.driveFolderEntityService.pack(file.folderId, {
detail: true,
- }) : null,
+ })) : null,
userId: file.userId,
user: (opts.withUser && file.userId) ? hint?.packedUser ?? this.userEntityService.pack(file.userId) : null,
});
@@ -263,10 +265,41 @@ export class DriveFileEntityService {
files: MiDriveFile[],
options?: PackOptions,
): Promise<Packed<'DriveFile'>[]> {
- const _user = files.map(({ user, userId }) => user ?? userId).filter(x => x != null);
- const _userMap = await this.userEntityService.packMany(_user)
- .then(users => new Map(users.map(user => [user.id, user])));
- const items = await Promise.all(files.map(f => this.packNullable(f, options, f.userId ? { packedUser: _userMap.get(f.userId) } : {})));
+ // -- ユーザ情報の事前取得 --
+
+ let userMap: Map<string, Packed<'UserLite'>> | null = null;
+ if (options?.withUser) {
+ const users = files
+ .map(({ user, userId }) => user ?? userId)
+ .filter(x => x != null);
+
+ const uniqueUsers = uniqueByKey(users, (user) => typeof user === 'string' ? user : user.id);
+ const packedUsers = await this.userEntityService.packMany(uniqueUsers);
+ userMap = new Map(packedUsers.map(user => [user.id, user]));
+ }
+
+ // -- フォルダ情報の事前取得 --
+
+ let folderMap: Map<string, Packed<'DriveFolder'>> | null = null;
+ if (options?.detail) {
+ const folders = files
+ .map(({ folder, folderId }) => folder ?? folderId)
+ .filter(x => x != null);
+
+ const uniqueFolders = uniqueByKey(folders, (folder) => typeof folder === 'string' ? folder : folder.id);
+ const packedFolders = await this.driveFolderEntityService.packMany(uniqueFolders, { detail: true });
+ folderMap = new Map(packedFolders.map(folder => [folder.id, folder]));
+ }
+
+ const items = await Promise.all(files.map(f => this.packNullable(
+ f,
+ options,
+ {
+ packedUser: f.userId ? userMap?.get(f.userId) : undefined,
+ packedFolder: f.folderId ? folderMap?.get(f.folderId) : undefined,
+ },
+ )));
+
return items.filter(x => x != null);
}
diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts
index 299f23ad38..326421e149 100644
--- a/packages/backend/src/core/entities/DriveFolderEntityService.ts
+++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts
@@ -12,6 +12,9 @@ import type { } from '@/models/Blocking.js';
import type { MiDriveFolder } from '@/models/DriveFolder.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
+import { In } from 'typeorm';
+import { uniqueByKey } from '@/misc/unique-by-key.js';
+import { splitIdAndObjects } from '@/misc/split-id-and-objects.js';
@Injectable()
export class DriveFolderEntityService {
@@ -32,12 +35,20 @@ export class DriveFolderEntityService {
options?: {
detail: boolean
},
+ hint?: {
+ folderMap?: Map<string, MiDriveFolder>;
+ foldersCountMap?: Map<string, number> | null;
+ filesCountMap?: Map<string, number> | null;
+ parentPacker?: (id: string) => Promise<Packed<'DriveFolder'>>;
+ },
): Promise<Packed<'DriveFolder'>> {
const opts = Object.assign({
detail: false,
}, options);
- const folder = typeof src === 'object' ? src : await this.driveFoldersRepository.findOneByOrFail({ id: src });
+ const folder = typeof src === 'object'
+ ? src
+ : hint?.folderMap?.get(src) ?? await this.driveFoldersRepository.findOneByOrFail({ id: src });
return await awaitAll({
id: folder.id,
@@ -46,20 +57,141 @@ export class DriveFolderEntityService {
parentId: folder.parentId,
...(opts.detail ? {
- foldersCount: this.driveFoldersRepository.countBy({
- parentId: folder.id,
- }),
- filesCount: this.driveFilesRepository.countBy({
- folderId: folder.id,
- }),
+ foldersCount: hint?.foldersCountMap?.get(folder.id)
+ ?? this.driveFoldersRepository.countBy({
+ parentId: folder.id,
+ }),
+ filesCount: hint?.filesCountMap?.get(folder.id)
+ ?? this.driveFilesRepository.countBy({
+ folderId: folder.id,
+ }),
...(folder.parentId ? {
- parent: this.pack(folder.parentId, {
- detail: true,
- }),
+ parent: hint?.parentPacker
+ ? hint.parentPacker(folder.parentId)
+ : this.pack(folder.parentId, { detail: true }, hint),
} : {}),
} : {}),
});
}
-}
+ public async packMany(
+ src: Array<MiDriveFolder['id'] | MiDriveFolder>,
+ options?: {
+ detail: boolean
+ },
+ ): Promise<Array<Packed<'DriveFolder'>>> {
+ /**
+ * 重複を除去しつつ、必要なDriveFolderオブジェクトをすべて取得する
+ */
+ const collectUniqueObjects = async (src: Array<MiDriveFolder['id'] | MiDriveFolder>) => {
+ const uniqueSrc = uniqueByKey(
+ src,
+ (s) => typeof s === 'string' ? s : s.id,
+ );
+ const { ids, objects } = splitIdAndObjects(uniqueSrc);
+
+ const uniqueObjects = new Map<string, MiDriveFolder>(objects.map(s => [s.id, s]));
+ const needsFetchIds = ids.filter(id => !uniqueObjects.has(id));
+
+ if (needsFetchIds.length > 0) {
+ const fetchedObjects = await this.driveFoldersRepository.find({
+ where: {
+ id: In(needsFetchIds),
+ },
+ });
+ for (const obj of fetchedObjects) {
+ uniqueObjects.set(obj.id, obj);
+ }
+ }
+
+ return uniqueObjects;
+ };
+
+ /**
+ * 親フォルダーを再帰的に収集する
+ */
+ const collectAncestors = async (folderMap: Map<string, MiDriveFolder>) => {
+ for (;;) {
+ const parentIds = new Set<string>();
+ for (const folder of folderMap.values()) {
+ if (folder.parentId != null && !folderMap.has(folder.parentId)) {
+ parentIds.add(folder.parentId);
+ }
+ }
+
+ if (parentIds.size === 0) break;
+
+ const fetchedParents = await this.driveFoldersRepository.find({
+ where: {
+ id: In([...parentIds]),
+ },
+ });
+
+ if (fetchedParents.length === 0) break;
+
+ for (const parent of fetchedParents) {
+ folderMap.set(parent.id, parent);
+ }
+ }
+ };
+
+ const opts = Object.assign({
+ detail: false,
+ }, options);
+
+ const folderMap = await collectUniqueObjects(src);
+
+ let foldersCountMap: Map<string, number> | null = null;
+ let filesCountMap: Map<string, number> | null = null;
+ if (opts.detail) {
+ await collectAncestors(folderMap);
+
+ const ids = [...folderMap.keys()];
+ if (ids.length > 0) {
+ const folderCounts = await this.driveFoldersRepository.createQueryBuilder('folder')
+ .select('folder.parentId', 'parentId')
+ .addSelect('COUNT(*)', 'count')
+ .where('folder.parentId IN (:...ids)', { ids })
+ .groupBy('folder.parentId')
+ .getRawMany<{ parentId: string; count: string }>();
+
+ const fileCounts = await this.driveFilesRepository.createQueryBuilder('file')
+ .select('file.folderId', 'folderId')
+ .addSelect('COUNT(*)', 'count')
+ .where('file.folderId IN (:...ids)', { ids })
+ .groupBy('file.folderId')
+ .getRawMany<{ folderId: string; count: string }>();
+
+ foldersCountMap = new Map(folderCounts.map(row => [row.parentId, Number(row.count)]));
+ filesCountMap = new Map(fileCounts.map(row => [row.folderId, Number(row.count)]));
+ } else {
+ foldersCountMap = new Map();
+ filesCountMap = new Map();
+ }
+ }
+
+ const packedMap = new Map<string, Promise<Packed<'DriveFolder'>>>();
+ const packFromId = (id: string): Promise<Packed<'DriveFolder'>> => {
+ const cached = packedMap.get(id);
+ if (cached) return cached;
+
+ const folder = folderMap.get(id);
+ if (!folder) {
+ throw new Error(`DriveFolder not found: ${id}`);
+ }
+
+ const packedPromise = this.pack(folder, options, {
+ folderMap,
+ foldersCountMap,
+ filesCountMap,
+ parentPacker: packFromId,
+ });
+ packedMap.set(id, packedPromise);
+
+ return packedPromise;
+ };
+
+ return Promise.all(src.map(s => packFromId(typeof s === 'string' ? s : s.id)));
+ }
+}
diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts
index 490d3f2511..309de3b08f 100644
--- a/packages/backend/src/core/entities/EmojiEntityService.ts
+++ b/packages/backend/src/core/entities/EmojiEntityService.ts
@@ -41,7 +41,7 @@ export class EmojiEntityService {
@bindThis
public packSimpleMany(
- emojis: any[],
+ emojis: (MiEmoji['id'] | MiEmoji)[],
) {
return Promise.all(emojis.map(x => this.packSimple(x)));
}
@@ -69,7 +69,7 @@ export class EmojiEntityService {
@bindThis
public packDetailedMany(
- emojis: any[],
+ emojis: (MiEmoji['id'] | MiEmoji)[],
): Promise<Packed<'EmojiDetailed'>[]> {
return Promise.all(emojis.map(x => this.packDetailed(x)));
}
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index 2da614a120..8e56ddbc02 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -55,13 +55,13 @@ export class MetaEntityService {
if (instance.defaultLightTheme) {
try {
defaultLightTheme = JSON.stringify(JSON5.parse(instance.defaultLightTheme));
- } catch (e) {
+ } catch (_) {
}
}
if (instance.defaultDarkTheme) {
try {
defaultDarkTheme = JSON.stringify(JSON5.parse(instance.defaultDarkTheme));
- } catch (e) {
+ } catch (_) {
}
}
diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts
index 54ce4d472a..fe4926bfe3 100644
--- a/packages/backend/src/core/entities/NoteReactionEntityService.ts
+++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts
@@ -54,7 +54,7 @@ export class NoteReactionEntityService implements OnModuleInit {
packedUser?: Packed<'UserLite'>
},
): Promise<Packed<'NoteReaction'>> {
- const opts = Object.assign({
+ const _opts = Object.assign({
}, options);
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
@@ -90,7 +90,7 @@ export class NoteReactionEntityService implements OnModuleInit {
packedUser?: Packed<'UserLite'>
},
): Promise<Packed<'NoteReactionWithNote'>> {
- const opts = Object.assign({
+ const _opts = Object.assign({
}, options);
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
diff --git a/packages/backend/src/core/entities/ReversiGameEntityService.ts b/packages/backend/src/core/entities/ReversiGameEntityService.ts
index df042e75c1..21099bad3e 100644
--- a/packages/backend/src/core/entities/ReversiGameEntityService.ts
+++ b/packages/backend/src/core/entities/ReversiGameEntityService.ts
@@ -14,6 +14,10 @@ import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { UserEntityService } from './UserEntityService.js';
+function assertBw(bw: string): bw is Packed<'ReversiGameDetailed'>['bw'] {
+ return ['random', '1', '2'].includes(bw);
+}
+
@Injectable()
export class ReversiGameEntityService {
constructor(
@@ -58,7 +62,7 @@ export class ReversiGameEntityService {
surrenderedUserId: game.surrenderedUserId,
timeoutUserId: game.timeoutUserId,
black: game.black,
- bw: game.bw,
+ bw: assertBw(game.bw) ? game.bw : 'random',
isLlotheo: game.isLlotheo,
canPutEverywhere: game.canPutEverywhere,
loopedBoard: game.loopedBoard,
@@ -116,7 +120,7 @@ export class ReversiGameEntityService {
surrenderedUserId: game.surrenderedUserId,
timeoutUserId: game.timeoutUserId,
black: game.black,
- bw: game.bw,
+ bw: assertBw(game.bw) ? game.bw : 'random',
isLlotheo: game.isLlotheo,
canPutEverywhere: game.canPutEverywhere,
loopedBoard: game.loopedBoard,
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index ac5b855096..0f4051e7b8 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -720,7 +720,7 @@ export class UserEntityService implements OnModuleInit {
me,
{
...options,
- userProfile: profilesMap.get(u.id),
+ userProfile: profilesMap?.get(u.id),
userRelations: userRelations,
userMemos: userMemos,
pinNotes: pinNotes,
diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts
index c50f2b723c..0d1c7ee46e 100644
--- a/packages/backend/src/misc/check-word-mute.ts
+++ b/packages/backend/src/misc/check-word-mute.ts
@@ -56,7 +56,7 @@ export async function checkWordMute(note: NoteLike, me: UserLike | null | undefi
try {
return new RE2(regexp[1], regexp[2]).test(text);
- } catch (err) {
+ } catch (_) {
// This should never happen due to input sanitisation.
return false;
}
diff --git a/packages/backend/src/misc/get-ip-hash.ts b/packages/backend/src/misc/get-ip-hash.ts
index e132fa8f31..571996973b 100644
--- a/packages/backend/src/misc/get-ip-hash.ts
+++ b/packages/backend/src/misc/get-ip-hash.ts
@@ -12,7 +12,7 @@ export function getIpHash(ip: string): string {
// (this means for IPv4 the entire address is used)
const prefix = IPCIDR.createAddress(ip).mask(64);
return 'ip-' + BigInt('0b' + prefix).toString(36);
- } catch (e) {
+ } catch (_) {
const prefix = IPCIDR.createAddress(ip.replace(/:[0-9]+$/, '')).mask(64);
return 'ip-' + BigInt('0b' + prefix).toString(36);
}
diff --git a/packages/backend/src/misc/i18n.ts b/packages/backend/src/misc/i18n.ts
index 6cbbdef74c..40067cacf5 100644
--- a/packages/backend/src/misc/i18n.ts
+++ b/packages/backend/src/misc/i18n.ts
@@ -26,7 +26,7 @@ export class I18n<T extends Record<string, any>> {
}
}
return str;
- } catch (e) {
+ } catch (_) {
console.warn(`missing localization '${key}'`);
return key;
}
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index ed7d5bfc3a..cf233defd9 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -64,6 +64,7 @@ import {
packedMetaDetailedOnlySchema,
packedMetaDetailedSchema,
packedMetaLiteSchema,
+ packedMetaClientOptionsSchema,
} from '@/models/json-schema/meta.js';
import { packedUserWebhookSchema } from '@/models/json-schema/user-webhook.js';
import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js';
@@ -135,6 +136,7 @@ export const refs = {
MetaLite: packedMetaLiteSchema,
MetaDetailedOnly: packedMetaDetailedOnlySchema,
MetaDetailed: packedMetaDetailedSchema,
+ MetaClientOptions: packedMetaClientOptionsSchema,
UserWebhook: packedUserWebhookSchema,
SystemWebhook: packedSystemWebhookSchema,
AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema,
@@ -262,8 +264,6 @@ type ObjectSchemaTypeDef<p extends Schema> =
never :
any;
-type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>;
-
export type SchemaTypeDef<p extends Schema> =
p['type'] extends 'null' ? null :
p['type'] extends 'integer' ? number :
diff --git a/packages/backend/src/misc/split-id-and-objects.ts b/packages/backend/src/misc/split-id-and-objects.ts
new file mode 100644
index 0000000000..d23bb93695
--- /dev/null
+++ b/packages/backend/src/misc/split-id-and-objects.ts
@@ -0,0 +1,27 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/**
+ * idとオブジェクトを分離する
+ * @param input idまたはオブジェクトの配列
+ * @returns idの配列とオブジェクトの配列
+ */
+export function splitIdAndObjects<T extends { id: string }>(input: (T | string)[]): { ids: string[]; objects: T[] } {
+ const ids: string[] = [];
+ const objects : T[] = [];
+
+ for (const item of input) {
+ if (typeof item === 'string') {
+ ids.push(item);
+ } else {
+ objects.push(item);
+ }
+ }
+
+ return {
+ ids,
+ objects,
+ };
+}
diff --git a/packages/backend/src/misc/unique-by-key.ts b/packages/backend/src/misc/unique-by-key.ts
new file mode 100644
index 0000000000..4308e29d21
--- /dev/null
+++ b/packages/backend/src/misc/unique-by-key.ts
@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+/**
+ * itemsの中でkey関数が返す値が重複しないようにした配列を返す
+ * @param items 重複を除去したい配列
+ * @param key 重複判定に使うキーを返す関数
+ * @returns 重複を除去した配列
+ */
+export function uniqueByKey<TItem, TKey = string>(items: Iterable<TItem>, key: (item: TItem) => TKey): TItem[] {
+ const map = new Map<TKey, TItem>();
+ for (const item of items) {
+ const k = key(item);
+ if (!map.has(k)) {
+ map.set(k, item);
+ }
+ }
+ return [...map.values()];
+}
diff --git a/packages/backend/src/models/AbuseReportNotificationRecipient.ts b/packages/backend/src/models/AbuseReportNotificationRecipient.ts
index 17ec6abed5..daed81c174 100644
--- a/packages/backend/src/models/AbuseReportNotificationRecipient.ts
+++ b/packages/backend/src/models/AbuseReportNotificationRecipient.ts
@@ -67,7 +67,7 @@ export class MiAbuseReportNotificationRecipient {
/**
* 通知先のユーザ.
*/
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'userId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId1' })
@@ -76,7 +76,7 @@ export class MiAbuseReportNotificationRecipient {
/**
* 通知先のユーザプロフィール.
*/
- @ManyToOne(type => MiUserProfile, {
+ @ManyToOne(() => MiUserProfile, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' })
@@ -96,7 +96,7 @@ export class MiAbuseReportNotificationRecipient {
/**
* 通知先のシステムWebhook.
*/
- @ManyToOne(type => MiSystemWebhook, {
+ @ManyToOne(() => MiSystemWebhook, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'systemWebhookId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_systemWebhookId' })
diff --git a/packages/backend/src/models/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts
index d43ebf9342..cd49fcddfe 100644
--- a/packages/backend/src/models/AbuseUserReport.ts
+++ b/packages/backend/src/models/AbuseUserReport.ts
@@ -18,7 +18,7 @@ export class MiAbuseUserReport {
@Column(id())
public targetUserId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -28,7 +28,7 @@ export class MiAbuseUserReport {
@Column(id())
public reporterId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -40,7 +40,7 @@ export class MiAbuseUserReport {
})
public assigneeId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/AccessToken.ts b/packages/backend/src/models/AccessToken.ts
index 6f98c14ec1..a853dcc6cb 100644
--- a/packages/backend/src/models/AccessToken.ts
+++ b/packages/backend/src/models/AccessToken.ts
@@ -41,7 +41,7 @@ export class MiAccessToken {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -53,7 +53,7 @@ export class MiAccessToken {
})
public appId: MiApp['id'] | null;
- @ManyToOne(type => MiApp, {
+ @ManyToOne(() => MiApp, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Announcement.ts b/packages/backend/src/models/Announcement.ts
index d0c59fff50..f664c75262 100644
--- a/packages/backend/src/models/Announcement.ts
+++ b/packages/backend/src/models/Announcement.ts
@@ -79,7 +79,7 @@ export class MiAnnouncement {
})
public userId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/AnnouncementRead.ts b/packages/backend/src/models/AnnouncementRead.ts
index 47de8dd180..2133cff140 100644
--- a/packages/backend/src/models/AnnouncementRead.ts
+++ b/packages/backend/src/models/AnnouncementRead.ts
@@ -18,7 +18,7 @@ export class MiAnnouncementRead {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -28,7 +28,7 @@ export class MiAnnouncementRead {
@Column(id())
public announcementId: MiAnnouncement['id'];
- @ManyToOne(type => MiAnnouncement, {
+ @ManyToOne(() => MiAnnouncement, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts
index ccc8823703..3433cf20af 100644
--- a/packages/backend/src/models/Antenna.ts
+++ b/packages/backend/src/models/Antenna.ts
@@ -24,7 +24,7 @@ export class MiAntenna {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -45,7 +45,7 @@ export class MiAntenna {
})
public userListId: MiUserList['id'] | null;
- @ManyToOne(type => MiUserList, {
+ @ManyToOne(() => MiUserList, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/App.ts b/packages/backend/src/models/App.ts
index 0185e2995c..bbb80b99ef 100644
--- a/packages/backend/src/models/App.ts
+++ b/packages/backend/src/models/App.ts
@@ -20,7 +20,7 @@ export class MiApp {
})
public userId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'SET NULL',
nullable: true,
})
diff --git a/packages/backend/src/models/AuthSession.ts b/packages/backend/src/models/AuthSession.ts
index 03050ba955..a7273e63bf 100644
--- a/packages/backend/src/models/AuthSession.ts
+++ b/packages/backend/src/models/AuthSession.ts
@@ -25,7 +25,7 @@ export class MiAuthSession {
})
public userId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
nullable: true,
})
@@ -35,7 +35,7 @@ export class MiAuthSession {
@Column(id())
public appId: MiApp['id'];
- @ManyToOne(type => MiApp, {
+ @ManyToOne(() => MiApp, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Blocking.ts b/packages/backend/src/models/Blocking.ts
index 34a6efe5a6..49b584f509 100644
--- a/packages/backend/src/models/Blocking.ts
+++ b/packages/backend/src/models/Blocking.ts
@@ -20,7 +20,7 @@ export class MiBlocking {
})
public blockeeId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -33,7 +33,7 @@ export class MiBlocking {
})
public blockerId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/BubbleGameRecord.ts b/packages/backend/src/models/BubbleGameRecord.ts
index 686e39c118..5dd7009fc6 100644
--- a/packages/backend/src/models/BubbleGameRecord.ts
+++ b/packages/backend/src/models/BubbleGameRecord.ts
@@ -18,7 +18,7 @@ export class MiBubbleGameRecord {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Channel.ts b/packages/backend/src/models/Channel.ts
index f5e9b17e3e..5a5b914eb1 100644
--- a/packages/backend/src/models/Channel.ts
+++ b/packages/backend/src/models/Channel.ts
@@ -27,7 +27,7 @@ export class MiChannel {
})
public userId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'SET NULL',
})
@JoinColumn()
@@ -52,7 +52,7 @@ export class MiChannel {
})
public bannerId: MiDriveFile['id'] | null;
- @ManyToOne(type => MiDriveFile, {
+ @ManyToOne(() => MiDriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChannelFavorite.ts b/packages/backend/src/models/ChannelFavorite.ts
index 167f41cf16..4f49468598 100644
--- a/packages/backend/src/models/ChannelFavorite.ts
+++ b/packages/backend/src/models/ChannelFavorite.ts
@@ -20,7 +20,7 @@ export class MiChannelFavorite {
})
public channelId: MiChannel['id'];
- @ManyToOne(type => MiChannel, {
+ @ManyToOne(() => MiChannel, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -32,7 +32,7 @@ export class MiChannelFavorite {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChannelFollowing.ts b/packages/backend/src/models/ChannelFollowing.ts
index c7afdd05b0..7597e704a8 100644
--- a/packages/backend/src/models/ChannelFollowing.ts
+++ b/packages/backend/src/models/ChannelFollowing.ts
@@ -21,7 +21,7 @@ export class MiChannelFollowing {
})
public followeeId: MiChannel['id'];
- @ManyToOne(type => MiChannel, {
+ @ManyToOne(() => MiChannel, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -34,7 +34,7 @@ export class MiChannelFollowing {
})
public followerId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChannelMuting.ts b/packages/backend/src/models/ChannelMuting.ts
index 11ac7e5cef..b7054c9c5f 100644
--- a/packages/backend/src/models/ChannelMuting.ts
+++ b/packages/backend/src/models/ChannelMuting.ts
@@ -20,7 +20,7 @@ export class MiChannelMuting {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -32,7 +32,7 @@ export class MiChannelMuting {
})
public channelId: MiChannel['id'];
- @ManyToOne(type => MiChannel, {
+ @ManyToOne(() => MiChannel, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChatApproval.ts b/packages/backend/src/models/ChatApproval.ts
index 55c9f07e9a..bd2509b67f 100644
--- a/packages/backend/src/models/ChatApproval.ts
+++ b/packages/backend/src/models/ChatApproval.ts
@@ -19,7 +19,7 @@ export class MiChatApproval {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -31,7 +31,7 @@ export class MiChatApproval {
})
public otherId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChatMessage.ts b/packages/backend/src/models/ChatMessage.ts
index 3d2b64268e..530ef9b842 100644
--- a/packages/backend/src/models/ChatMessage.ts
+++ b/packages/backend/src/models/ChatMessage.ts
@@ -20,7 +20,7 @@ export class MiChatMessage {
})
public fromUserId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -32,7 +32,7 @@ export class MiChatMessage {
})
public toUserId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -44,7 +44,7 @@ export class MiChatMessage {
})
public toRoomId: MiChatRoom['id'] | null;
- @ManyToOne(type => MiChatRoom, {
+ @ManyToOne(() => MiChatRoom, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -72,7 +72,7 @@ export class MiChatMessage {
})
public fileId: MiDriveFile['id'] | null;
- @ManyToOne(type => MiDriveFile, {
+ @ManyToOne(() => MiDriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChatRoom.ts b/packages/backend/src/models/ChatRoom.ts
index ad2a910b78..c148b16af8 100644
--- a/packages/backend/src/models/ChatRoom.ts
+++ b/packages/backend/src/models/ChatRoom.ts
@@ -23,7 +23,7 @@ export class MiChatRoom {
})
public ownerId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChatRoomInvitation.ts b/packages/backend/src/models/ChatRoomInvitation.ts
index 36ce12bc92..5827d0401d 100644
--- a/packages/backend/src/models/ChatRoomInvitation.ts
+++ b/packages/backend/src/models/ChatRoomInvitation.ts
@@ -20,7 +20,7 @@ export class MiChatRoomInvitation {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -32,7 +32,7 @@ export class MiChatRoomInvitation {
})
public roomId: MiChatRoom['id'];
- @ManyToOne(type => MiChatRoom, {
+ @ManyToOne(() => MiChatRoom, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ChatRoomMembership.ts b/packages/backend/src/models/ChatRoomMembership.ts
index 3cb5524859..d59b4426df 100644
--- a/packages/backend/src/models/ChatRoomMembership.ts
+++ b/packages/backend/src/models/ChatRoomMembership.ts
@@ -20,7 +20,7 @@ export class MiChatRoomMembership {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -32,7 +32,7 @@ export class MiChatRoomMembership {
})
public roomId: MiChatRoom['id'];
- @ManyToOne(type => MiChatRoom, {
+ @ManyToOne(() => MiChatRoom, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Clip.ts b/packages/backend/src/models/Clip.ts
index 6295a329fb..ddd0298f44 100644
--- a/packages/backend/src/models/Clip.ts
+++ b/packages/backend/src/models/Clip.ts
@@ -25,7 +25,7 @@ export class MiClip {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ClipFavorite.ts b/packages/backend/src/models/ClipFavorite.ts
index 40bdb9f4aa..2d46fd0f0e 100644
--- a/packages/backend/src/models/ClipFavorite.ts
+++ b/packages/backend/src/models/ClipFavorite.ts
@@ -18,7 +18,7 @@ export class MiClipFavorite {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiClipFavorite {
@Column(id())
public clipId: MiClip['id'];
- @ManyToOne(type => MiClip, {
+ @ManyToOne(() => MiClip, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ClipNote.ts b/packages/backend/src/models/ClipNote.ts
index 6e1d2bec4c..23df66c4e0 100644
--- a/packages/backend/src/models/ClipNote.ts
+++ b/packages/backend/src/models/ClipNote.ts
@@ -21,7 +21,7 @@ export class MiClipNote {
})
public noteId: MiNote['id'];
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -34,7 +34,7 @@ export class MiClipNote {
})
public clipId: MiClip['id'];
- @ManyToOne(type => MiClip, {
+ @ManyToOne(() => MiClip, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/DriveFile.ts b/packages/backend/src/models/DriveFile.ts
index 7b03e3e494..79189b10eb 100644
--- a/packages/backend/src/models/DriveFile.ts
+++ b/packages/backend/src/models/DriveFile.ts
@@ -22,7 +22,7 @@ export class MiDriveFile {
})
public userId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'SET NULL',
})
@JoinColumn()
@@ -142,7 +142,7 @@ export class MiDriveFile {
})
public folderId: MiDriveFolder['id'] | null;
- @ManyToOne(type => MiDriveFolder, {
+ @ManyToOne(() => MiDriveFolder, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/DriveFolder.ts b/packages/backend/src/models/DriveFolder.ts
index 07046d6e11..7e34c07f46 100644
--- a/packages/backend/src/models/DriveFolder.ts
+++ b/packages/backend/src/models/DriveFolder.ts
@@ -26,7 +26,7 @@ export class MiDriveFolder {
})
public userId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -40,7 +40,7 @@ export class MiDriveFolder {
})
public parentId: MiDriveFolder['id'] | null;
- @ManyToOne(type => MiDriveFolder, {
+ @ManyToOne(() => MiDriveFolder, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Flash.ts b/packages/backend/src/models/Flash.ts
index 5db7dca992..ed677a9de3 100644
--- a/packages/backend/src/models/Flash.ts
+++ b/packages/backend/src/models/Flash.ts
@@ -38,7 +38,7 @@ export class MiFlash {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/FlashLike.ts b/packages/backend/src/models/FlashLike.ts
index a9fb48123e..0d99c2a9ae 100644
--- a/packages/backend/src/models/FlashLike.ts
+++ b/packages/backend/src/models/FlashLike.ts
@@ -18,7 +18,7 @@ export class MiFlashLike {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiFlashLike {
@Column(id())
public flashId: MiFlash['id'];
- @ManyToOne(type => MiFlash, {
+ @ManyToOne(() => MiFlash, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/FollowRequest.ts b/packages/backend/src/models/FollowRequest.ts
index 3ff5e7a478..468829b7e8 100644
--- a/packages/backend/src/models/FollowRequest.ts
+++ b/packages/backend/src/models/FollowRequest.ts
@@ -20,7 +20,7 @@ export class MiFollowRequest {
})
public followeeId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -33,7 +33,7 @@ export class MiFollowRequest {
})
public followerId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts
index 62cbc29f26..fe62166287 100644
--- a/packages/backend/src/models/Following.ts
+++ b/packages/backend/src/models/Following.ts
@@ -21,7 +21,7 @@ export class MiFollowing {
})
public followeeId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -34,7 +34,7 @@ export class MiFollowing {
})
public followerId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/GalleryLike.ts b/packages/backend/src/models/GalleryLike.ts
index ed0963122d..787b38e46d 100644
--- a/packages/backend/src/models/GalleryLike.ts
+++ b/packages/backend/src/models/GalleryLike.ts
@@ -18,7 +18,7 @@ export class MiGalleryLike {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiGalleryLike {
@Column(id())
public postId: MiGalleryPost['id'];
- @ManyToOne(type => MiGalleryPost, {
+ @ManyToOne(() => MiGalleryPost, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/GalleryPost.ts b/packages/backend/src/models/GalleryPost.ts
index 04d8823e37..f66956628b 100644
--- a/packages/backend/src/models/GalleryPost.ts
+++ b/packages/backend/src/models/GalleryPost.ts
@@ -36,7 +36,7 @@ export class MiGalleryPost {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 205c9eeb89..620853450c 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -21,7 +21,7 @@ export class MiMeta {
})
public rootUserId: MiUser['id'] | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'SET NULL',
nullable: true,
})
@@ -725,7 +725,11 @@ export class MiMeta {
@Column('jsonb', {
default: { },
})
- public clientOptions: Record<string, any>;
+ public clientOptions: {
+ entrancePageStyle: 'classic' | 'simple';
+ showTimelineForVisitor: boolean;
+ showActivitiesForVisitor: boolean;
+ };
}
export type SoftwareSuspension = {
diff --git a/packages/backend/src/models/ModerationLog.ts b/packages/backend/src/models/ModerationLog.ts
index edde315fdf..c22114a36d 100644
--- a/packages/backend/src/models/ModerationLog.ts
+++ b/packages/backend/src/models/ModerationLog.ts
@@ -16,7 +16,7 @@ export class MiModerationLog {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Muting.ts b/packages/backend/src/models/Muting.ts
index e1240b9c4e..9406b97a62 100644
--- a/packages/backend/src/models/Muting.ts
+++ b/packages/backend/src/models/Muting.ts
@@ -26,7 +26,7 @@ export class MiMuting {
})
public muteeId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -39,7 +39,7 @@ export class MiMuting {
})
public muterId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts
index 23e5960b60..089fe8f188 100644
--- a/packages/backend/src/models/Note.ts
+++ b/packages/backend/src/models/Note.ts
@@ -35,7 +35,7 @@ export class MiNote {
})
public replyId: MiNote['id'] | null;
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
createForeignKeyConstraints: false,
})
@JoinColumn()
@@ -49,7 +49,7 @@ export class MiNote {
})
public renoteId: MiNote['id'] | null;
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
createForeignKeyConstraints: false,
})
@JoinColumn()
@@ -83,7 +83,7 @@ export class MiNote {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -208,7 +208,7 @@ export class MiNote {
})
public channelId: MiChannel['id'] | null;
- @ManyToOne(type => MiChannel, {
+ @ManyToOne(() => MiChannel, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/NoteDraft.ts b/packages/backend/src/models/NoteDraft.ts
index f078e8c21b..5bfd9699fe 100644
--- a/packages/backend/src/models/NoteDraft.ts
+++ b/packages/backend/src/models/NoteDraft.ts
@@ -27,7 +27,7 @@ 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, {
+ @ManyToOne(() => MiNote, {
createForeignKeyConstraints: false,
})
@JoinColumn()
@@ -42,7 +42,7 @@ 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, {
+ @ManyToOne(() => MiNote, {
createForeignKeyConstraints: false,
})
@JoinColumn()
@@ -66,7 +66,7 @@ export class MiNoteDraft {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -120,7 +120,7 @@ export class MiNoteDraft {
// 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, {
+ @ManyToOne(() => MiChannel, {
createForeignKeyConstraints: false,
})
@JoinColumn()
diff --git a/packages/backend/src/models/NoteFavorite.ts b/packages/backend/src/models/NoteFavorite.ts
index cf76c767b0..0e498eb70d 100644
--- a/packages/backend/src/models/NoteFavorite.ts
+++ b/packages/backend/src/models/NoteFavorite.ts
@@ -18,7 +18,7 @@ export class MiNoteFavorite {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiNoteFavorite {
@Column(id())
public noteId: MiNote['id'];
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/NoteReaction.ts b/packages/backend/src/models/NoteReaction.ts
index 42dfcaa9ad..98263081ab 100644
--- a/packages/backend/src/models/NoteReaction.ts
+++ b/packages/backend/src/models/NoteReaction.ts
@@ -18,7 +18,7 @@ export class MiNoteReaction {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -28,7 +28,7 @@ export class MiNoteReaction {
@Column(id())
public noteId: MiNote['id'];
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/NoteThreadMuting.ts b/packages/backend/src/models/NoteThreadMuting.ts
index e7bd39f348..32bb829c0b 100644
--- a/packages/backend/src/models/NoteThreadMuting.ts
+++ b/packages/backend/src/models/NoteThreadMuting.ts
@@ -19,7 +19,7 @@ export class MiNoteThreadMuting {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Page.ts b/packages/backend/src/models/Page.ts
index d46f6e9d16..8811200801 100644
--- a/packages/backend/src/models/Page.ts
+++ b/packages/backend/src/models/Page.ts
@@ -56,7 +56,7 @@ export class MiPage {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -68,7 +68,7 @@ export class MiPage {
})
public eyeCatchingImageId: MiDriveFile['id'] | null;
- @ManyToOne(type => MiDriveFile, {
+ @ManyToOne(() => MiDriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/PageLike.ts b/packages/backend/src/models/PageLike.ts
index 05ca22cf2c..cf3025ae1c 100644
--- a/packages/backend/src/models/PageLike.ts
+++ b/packages/backend/src/models/PageLike.ts
@@ -18,7 +18,7 @@ export class MiPageLike {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiPageLike {
@Column(id())
public pageId: MiPage['id'];
- @ManyToOne(type => MiPage, {
+ @ManyToOne(() => MiPage, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/PasswordResetRequest.ts b/packages/backend/src/models/PasswordResetRequest.ts
index fdaf21056b..3379b540ee 100644
--- a/packages/backend/src/models/PasswordResetRequest.ts
+++ b/packages/backend/src/models/PasswordResetRequest.ts
@@ -24,7 +24,7 @@ export class MiPasswordResetRequest {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Poll.ts b/packages/backend/src/models/Poll.ts
index ca985c8b24..d82e29fb85 100644
--- a/packages/backend/src/models/Poll.ts
+++ b/packages/backend/src/models/Poll.ts
@@ -15,7 +15,7 @@ export class MiPoll {
@PrimaryColumn(id())
public noteId: MiNote['id'];
- @OneToOne(type => MiNote, {
+ @OneToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/PollVote.ts b/packages/backend/src/models/PollVote.ts
index b5c780293c..600ca8ea41 100644
--- a/packages/backend/src/models/PollVote.ts
+++ b/packages/backend/src/models/PollVote.ts
@@ -18,7 +18,7 @@ export class MiPollVote {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -28,7 +28,7 @@ export class MiPollVote {
@Column(id())
public noteId: MiNote['id'];
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/PromoNote.ts b/packages/backend/src/models/PromoNote.ts
index ae27adec9e..871f7471fc 100644
--- a/packages/backend/src/models/PromoNote.ts
+++ b/packages/backend/src/models/PromoNote.ts
@@ -13,7 +13,7 @@ export class MiPromoNote {
@PrimaryColumn(id())
public noteId: MiNote['id'];
- @OneToOne(type => MiNote, {
+ @OneToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/PromoRead.ts b/packages/backend/src/models/PromoRead.ts
index b2a698cc7b..15a3573ef3 100644
--- a/packages/backend/src/models/PromoRead.ts
+++ b/packages/backend/src/models/PromoRead.ts
@@ -18,7 +18,7 @@ export class MiPromoRead {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiPromoRead {
@Column(id())
public noteId: MiNote['id'];
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/RegistrationTicket.ts b/packages/backend/src/models/RegistrationTicket.ts
index 0a4e4b9189..07216599d3 100644
--- a/packages/backend/src/models/RegistrationTicket.ts
+++ b/packages/backend/src/models/RegistrationTicket.ts
@@ -23,7 +23,7 @@ export class MiRegistrationTicket {
})
public expiresAt: Date | null;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -36,7 +36,7 @@ export class MiRegistrationTicket {
})
public createdById: MiUser['id'] | null;
- @OneToOne(type => MiUser, {
+ @OneToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/RegistryItem.ts b/packages/backend/src/models/RegistryItem.ts
index 335e8b9eab..869980bbff 100644
--- a/packages/backend/src/models/RegistryItem.ts
+++ b/packages/backend/src/models/RegistryItem.ts
@@ -25,7 +25,7 @@ export class MiRegistryItem {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/RenoteMuting.ts b/packages/backend/src/models/RenoteMuting.ts
index 448a0b7663..b760a09c53 100644
--- a/packages/backend/src/models/RenoteMuting.ts
+++ b/packages/backend/src/models/RenoteMuting.ts
@@ -20,7 +20,7 @@ export class MiRenoteMuting {
})
public muteeId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -33,7 +33,7 @@ export class MiRenoteMuting {
})
public muterId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/ReversiGame.ts b/packages/backend/src/models/ReversiGame.ts
index 6b29a0ce8c..fbbf24792f 100644
--- a/packages/backend/src/models/ReversiGame.ts
+++ b/packages/backend/src/models/ReversiGame.ts
@@ -27,7 +27,7 @@ export class MiReversiGame {
@Column(id())
public user1Id: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -36,7 +36,7 @@ export class MiReversiGame {
@Column(id())
public user2Id: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/RoleAssignment.ts b/packages/backend/src/models/RoleAssignment.ts
index 37755d631b..cb96377f66 100644
--- a/packages/backend/src/models/RoleAssignment.ts
+++ b/packages/backend/src/models/RoleAssignment.ts
@@ -21,7 +21,7 @@ export class MiRoleAssignment {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -34,7 +34,7 @@ export class MiRoleAssignment {
})
public roleId: MiRole['id'];
- @ManyToOne(type => MiRole, {
+ @ManyToOne(() => MiRole, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Signin.ts b/packages/backend/src/models/Signin.ts
index f8ff9c57d7..59cbad735d 100644
--- a/packages/backend/src/models/Signin.ts
+++ b/packages/backend/src/models/Signin.ts
@@ -16,7 +16,7 @@ export class MiSignin {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/SwSubscription.ts b/packages/backend/src/models/SwSubscription.ts
index 0c531132b3..a95aede44f 100644
--- a/packages/backend/src/models/SwSubscription.ts
+++ b/packages/backend/src/models/SwSubscription.ts
@@ -16,7 +16,7 @@ export class MiSwSubscription {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/SystemAccount.ts b/packages/backend/src/models/SystemAccount.ts
index f32880b81d..2a48e62ed1 100644
--- a/packages/backend/src/models/SystemAccount.ts
+++ b/packages/backend/src/models/SystemAccount.ts
@@ -18,7 +18,7 @@ export class MiSystemAccount {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts
index a6e9edcf5f..084dd35485 100644
--- a/packages/backend/src/models/User.ts
+++ b/packages/backend/src/models/User.ts
@@ -99,7 +99,7 @@ export class MiUser {
})
public avatarId: MiDriveFile['id'] | null;
- @OneToOne(type => MiDriveFile, {
+ @OneToOne(() => MiDriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()
@@ -112,7 +112,7 @@ export class MiUser {
})
public bannerId: MiDriveFile['id'] | null;
- @OneToOne(type => MiDriveFile, {
+ @OneToOne(() => MiDriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserKeypair.ts b/packages/backend/src/models/UserKeypair.ts
index f5252d126c..894739c84c 100644
--- a/packages/backend/src/models/UserKeypair.ts
+++ b/packages/backend/src/models/UserKeypair.ts
@@ -12,7 +12,7 @@ export class MiUserKeypair {
@PrimaryColumn(id())
public userId: MiUser['id'];
- @OneToOne(type => MiUser, {
+ @OneToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserList.ts b/packages/backend/src/models/UserList.ts
index 5fb991a87d..05fd833b6f 100644
--- a/packages/backend/src/models/UserList.ts
+++ b/packages/backend/src/models/UserList.ts
@@ -25,7 +25,7 @@ export class MiUserList {
})
public isPublic: boolean;
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserListFavorite.ts b/packages/backend/src/models/UserListFavorite.ts
index 80b2d61eb7..67ab92d98c 100644
--- a/packages/backend/src/models/UserListFavorite.ts
+++ b/packages/backend/src/models/UserListFavorite.ts
@@ -18,7 +18,7 @@ export class MiUserListFavorite {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiUserListFavorite {
@Column(id())
public userListId: MiUserList['id'];
- @ManyToOne(type => MiUserList, {
+ @ManyToOne(() => MiUserList, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserListMembership.ts b/packages/backend/src/models/UserListMembership.ts
index af659d071d..1a2b3fffc1 100644
--- a/packages/backend/src/models/UserListMembership.ts
+++ b/packages/backend/src/models/UserListMembership.ts
@@ -21,7 +21,7 @@ export class MiUserListMembership {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -34,7 +34,7 @@ export class MiUserListMembership {
})
public userListId: MiUserList['id'];
- @ManyToOne(type => MiUserList, {
+ @ManyToOne(() => MiUserList, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserMemo.ts b/packages/backend/src/models/UserMemo.ts
index 29e28d290a..facc8c6b1c 100644
--- a/packages/backend/src/models/UserMemo.ts
+++ b/packages/backend/src/models/UserMemo.ts
@@ -20,7 +20,7 @@ export class MiUserMemo {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -33,7 +33,7 @@ export class MiUserMemo {
})
public targetUserId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserNotePining.ts b/packages/backend/src/models/UserNotePining.ts
index 92c5cd55d0..950da2ad22 100644
--- a/packages/backend/src/models/UserNotePining.ts
+++ b/packages/backend/src/models/UserNotePining.ts
@@ -18,7 +18,7 @@ export class MiUserNotePining {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -27,7 +27,7 @@ export class MiUserNotePining {
@Column(id())
public noteId: MiNote['id'];
- @ManyToOne(type => MiNote, {
+ @ManyToOne(() => MiNote, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts
index 501b539210..b05bf14ef9 100644
--- a/packages/backend/src/models/UserProfile.ts
+++ b/packages/backend/src/models/UserProfile.ts
@@ -17,7 +17,7 @@ export class MiUserProfile {
@PrimaryColumn(id())
public userId: MiUser['id'];
- @OneToOne(type => MiUser, {
+ @OneToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
@@ -215,7 +215,7 @@ export class MiUserProfile {
})
public pinnedPageId: MiPage['id'] | null;
- @OneToOne(type => MiPage, {
+ @OneToOne(() => MiPage, {
onDelete: 'SET NULL',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserPublickey.ts b/packages/backend/src/models/UserPublickey.ts
index 6bcd785304..8c23d368e9 100644
--- a/packages/backend/src/models/UserPublickey.ts
+++ b/packages/backend/src/models/UserPublickey.ts
@@ -12,7 +12,7 @@ export class MiUserPublickey {
@PrimaryColumn(id())
public userId: MiUser['id'];
- @OneToOne(type => MiUser, {
+ @OneToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/UserSecurityKey.ts b/packages/backend/src/models/UserSecurityKey.ts
index 0babbe1abe..577ec359e4 100644
--- a/packages/backend/src/models/UserSecurityKey.ts
+++ b/packages/backend/src/models/UserSecurityKey.ts
@@ -18,7 +18,7 @@ export class MiUserSecurityKey {
@Column(id())
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/Webhook.ts b/packages/backend/src/models/Webhook.ts
index b4cab4edc8..5f833115cc 100644
--- a/packages/backend/src/models/Webhook.ts
+++ b/packages/backend/src/models/Webhook.ts
@@ -22,7 +22,7 @@ export class MiWebhook {
})
public userId: MiUser['id'];
- @ManyToOne(type => MiUser, {
+ @ManyToOne(() => MiUser, {
onDelete: 'CASCADE',
})
@JoinColumn()
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
index a0e7d490b3..0c3ec141bc 100644
--- a/packages/backend/src/models/json-schema/meta.ts
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -72,8 +72,7 @@ export const packedMetaLiteSchema = {
optional: false, nullable: true,
},
clientOptions: {
- type: 'object',
- optional: false, nullable: false,
+ ref: 'MetaClientOptions',
},
disableRegistration: {
type: 'boolean',
@@ -397,3 +396,23 @@ export const packedMetaDetailedSchema = {
},
],
} as const;
+
+export const packedMetaClientOptionsSchema = {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ entrancePageStyle: {
+ type: 'string',
+ enum: ['classic', 'simple'],
+ optional: false, nullable: false,
+ },
+ showTimelineForVisitor: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ showActivitiesForVisitor: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ },
+} as const;
diff --git a/packages/backend/src/models/json-schema/reversi-game.ts b/packages/backend/src/models/json-schema/reversi-game.ts
index cb37200384..378ae41cb5 100644
--- a/packages/backend/src/models/json-schema/reversi-game.ts
+++ b/packages/backend/src/models/json-schema/reversi-game.ts
@@ -81,6 +81,7 @@ export const packedReversiGameLiteSchema = {
bw: {
type: 'string',
optional: false, nullable: false,
+ enum: ['random', '1', '2'],
},
noIrregularRules: {
type: 'boolean',
@@ -199,6 +200,7 @@ export const packedReversiGameDetailedSchema = {
bw: {
type: 'string',
optional: false, nullable: false,
+ enum: ['random', '1', '2'],
},
noIrregularRules: {
type: 'boolean',
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index b5fd38a7d7..f71ec1d023 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -618,6 +618,9 @@ export const packedMeDetailedOnlySchema = {
achievementEarned: { optional: true, ...notificationRecieveConfig },
app: { optional: true, ...notificationRecieveConfig },
test: { optional: true, ...notificationRecieveConfig },
+ login: { optional: true, ...notificationRecieveConfig },
+ createToken: { optional: true, ...notificationRecieveConfig },
+ exportCompleted: { optional: true, ...notificationRecieveConfig },
},
},
emailNotificationTypes: {
diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts
index e237cd4975..53ecd2d180 100644
--- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts
@@ -123,8 +123,8 @@ export class ExportCustomEmojisProcessorService {
metaStream.end();
// Create archive
- await new Promise<void>(async (resolve) => {
- const [archivePath, archiveCleanup] = await createTemp();
+ const [archivePath, archiveCleanup] = await createTemp();
+ await new Promise<void>((resolve) => {
const archiveStream = fs.createWriteStream(archivePath);
const archive = archiver('zip', {
zlib: { level: 0 },
diff --git a/packages/backend/src/queue/processors/PostScheduledNoteProcessorService.ts b/packages/backend/src/queue/processors/PostScheduledNoteProcessorService.ts
index d0eaeee090..719a09980c 100644
--- a/packages/backend/src/queue/processors/PostScheduledNoteProcessorService.ts
+++ b/packages/backend/src/queue/processors/PostScheduledNoteProcessorService.ts
@@ -63,7 +63,7 @@ export class PostScheduledNoteProcessorService {
this.notificationService.createNotification(draft.userId, 'scheduledNotePosted', {
noteId: note.id,
});
- } catch (err) {
+ } catch (_) {
this.notificationService.createNotification(draft.userId, 'scheduledNotePostFailed', {
noteDraftId: draft.id,
});
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index a5fb5b82e3..54ffeecc6b 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -116,7 +116,7 @@ export class ActivityPubServerService {
try {
signature = httpSignature.parseRequest(request.raw, { 'headers': ['(request-target)', 'host', 'date'], authorizationHeaderName: 'signature' });
- } catch (e) {
+ } catch (_) {
reply.code(401);
return;
}
diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index 772c37094c..f5034d0733 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -7,27 +7,22 @@ import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { Inject, Injectable } from '@nestjs/common';
-import rename from 'rename';
-import sharp from 'sharp';
-import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
import type { Config } from '@/config.js';
-import type { MiDriveFile, DriveFilesRepository } from '@/models/_.js';
+import type { DriveFilesRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
-import { createTemp } from '@/misc/create-temp.js';
-import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
import { StatusError } from '@/misc/status-error.js';
import type Logger from '@/logger.js';
import { DownloadService } from '@/core/DownloadService.js';
-import { IImageStreamable, ImageProcessingService, webpDefault } from '@/core/ImageProcessingService.js';
-import { VideoProcessingService } from '@/core/VideoProcessingService.js';
import { InternalStorageService } from '@/core/InternalStorageService.js';
-import { contentDisposition } from '@/misc/content-disposition.js';
import { FileInfoService } from '@/core/FileInfoService.js';
+import { ImageProcessingService } from '@/core/ImageProcessingService.js';
+import { VideoProcessingService } from '@/core/VideoProcessingService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { bindThis } from '@/decorators.js';
-import { isMimeImage } from '@/misc/is-mime-image.js';
-import { correctFilename } from '@/misc/correct-filename.js';
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
+import { FileServerDriveHandler } from './file/FileServerDriveHandler.js';
+import { FileServerFileResolver } from './file/FileServerFileResolver.js';
+import { FileServerProxyHandler } from './file/FileServerProxyHandler.js';
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify';
const _filename = fileURLToPath(import.meta.url);
@@ -38,6 +33,9 @@ const assets = `${_dirname}/../../server/file/assets/`;
@Injectable()
export class FileServerService {
private logger: Logger;
+ private driveHandler: FileServerDriveHandler;
+ private proxyHandler: FileServerProxyHandler;
+ private fileResolver: FileServerFileResolver;
constructor(
@Inject(DI.config)
@@ -54,6 +52,24 @@ export class FileServerService {
private loggerService: LoggerService,
) {
this.logger = this.loggerService.getLogger('server', 'gray');
+ this.fileResolver = new FileServerFileResolver(
+ this.driveFilesRepository,
+ this.fileInfoService,
+ this.downloadService,
+ this.internalStorageService,
+ );
+ this.driveHandler = new FileServerDriveHandler(
+ this.config,
+ this.fileResolver,
+ assets,
+ this.videoProcessingService,
+ );
+ this.proxyHandler = new FileServerProxyHandler(
+ this.config,
+ this.fileResolver,
+ assets,
+ this.imageProcessingService,
+ );
//this.createServer = this.createServer.bind(this);
}
@@ -78,7 +94,7 @@ export class FileServerService {
});
fastify.get<{ Params: { key: string; } }>('/files/:key', async (request, reply) => {
- return await this.sendDriveFile(request, reply)
+ return await this.driveHandler.handle(request, reply)
.catch(err => this.errorHandler(request, reply, err));
});
fastify.get<{ Params: { key: string; } }>('/files/:key/*', async (request, reply) => {
@@ -91,7 +107,7 @@ export class FileServerService {
Params: { url: string; };
Querystring: { url?: string; };
}>('/proxy/:url*', async (request, reply) => {
- return await this.proxyHandler(request, reply)
+ return await this.proxyHandler.handle(request, reply)
.catch(err => this.errorHandler(request, reply, err));
});
@@ -116,462 +132,4 @@ export class FileServerService {
reply.code(500);
return;
}
-
- @bindThis
- private async sendDriveFile(request: FastifyRequest<{ Params: { key: string; } }>, reply: FastifyReply) {
- const key = request.params.key;
- const file = await this.getFileFromKey(key).then();
-
- if (file === '404') {
- reply.code(404);
- reply.header('Cache-Control', 'max-age=86400');
- return reply.sendFile('/dummy.png', assets);
- }
-
- if (file === '204') {
- reply.code(204);
- reply.header('Cache-Control', 'max-age=86400');
- return;
- }
-
- try {
- if (file.state === 'remote') {
- let image: IImageStreamable | null = null;
-
- if (file.fileRole === 'thumbnail') {
- if (isMimeImage(file.mime, 'sharp-convertible-image-with-bmp')) {
- reply.header('Cache-Control', 'max-age=31536000, immutable');
-
- const url = new URL(`${this.config.mediaProxy}/static.webp`);
- url.searchParams.set('url', file.url);
- url.searchParams.set('static', '1');
-
- file.cleanup();
- return await reply.redirect(url.toString(), 301);
- } else if (file.mime.startsWith('video/')) {
- const externalThumbnail = this.videoProcessingService.getExternalVideoThumbnailUrl(file.url);
- if (externalThumbnail) {
- file.cleanup();
- return await reply.redirect(externalThumbnail, 301);
- }
-
- image = await this.videoProcessingService.generateVideoThumbnail(file.path);
- }
- }
-
- if (file.fileRole === 'webpublic') {
- if (['image/svg+xml'].includes(file.mime)) {
- reply.header('Cache-Control', 'max-age=31536000, immutable');
-
- const url = new URL(`${this.config.mediaProxy}/svg.webp`);
- url.searchParams.set('url', file.url);
-
- file.cleanup();
- return await reply.redirect(url.toString(), 301);
- }
- }
-
- if (!image) {
- if (request.headers.range && file.file.size > 0) {
- const range = request.headers.range as string;
- const parts = range.replace(/bytes=/, '').split('-');
- const start = parseInt(parts[0], 10);
- let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
- if (end > file.file.size) {
- end = file.file.size - 1;
- }
- const chunksize = end - start + 1;
-
- image = {
- data: fs.createReadStream(file.path, {
- start,
- end,
- }),
- ext: file.ext,
- type: file.mime,
- };
-
- reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
- reply.header('Accept-Ranges', 'bytes');
- reply.header('Content-Length', chunksize);
- reply.code(206);
- } else {
- image = {
- data: fs.createReadStream(file.path),
- ext: file.ext,
- type: file.mime,
- };
- }
- }
-
- if ('pipe' in image.data && typeof image.data.pipe === 'function') {
- // image.dataがstreamなら、stream終了後にcleanup
- image.data.on('end', file.cleanup);
- image.data.on('close', file.cleanup);
- } else {
- // image.dataがstreamでないなら直ちにcleanup
- file.cleanup();
- }
-
- reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream');
- reply.header('Content-Length', file.file.size);
- reply.header('Cache-Control', 'max-age=31536000, immutable');
- reply.header('Content-Disposition',
- contentDisposition(
- 'inline',
- correctFilename(file.filename, image.ext),
- ),
- );
- return image.data;
- }
-
- if (file.fileRole !== 'original') {
- const filename = rename(file.filename, {
- suffix: file.fileRole === 'thumbnail' ? '-thumb' : '-web',
- extname: file.ext ? `.${file.ext}` : '.unknown',
- }).toString();
-
- reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.mime) ? file.mime : 'application/octet-stream');
- reply.header('Cache-Control', 'max-age=31536000, immutable');
- reply.header('Content-Disposition', contentDisposition('inline', filename));
-
- if (request.headers.range && file.file.size > 0) {
- const range = request.headers.range as string;
- const parts = range.replace(/bytes=/, '').split('-');
- const start = parseInt(parts[0], 10);
- let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
- if (end > file.file.size) {
- end = file.file.size - 1;
- }
- const chunksize = end - start + 1;
- const fileStream = fs.createReadStream(file.path, {
- start,
- end,
- });
- reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
- reply.header('Accept-Ranges', 'bytes');
- reply.header('Content-Length', chunksize);
- reply.code(206);
- return fileStream;
- }
-
- return fs.createReadStream(file.path);
- } else {
- reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.file.type) ? file.file.type : 'application/octet-stream');
- reply.header('Content-Length', file.file.size);
- reply.header('Cache-Control', 'max-age=31536000, immutable');
- reply.header('Content-Disposition', contentDisposition('inline', file.filename));
-
- if (request.headers.range && file.file.size > 0) {
- const range = request.headers.range as string;
- const parts = range.replace(/bytes=/, '').split('-');
- const start = parseInt(parts[0], 10);
- let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
- if (end > file.file.size) {
- end = file.file.size - 1;
- }
- const chunksize = end - start + 1;
- const fileStream = fs.createReadStream(file.path, {
- start,
- end,
- });
- reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
- reply.header('Accept-Ranges', 'bytes');
- reply.header('Content-Length', chunksize);
- reply.code(206);
- return fileStream;
- }
-
- return fs.createReadStream(file.path);
- }
- } catch (e) {
- if ('cleanup' in file) file.cleanup();
- throw e;
- }
- }
-
- @bindThis
- private async proxyHandler(request: FastifyRequest<{ Params: { url: string; }; Querystring: { url?: string; }; }>, reply: FastifyReply) {
- const url = 'url' in request.query ? request.query.url : 'https://' + request.params.url;
-
- if (typeof url !== 'string') {
- reply.code(400);
- return;
- }
-
- // アバタークロップなど、どうしてもオリジンである必要がある場合
- const mustOrigin = 'origin' in request.query;
-
- if (this.config.externalMediaProxyEnabled && !mustOrigin) {
- // 外部のメディアプロキシが有効なら、そちらにリダイレクト
-
- reply.header('Cache-Control', 'public, max-age=259200'); // 3 days
-
- const url = new URL(`${this.config.mediaProxy}/${request.params.url || ''}`);
-
- for (const [key, value] of Object.entries(request.query)) {
- url.searchParams.append(key, value);
- }
-
- return await reply.redirect(
- url.toString(),
- 301,
- );
- }
-
- if (!request.headers['user-agent']) {
- throw new StatusError('User-Agent is required', 400, 'User-Agent is required');
- } else if (request.headers['user-agent'].toLowerCase().indexOf('misskey/') !== -1) {
- throw new StatusError('Refusing to proxy a request from another proxy', 403, 'Proxy is recursive');
- }
-
- // Create temp file
- const file = await this.getStreamAndTypeFromUrl(url);
- if (file === '404') {
- reply.code(404);
- reply.header('Cache-Control', 'max-age=86400');
- return reply.sendFile('/dummy.png', assets);
- }
-
- if (file === '204') {
- reply.code(204);
- reply.header('Cache-Control', 'max-age=86400');
- return;
- }
-
- try {
- const isConvertibleImage = isMimeImage(file.mime, 'sharp-convertible-image-with-bmp');
- const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image-with-bmp');
-
- if (
- 'emoji' in request.query ||
- 'avatar' in request.query ||
- 'static' in request.query ||
- 'preview' in request.query ||
- 'badge' in request.query
- ) {
- if (!isConvertibleImage) {
- // 画像でないなら404でお茶を濁す
- throw new StatusError('Unexpected mime', 404);
- }
- }
-
- let image: IImageStreamable | null = null;
- if ('emoji' in request.query || 'avatar' in request.query) {
- if (!isAnimationConvertibleImage && !('static' in request.query)) {
- image = {
- data: fs.createReadStream(file.path),
- ext: file.ext,
- type: file.mime,
- };
- } else {
- const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) }))
- .resize({
- height: 'emoji' in request.query ? 128 : 320,
- withoutEnlargement: true,
- })
- .webp(webpDefault);
-
- image = {
- data,
- ext: 'webp',
- type: 'image/webp',
- };
- }
- } else if ('static' in request.query) {
- image = this.imageProcessingService.convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 498, 422);
- } else if ('preview' in request.query) {
- image = this.imageProcessingService.convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 200, 200);
- } else if ('badge' in request.query) {
- const mask = (await sharpBmp(file.path, file.mime))
- .resize(96, 96, {
- fit: 'contain',
- position: 'centre',
- withoutEnlargement: false,
- })
- .greyscale()
- .normalise()
- .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast
- .flatten({ background: '#000' })
- .toColorspace('b-w');
-
- const stats = await mask.clone().stats();
-
- if (stats.entropy < 0.1) {
- // エントロピーがあまりない場合は404にする
- throw new StatusError('Skip to provide badge', 404);
- }
-
- const data = sharp({
- create: { width: 96, height: 96, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } },
- })
- .pipelineColorspace('b-w')
- .boolean(await mask.png().toBuffer(), 'eor');
-
- image = {
- data: await data.png().toBuffer(),
- ext: 'png',
- type: 'image/png',
- };
- } else if (file.mime === 'image/svg+xml') {
- image = this.imageProcessingService.convertToWebpStream(file.path, 2048, 2048);
- } else if (!file.mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(file.mime)) {
- throw new StatusError('Rejected type', 403, 'Rejected type');
- }
-
- if (!image) {
- if (request.headers.range && file.file && file.file.size > 0) {
- const range = request.headers.range as string;
- const parts = range.replace(/bytes=/, '').split('-');
- const start = parseInt(parts[0], 10);
- let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
- if (end > file.file.size) {
- end = file.file.size - 1;
- }
- const chunksize = end - start + 1;
-
- image = {
- data: fs.createReadStream(file.path, {
- start,
- end,
- }),
- ext: file.ext,
- type: file.mime,
- };
-
- reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
- reply.header('Accept-Ranges', 'bytes');
- reply.header('Content-Length', chunksize);
- reply.code(206);
- } else {
- image = {
- data: fs.createReadStream(file.path),
- ext: file.ext,
- type: file.mime,
- };
- }
- }
-
- if ('cleanup' in file) {
- if ('pipe' in image.data && typeof image.data.pipe === 'function') {
- // image.dataがstreamなら、stream終了後にcleanup
- image.data.on('end', file.cleanup);
- image.data.on('close', file.cleanup);
- } else {
- // image.dataがstreamでないなら直ちにcleanup
- file.cleanup();
- }
- }
-
- reply.header('Content-Type', image.type);
- reply.header('Cache-Control', 'max-age=31536000, immutable');
- reply.header('Content-Disposition',
- contentDisposition(
- 'inline',
- correctFilename(file.filename, image.ext),
- ),
- );
- return image.data;
- } catch (e) {
- if ('cleanup' in file) file.cleanup();
- throw e;
- }
- }
-
- @bindThis
- private async getStreamAndTypeFromUrl(url: string): Promise<
- { state: 'remote'; fileRole?: 'thumbnail' | 'webpublic' | 'original'; file?: MiDriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
- | { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; mime: string; ext: string | null; path: string; }
- | '404'
- | '204'
- > {
- if (url.startsWith(`${this.config.url}/files/`)) {
- const key = url.replace(`${this.config.url}/files/`, '').split('/').shift();
- if (!key) throw new StatusError('Invalid File Key', 400, 'Invalid File Key');
-
- return await this.getFileFromKey(key);
- }
-
- return await this.downloadAndDetectTypeFromUrl(url);
- }
-
- @bindThis
- private async downloadAndDetectTypeFromUrl(url: string): Promise<
- { state: 'remote'; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
- > {
- const [path, cleanup] = await createTemp();
- try {
- const { filename } = await this.downloadService.downloadUrl(url, path);
-
- const { mime, ext } = await this.fileInfoService.detectType(path);
-
- return {
- state: 'remote',
- mime, ext,
- path, cleanup,
- filename,
- };
- } catch (e) {
- cleanup();
- throw e;
- }
- }
-
- @bindThis
- private async getFileFromKey(key: string): Promise<
- { state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; url: string; mime: string; ext: string | null; path: string; cleanup: () => void; }
- | { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: MiDriveFile; filename: string; mime: string; ext: string | null; path: string; }
- | '404'
- | '204'
- > {
- // Fetch drive file
- const file = await this.driveFilesRepository.createQueryBuilder('file')
- .where('file.accessKey = :accessKey', { accessKey: key })
- .orWhere('file.thumbnailAccessKey = :thumbnailAccessKey', { thumbnailAccessKey: key })
- .orWhere('file.webpublicAccessKey = :webpublicAccessKey', { webpublicAccessKey: key })
- .getOne();
-
- if (file == null) return '404';
-
- const isThumbnail = file.thumbnailAccessKey === key;
- const isWebpublic = file.webpublicAccessKey === key;
-
- if (!file.storedInternal) {
- if (!(file.isLink && file.uri)) return '204';
- const result = await this.downloadAndDetectTypeFromUrl(file.uri);
- file.size = (await fs.promises.stat(result.path)).size; // DB file.sizeは正確とは限らないので
- return {
- ...result,
- url: file.uri,
- fileRole: isThumbnail ? 'thumbnail' : isWebpublic ? 'webpublic' : 'original',
- file,
- filename: file.name,
- };
- }
-
- const path = this.internalStorageService.resolvePath(key);
-
- if (isThumbnail || isWebpublic) {
- const { mime, ext } = await this.fileInfoService.detectType(path);
- return {
- state: 'stored_internal',
- fileRole: isThumbnail ? 'thumbnail' : 'webpublic',
- file,
- filename: file.name,
- mime, ext,
- path,
- };
- }
-
- return {
- state: 'stored_internal',
- fileRole: 'original',
- file,
- filename: file.name,
- // 古いファイルは修正前のmimeを持っているのでできるだけ修正してあげる
- mime: this.fileInfoService.fixMime(file.type),
- ext: null,
- path,
- };
- }
}
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index 239ef82dec..93c36f5365 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -48,8 +48,6 @@ export class NodeinfoServerService {
@bindThis
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
const nodeinfo2 = async (version: number) => {
- const now = Date.now();
-
const notesChart = await this.notesChart.getChart('hour', 1, null);
const localPosts = notesChart.local.total[0];
diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts
index 111421472d..8259a2a9e4 100644
--- a/packages/backend/src/server/ServerModule.ts
+++ b/packages/backend/src/server/ServerModule.ts
@@ -13,7 +13,6 @@ import { NodeinfoServerService } from './NodeinfoServerService.js';
import { ServerService } from './ServerService.js';
import { WellKnownServerService } from './WellKnownServerService.js';
import { GetterService } from './api/GetterService.js';
-import { ChannelsService } from './api/stream/ChannelsService.js';
import { ActivityPubServerService } from './ActivityPubServerService.js';
import { ApiLoggerService } from './api/ApiLoggerService.js';
import { ApiServerService } from './api/ApiServerService.js';
@@ -31,24 +30,25 @@ import { UrlPreviewService } from './web/UrlPreviewService.js';
import { ClientLoggerService } from './web/ClientLoggerService.js';
import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
-import { MainChannelService } from './api/stream/channels/main.js';
-import { AdminChannelService } from './api/stream/channels/admin.js';
-import { AntennaChannelService } from './api/stream/channels/antenna.js';
-import { ChannelChannelService } from './api/stream/channels/channel.js';
-import { DriveChannelService } from './api/stream/channels/drive.js';
-import { GlobalTimelineChannelService } from './api/stream/channels/global-timeline.js';
-import { HashtagChannelService } from './api/stream/channels/hashtag.js';
-import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js';
-import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js';
-import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js';
-import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js';
-import { ServerStatsChannelService } from './api/stream/channels/server-stats.js';
-import { UserListChannelService } from './api/stream/channels/user-list.js';
-import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js';
-import { ChatUserChannelService } from './api/stream/channels/chat-user.js';
-import { ChatRoomChannelService } from './api/stream/channels/chat-room.js';
-import { ReversiChannelService } from './api/stream/channels/reversi.js';
-import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js';
+import MainStreamConnection from '@/server/api/stream/Connection.js';
+import { MainChannel } from './api/stream/channels/main.js';
+import { AdminChannel } from './api/stream/channels/admin.js';
+import { AntennaChannel } from './api/stream/channels/antenna.js';
+import { ChannelChannel } from './api/stream/channels/channel.js';
+import { DriveChannel } from './api/stream/channels/drive.js';
+import { GlobalTimelineChannel } from './api/stream/channels/global-timeline.js';
+import { HashtagChannel } from './api/stream/channels/hashtag.js';
+import { HomeTimelineChannel } from './api/stream/channels/home-timeline.js';
+import { HybridTimelineChannel } from './api/stream/channels/hybrid-timeline.js';
+import { LocalTimelineChannel } from './api/stream/channels/local-timeline.js';
+import { QueueStatsChannel } from './api/stream/channels/queue-stats.js';
+import { ServerStatsChannel } from './api/stream/channels/server-stats.js';
+import { UserListChannel } from './api/stream/channels/user-list.js';
+import { RoleTimelineChannel } from './api/stream/channels/role-timeline.js';
+import { ChatUserChannel } from './api/stream/channels/chat-user.js';
+import { ChatRoomChannel } from './api/stream/channels/chat-room.js';
+import { ReversiChannel } from './api/stream/channels/reversi.js';
+import { ReversiGameChannel } from './api/stream/channels/reversi-game.js';
import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.js';
@Module({
@@ -69,7 +69,7 @@ import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.j
ServerService,
WellKnownServerService,
GetterService,
- ChannelsService,
+ MainStreamConnection,
ApiCallService,
ApiLoggerService,
ApiServerService,
@@ -80,24 +80,24 @@ import { SigninWithPasskeyApiService } from './api/SigninWithPasskeyApiService.j
SigninService,
SignupApiService,
StreamingApiServerService,
- MainChannelService,
- AdminChannelService,
- AntennaChannelService,
- ChannelChannelService,
- DriveChannelService,
- GlobalTimelineChannelService,
- HashtagChannelService,
- RoleTimelineChannelService,
- ChatUserChannelService,
- ChatRoomChannelService,
- ReversiChannelService,
- ReversiGameChannelService,
- HomeTimelineChannelService,
- HybridTimelineChannelService,
- LocalTimelineChannelService,
- QueueStatsChannelService,
- ServerStatsChannelService,
- UserListChannelService,
+ MainChannel,
+ AdminChannel,
+ AntennaChannel,
+ ChannelChannel,
+ DriveChannel,
+ GlobalTimelineChannel,
+ HashtagChannel,
+ RoleTimelineChannel,
+ ChatUserChannel,
+ ChatRoomChannel,
+ ReversiChannel,
+ ReversiGameChannel,
+ HomeTimelineChannel,
+ HybridTimelineChannel,
+ LocalTimelineChannel,
+ QueueStatsChannel,
+ ServerStatsChannel,
+ UserListChannel,
OpenApiServerService,
OAuth2ProviderService,
],
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index 8bae46d9fb..0ccb3df631 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -426,7 +426,7 @@ export class ApiCallService implements OnApplicationShutdown {
if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') {
try {
data[k] = JSON.parse(data[k]);
- } catch (e) {
+ } catch (_) {
throw new ApiError({
message: 'Invalid param.',
code: 'INVALID_PARAM',
diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts
index 00e8828242..5c9d16a95a 100644
--- a/packages/backend/src/server/api/SigninApiService.ts
+++ b/packages/backend/src/server/api/SigninApiService.ts
@@ -231,7 +231,7 @@ export class SigninApiService {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
return await fail(403, {
id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f',
});
diff --git a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts
index 920f9d0b3a..6feb4c3afa 100644
--- a/packages/backend/src/server/api/SigninWithPasskeyApiService.ts
+++ b/packages/backend/src/server/api/SigninWithPasskeyApiService.ts
@@ -93,7 +93,7 @@ export class SigninWithPasskeyApiService {
// Not more than 1 API call per 250ms and not more than 100 attempts per 30min
// NOTE: 1 Sign-in require 2 API calls
await this.rateLimiterService.limit({ key: 'signin-with-passkey', duration: 60 * 30 * 1000, max: 200, minInterval: 250 }, getIpHash(request.ip));
- } catch (err) {
+ } catch (_) {
reply.code(429);
return {
error: {
diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts
index 53336a087d..b419c51ef1 100644
--- a/packages/backend/src/server/api/SignupApiService.ts
+++ b/packages/backend/src/server/api/SignupApiService.ts
@@ -255,7 +255,7 @@ export class SignupApiService {
throw new FastifyReplyError(400, 'EXPIRED');
}
- const { account, secret } = await this.signupService.signup({
+ const { account } = await this.signupService.signup({
username: pendingUser.username,
passwordHash: pendingUser.password,
});
diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts
index 21f2f0b7e2..8a317bdc4e 100644
--- a/packages/backend/src/server/api/StreamingApiServerService.ts
+++ b/packages/backend/src/server/api/StreamingApiServerService.ts
@@ -8,18 +8,14 @@ import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis';
import * as WebSocket from 'ws';
import { DI } from '@/di-symbols.js';
-import type { UsersRepository, MiAccessToken } from '@/models/_.js';
-import { NotificationService } from '@/core/NotificationService.js';
+import type { MiAccessToken } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
-import { CacheService } from '@/core/CacheService.js';
import { MiLocalUser } from '@/models/User.js';
import { UserService } from '@/core/UserService.js';
-import { ChannelFollowingService } from '@/core/ChannelFollowingService.js';
-import { ChannelMutingService } from '@/core/ChannelMutingService.js';
import { AuthenticateService, AuthenticationError } from './AuthenticateService.js';
-import MainStreamConnection from './stream/Connection.js';
-import { ChannelsService } from './stream/ChannelsService.js';
+import MainStreamConnection, { ConnectionRequest } from './stream/Connection.js';
import type * as http from 'node:http';
+import { ContextIdFactory, ModuleRef } from '@nestjs/core';
@Injectable()
export class StreamingApiServerService {
@@ -31,16 +27,9 @@ export class StreamingApiServerService {
@Inject(DI.redisForSub)
private redisForSub: Redis.Redis,
- @Inject(DI.usersRepository)
- private usersRepository: UsersRepository,
-
- private cacheService: CacheService,
+ private moduleRef: ModuleRef,
private authenticateService: AuthenticateService,
- private channelsService: ChannelsService,
- private notificationService: NotificationService,
private usersService: UserService,
- private channelFollowingService: ChannelFollowingService,
- private channelMutingService: ChannelMutingService,
) {
}
@@ -94,14 +83,12 @@ export class StreamingApiServerService {
return;
}
- const stream = new MainStreamConnection(
- this.channelsService,
- this.notificationService,
- this.cacheService,
- this.channelFollowingService,
- this.channelMutingService,
- user, app,
- );
+ const contextId = ContextIdFactory.create();
+ this.moduleRef.registerRequestByContextId<ConnectionRequest>({
+ user,
+ token: app,
+ }, contextId);
+ const stream = await this.moduleRef.create(MainStreamConnection, contextId);
await stream.init();
@@ -124,7 +111,7 @@ export class StreamingApiServerService {
user: MiLocalUser | null;
app: MiAccessToken | null
}) => {
- const { stream, user, app } = ctx;
+ const { stream, user } = ctx;
const ev = new EventEmitter();
diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts
index 9aecc0f0fd..6679005c3c 100644
--- a/packages/backend/src/server/api/endpoint-list.ts
+++ b/packages/backend/src/server/api/endpoint-list.ts
@@ -391,6 +391,7 @@ export * as 'users/featured-notes' from './endpoints/users/featured-notes.js';
export * as 'users/flashs' from './endpoints/users/flashs.js';
export * as 'users/followers' from './endpoints/users/followers.js';
export * as 'users/following' from './endpoints/users/following.js';
+export * as 'users/get-following-users-by-birthday' from './endpoints/users/get-following-users-by-birthday.js';
export * as 'users/gallery/posts' from './endpoints/users/gallery/posts.js';
export * as 'users/get-frequently-replied-users' from './endpoints/users/get-frequently-replied-users.js';
export * as 'users/lists/create' from './endpoints/users/lists/create.js';
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
index b8bfda73a4..74462b302a 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
@@ -72,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private announcementService: AnnouncementService,
) {
super(meta, paramDef, async (ps, me) => {
- const { raw, packed } = await this.announcementService.create({
+ const { packed } = await this.announcementService.create({
updatedAt: null,
title: ps.title,
text: ps.text,
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
index 804bd5d9b9..aeebceed5a 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
@@ -51,11 +51,13 @@ export const meta = {
},
icon: {
type: 'string',
- optional: false, nullable: true,
+ optional: false, nullable: false,
+ enum: ['info', 'warning', 'error', 'success'],
},
display: {
type: 'string',
optional: false, nullable: false,
+ enum: ['normal', 'banner', 'dialog'],
},
isActive: {
type: 'boolean',
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
index cf03859ce5..d4305e7d7c 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
@@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
// Create file
driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true });
- } catch (e) {
+ } catch (_) {
// TODO: need to return Drive Error
throw new ApiError();
}
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
index 660aa55bf8..b9448b4bc2 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
@@ -24,39 +24,7 @@ export const meta = {
optional: false, nullable: false,
items: {
type: 'object',
- optional: false, nullable: false,
- properties: {
- id: {
- type: 'string',
- optional: false, nullable: false,
- format: 'id',
- },
- aliases: {
- type: 'array',
- optional: false, nullable: false,
- items: {
- type: 'string',
- optional: false, nullable: false,
- },
- },
- name: {
- type: 'string',
- optional: false, nullable: false,
- },
- category: {
- type: 'string',
- optional: false, nullable: true,
- },
- host: {
- type: 'string',
- optional: false, nullable: true,
- description: 'The local host is represented with `null`.',
- },
- url: {
- type: 'string',
- optional: false, nullable: false,
- },
- },
+ ref: 'EmojiDetailed',
},
},
} as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
index 34d200455e..658367409c 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
@@ -24,39 +24,7 @@ export const meta = {
optional: false, nullable: false,
items: {
type: 'object',
- optional: false, nullable: false,
- properties: {
- id: {
- type: 'string',
- optional: false, nullable: false,
- format: 'id',
- },
- aliases: {
- type: 'array',
- optional: false, nullable: false,
- items: {
- type: 'string',
- optional: false, nullable: false,
- },
- },
- name: {
- type: 'string',
- optional: false, nullable: false,
- },
- category: {
- type: 'string',
- optional: false, nullable: true,
- },
- host: {
- type: 'string',
- optional: false, nullable: true,
- description: 'The local host is represented with `null`. The field exists for compatibility with other API endpoints that return files.',
- },
- url: {
- type: 'string',
- optional: false, nullable: false,
- },
- },
+ ref: 'EmojiDetailed',
},
},
} as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
index 7bde10af46..e20bc21f6b 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -117,7 +117,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
case 'SAME_NAME_EMOJI_EXISTS': throw new ApiError(meta.errors.sameNameEmojiExists);
}
// 網羅性チェック
- const mustBeNever: never = error;
+ const _mustBeNever: never = error;
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
index b7781b8c99..bdd0ee6cac 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
@@ -13,7 +13,7 @@ export const meta = {
tags: ['admin'],
requireCredential: true,
- requireModerator: true,
+ requireAdmin: true,
kind: 'read:admin:user-ips',
res: {
type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 2c7f793584..5beed3a7e8 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -428,8 +428,7 @@ export const meta = {
optional: false, nullable: true,
},
clientOptions: {
- type: 'object',
- optional: false, nullable: false,
+ ref: 'MetaClientOptions',
},
description: {
type: 'string',
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index b3c2cecc67..372fe3a25f 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Injectable, Inject } from '@nestjs/common';
+import { DI } from '@/di-symbols.js';
import type { MiMeta } from '@/models/Meta.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
@@ -67,7 +68,14 @@ export const paramDef = {
description: { type: 'string', nullable: true },
defaultLightTheme: { type: 'string', nullable: true },
defaultDarkTheme: { type: 'string', nullable: true },
- clientOptions: { type: 'object', nullable: false },
+ clientOptions: {
+ type: 'object', nullable: false,
+ properties: {
+ entrancePageStyle: { type: 'string', nullable: false, enum: ['classic', 'simple'] },
+ showTimelineForVisitor: { type: 'boolean', nullable: false },
+ showActivitiesForVisitor: { type: 'boolean', nullable: false },
+ },
+ },
cacheRemoteFiles: { type: 'boolean' },
cacheRemoteSensitiveFiles: { type: 'boolean' },
emailRequiredForSignup: { type: 'boolean' },
@@ -217,6 +225,9 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
+ @Inject(DI.meta)
+ private serverSettings: MiMeta,
+
private metaService: MetaService,
private moderationLogService: ModerationLogService,
) {
@@ -329,7 +340,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
if (ps.clientOptions !== undefined) {
- set.clientOptions = ps.clientOptions;
+ set.clientOptions = {
+ ...this.serverSettings.clientOptions,
+ ...ps.clientOptions,
+ };
}
if (ps.cacheRemoteFiles !== undefined) {
diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts
index 14286bc23e..ff03fce72b 100644
--- a/packages/backend/src/server/api/endpoints/ap/get.ts
+++ b/packages/backend/src/server/api/endpoints/ap/get.ts
@@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private apResolverService: ApResolverService,
) {
super(meta, paramDef, async (ps, me) => {
- const resolver = this.apResolverService.createResolver();
+ const resolver = await this.apResolverService.createResolver();
const object = await resolver.resolve(ps.uri);
return object;
});
diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts
index fe48e7497a..47da6b4fbd 100644
--- a/packages/backend/src/server/api/endpoints/ap/show.ts
+++ b/packages/backend/src/server/api/endpoints/ap/show.ts
@@ -148,7 +148,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (this.utilityService.isSelfHost(host)) return null;
// リモートから一旦オブジェクトフェッチ
- const resolver = this.apResolverService.createResolver();
+ const resolver = await this.apResolverService.createResolver();
// allow ap/show exclusively to lookup URLs that are cross-origin or non-canonical (like https://alice.example.com/@bob@bob.example.com -> https://bob.example.com/@bob)
const object = await resolver.resolve(uri, FetchAllowSoftFailMask.CrossOrigin | FetchAllowSoftFailMask.NonCanonicalId).catch((err) => {
if (err instanceof IdentifiableError) {
@@ -215,7 +215,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
type: 'Note',
object,
};
- } catch (e) {
+ } catch (_) {
return null;
}
}
diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts
index 30f0c1b0c8..7b2c137bd4 100644
--- a/packages/backend/src/server/api/endpoints/hashtags/users.ts
+++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts
@@ -32,6 +32,7 @@ export const paramDef = {
properties: {
tag: { type: 'string' },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+ offset: { type: 'integer', default: 0 },
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
state: { type: 'string', enum: ['all', 'alive'], default: 'all' },
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' },
@@ -74,7 +75,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break;
}
- const users = await query.limit(ps.limit).getMany();
+ const users = await query
+ .limit(ps.limit)
+ .offset(ps.offset)
+ .getMany();
return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
});
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
index 65eece5b97..8dc5cafb56 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
@@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts
index 9391aee5e0..050dbaf49e 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts
@@ -212,7 +212,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
index a54c598213..b6c837eda7 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
@@ -72,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
index c350136eae..6e5d9943de 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
@@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
index b5a53cc889..23b577dc18 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
@@ -57,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts
index bb78d47149..19ea187ee8 100644
--- a/packages/backend/src/server/api/endpoints/i/change-password.ts
+++ b/packages/backend/src/server/api/endpoints/i/change-password.ts
@@ -45,7 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts
index bfa0b4605d..42324c7778 100644
--- a/packages/backend/src/server/api/endpoints/i/delete-account.ts
+++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts
@@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts
index f933eaab00..4fe39bb8e8 100644
--- a/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts
+++ b/packages/backend/src/server/api/endpoints/i/notifications-grouped.ts
@@ -71,7 +71,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private notificationService: NotificationService,
) {
super(meta, paramDef, async (ps, me) => {
- const EXTRA_LIMIT = 100;
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : undefined);
const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : undefined);
diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts
index da1faee30d..c2f4281f36 100644
--- a/packages/backend/src/server/api/endpoints/i/update-email.ts
+++ b/packages/backend/src/server/api/endpoints/i/update-email.ts
@@ -91,7 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
await this.userAuthService.twoFactorAuthenticate(profile, token);
- } catch (e) {
+ } catch (_) {
throw new Error('authentication failed');
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index 9971a1ea4d..5207d9f2b0 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -323,7 +323,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
try {
new RE2(regexp[1], regexp[2]);
- } catch (err) {
+ } catch (_) {
throw new ApiError(meta.errors.invalidRegexp);
}
}
@@ -587,7 +587,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
})
.execute();
}
- } catch (err) {
+ } catch (_) {
// なにもしない
}
}
diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts
index 29c6aa7434..7c0dddb827 100644
--- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts
@@ -59,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw err;
});
- const mutedNotes = await this.notesRepository.find({
+ const _mutedNotes = await this.notesRepository.find({
where: [{
id: note.threadId ?? note.id,
}, {
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index 047f9a053b..4defcc9dcf 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -86,7 +86,7 @@ export const paramDef = {
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
- birthday: { ...birthdaySchema, nullable: true },
+ birthday: { ...birthdaySchema, nullable: true, description: '@deprecated use get-following-users-by-birthday instead.' },
},
},
],
@@ -146,15 +146,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.andWhere('following.followerId = :userId', { userId: user.id })
.innerJoinAndSelect('following.followee', 'followee');
+ // @deprecated use get-following-users-by-birthday instead.
if (ps.birthday) {
- try {
- const birthday = ps.birthday.substring(5, 10);
- const birthdayUserQuery = this.userProfilesRepository.createQueryBuilder('user_profile');
- birthdayUserQuery.select('user_profile.userId')
- .where(`SUBSTR(user_profile.birthday, 6, 5) = '${birthday}'`);
+ query.innerJoin(this.userProfilesRepository.metadata.targetName, 'followeeProfile', 'followeeProfile.userId = following.followeeId');
- query.andWhere(`following.followeeId IN (${ birthdayUserQuery.getQuery() })`);
- } catch (err) {
+ try {
+ const birthday = ps.birthday.split('-');
+ birthday.shift(); // 年の部分を削除
+ // なぜか get_birthday_date() = :birthday だとインデックスが効かないので、BETWEEN で対応
+ query.andWhere('get_birthday_date(followeeProfile.birthday) BETWEEN :birthday AND :birthday', { birthday: parseInt(birthday.join('')) });
+ } catch (_) {
throw new ApiError(meta.errors.birthdayInvalid);
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/get-following-users-by-birthday.ts b/packages/backend/src/server/api/endpoints/users/get-following-users-by-birthday.ts
new file mode 100644
index 0000000000..947c19d81e
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/users/get-following-users-by-birthday.ts
@@ -0,0 +1,167 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Brackets } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import type {
+ FollowingsRepository,
+ UserProfilesRepository,
+} from '@/models/_.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import type { Packed } from '@/misc/json-schema.js';
+
+export const meta = {
+ tags: ['users'],
+
+ requireCredential: true,
+ kind: 'read:account',
+
+ description: 'Retrieve users who have a birthday on the specified range.',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ id: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'misskey:id',
+ },
+ birthday: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ user: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'UserLite',
+ },
+ },
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+ offset: { type: 'integer', default: 0 },
+ birthday: {
+ oneOf: [{
+ type: 'object',
+ properties: {
+ month: { type: 'integer', minimum: 1, maximum: 12 },
+ day: { type: 'integer', minimum: 1, maximum: 31 },
+ },
+ required: ['month', 'day'],
+ }, {
+ type: 'object',
+ properties: {
+ begin: {
+ type: 'object',
+ properties: {
+ month: { type: 'integer', minimum: 1, maximum: 12 },
+ day: { type: 'integer', minimum: 1, maximum: 31 },
+ },
+ required: ['month', 'day'],
+ },
+ end: {
+ type: 'object',
+ properties: {
+ month: { type: 'integer', minimum: 1, maximum: 12 },
+ day: { type: 'integer', minimum: 1, maximum: 31 },
+ },
+ required: ['month', 'day'],
+ },
+ },
+ required: ['begin', 'end'],
+ }],
+ },
+ },
+ required: ['birthday'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ @Inject(DI.userProfilesRepository)
+ private userProfilesRepository: UserProfilesRepository,
+ @Inject(DI.followingsRepository)
+ private followingsRepository: FollowingsRepository,
+
+ private userEntityService: UserEntityService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const query = this.followingsRepository
+ .createQueryBuilder('following')
+ .andWhere('following.followerId = :userId', { userId: me.id })
+ .innerJoin(this.userProfilesRepository.metadata.targetName, 'followeeProfile', 'followeeProfile.userId = following.followeeId');
+
+ if (Object.hasOwn(ps.birthday, 'begin') && Object.hasOwn(ps.birthday, 'end')) {
+ const range = ps.birthday as { begin: { month: number; day: number }; end: { month: number; day: number }; };
+
+ // 誕生日は mmdd の形式の最大4桁の数字(例: 8月30日 → 830)でインデックスが効くようになっているので、その形式に変換
+ const begin = range.begin.month * 100 + range.begin.day;
+ const end = range.end.month * 100 + range.end.day;
+
+ if (begin <= end) {
+ query.andWhere('get_birthday_date(followeeProfile.birthday) BETWEEN :begin AND :end', { begin, end });
+ } else {
+ // 12/31 から 1/1 の範囲を取得するために OR で対応
+ query.andWhere(new Brackets(qb => {
+ qb.where('get_birthday_date(followeeProfile.birthday) BETWEEN :begin AND 1231', { begin });
+ qb.orWhere('get_birthday_date(followeeProfile.birthday) BETWEEN 101 AND :end', { end });
+ }));
+ }
+ } else {
+ const { month, day } = ps.birthday as { month: number; day: number };
+ // なぜか get_birthday_date() = :birthday だとインデックスが効かないので、BETWEEN で対応
+ query.andWhere('get_birthday_date(followeeProfile.birthday) BETWEEN :birthday AND :birthday', { birthday: month * 100 + day });
+ }
+
+ query.select('following.followeeId', 'user_id');
+ query.addSelect('get_birthday_date(followeeProfile.birthday)', 'birthday_date');
+ query.orderBy('birthday_date', 'ASC');
+
+ const birthdayUsers = await query
+ .offset(ps.offset).limit(ps.limit)
+ .getRawMany<{ birthday_date: number; user_id: string }>();
+
+ const users = new Map<string, Packed<'UserLite'>>((
+ await this.userEntityService.packMany(
+ birthdayUsers.map(u => u.user_id),
+ me,
+ { schema: 'UserLite' },
+ )
+ ).map(u => [u.id, u]));
+
+ return birthdayUsers
+ .map(item => {
+ const birthday = new Date();
+ birthday.setHours(0, 0, 0, 0);
+ // item.birthday_date は mmdd の形式の最大4桁の数字(例: 8月30日 → 830)で出力されるので、日付に戻してDateオブジェクトに設定
+ birthday.setMonth(Math.floor(item.birthday_date / 100) - 1, item.birthday_date % 100);
+
+ if (birthday.getTime() < new Date().setHours(0, 0, 0, 0)) {
+ birthday.setFullYear(new Date().getFullYear() + 1);
+ }
+
+ const birthdayStr = `${birthday.getFullYear()}-${(birthday.getMonth() + 1).toString().padStart(2, '0')}-${(birthday.getDate()).toString().padStart(2, '0')}`;
+ return {
+ id: item.user_id,
+ birthday: birthdayStr,
+ user: users.get(item.user_id),
+ };
+ })
+ .filter(item => item.user != null)
+ .map(item => item as { id: string; birthday: string; user: Packed<'UserLite'> });
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/openapi/schemas.ts b/packages/backend/src/server/api/openapi/schemas.ts
index 1cdcbebd1a..0714f61294 100644
--- a/packages/backend/src/server/api/openapi/schemas.ts
+++ b/packages/backend/src/server/api/openapi/schemas.ts
@@ -9,9 +9,8 @@ import { refs } from '@/misc/json-schema.js';
export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 'res', includeSelfRef: boolean): any {
// optional, nullable, refはスキーマ定義に含まれないので分離しておく
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { optional, nullable, ref, selfRef, ..._res }: any = schema;
- const res = deepClone(_res);
+ const { optional, nullable, ref, selfRef, ...res1 }: any = schema;
+ const res = deepClone(res1);
if (schema.type === 'object' && schema.properties) {
if (type === 'res') {
diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts
index c0ef589dea..63ad9281b2 100644
--- a/packages/backend/src/server/api/stream/ChannelsService.ts
+++ b/packages/backend/src/server/api/stream/ChannelsService.ts
@@ -4,72 +4,54 @@
*/
import { Injectable } from '@nestjs/common';
+import { HybridTimelineChannel } from './channels/hybrid-timeline.js';
+import { LocalTimelineChannel } from './channels/local-timeline.js';
+import { HomeTimelineChannel } from './channels/home-timeline.js';
+import { GlobalTimelineChannel } from './channels/global-timeline.js';
+import { MainChannel } from './channels/main.js';
+import { ChannelChannel } from './channels/channel.js';
+import { AdminChannel } from './channels/admin.js';
+import { ServerStatsChannel } from './channels/server-stats.js';
+import { QueueStatsChannel } from './channels/queue-stats.js';
+import { UserListChannel } from './channels/user-list.js';
+import { AntennaChannel } from './channels/antenna.js';
+import { DriveChannel } from './channels/drive.js';
+import { HashtagChannel } from './channels/hashtag.js';
+import { RoleTimelineChannel } from './channels/role-timeline.js';
+import { ChatUserChannel } from './channels/chat-user.js';
+import { ChatRoomChannel } from './channels/chat-room.js';
+import { ReversiChannel } from './channels/reversi.js';
+import { ReversiGameChannel } from './channels/reversi-game.js';
+import type { ChannelConstructor } from './channel.js';
import { bindThis } from '@/decorators.js';
-import { HybridTimelineChannelService } from './channels/hybrid-timeline.js';
-import { LocalTimelineChannelService } from './channels/local-timeline.js';
-import { HomeTimelineChannelService } from './channels/home-timeline.js';
-import { GlobalTimelineChannelService } from './channels/global-timeline.js';
-import { MainChannelService } from './channels/main.js';
-import { ChannelChannelService } from './channels/channel.js';
-import { AdminChannelService } from './channels/admin.js';
-import { ServerStatsChannelService } from './channels/server-stats.js';
-import { QueueStatsChannelService } from './channels/queue-stats.js';
-import { UserListChannelService } from './channels/user-list.js';
-import { AntennaChannelService } from './channels/antenna.js';
-import { DriveChannelService } from './channels/drive.js';
-import { HashtagChannelService } from './channels/hashtag.js';
-import { RoleTimelineChannelService } from './channels/role-timeline.js';
-import { ChatUserChannelService } from './channels/chat-user.js';
-import { ChatRoomChannelService } from './channels/chat-room.js';
-import { ReversiChannelService } from './channels/reversi.js';
-import { ReversiGameChannelService } from './channels/reversi-game.js';
-import { type MiChannelService } from './channel.js';
@Injectable()
export class ChannelsService {
constructor(
- private mainChannelService: MainChannelService,
- private homeTimelineChannelService: HomeTimelineChannelService,
- private localTimelineChannelService: LocalTimelineChannelService,
- private hybridTimelineChannelService: HybridTimelineChannelService,
- private globalTimelineChannelService: GlobalTimelineChannelService,
- private userListChannelService: UserListChannelService,
- private hashtagChannelService: HashtagChannelService,
- private roleTimelineChannelService: RoleTimelineChannelService,
- private antennaChannelService: AntennaChannelService,
- private channelChannelService: ChannelChannelService,
- private driveChannelService: DriveChannelService,
- private serverStatsChannelService: ServerStatsChannelService,
- private queueStatsChannelService: QueueStatsChannelService,
- private adminChannelService: AdminChannelService,
- private chatUserChannelService: ChatUserChannelService,
- private chatRoomChannelService: ChatRoomChannelService,
- private reversiChannelService: ReversiChannelService,
- private reversiGameChannelService: ReversiGameChannelService,
) {
}
@bindThis
- public getChannelService(name: string): MiChannelService<boolean> {
+ public getChannelConstructor(name: string): ChannelConstructor<boolean> {
switch (name) {
- case 'main': return this.mainChannelService;
- case 'homeTimeline': return this.homeTimelineChannelService;
- case 'localTimeline': return this.localTimelineChannelService;
- case 'hybridTimeline': return this.hybridTimelineChannelService;
- case 'globalTimeline': return this.globalTimelineChannelService;
- case 'userList': return this.userListChannelService;
- case 'hashtag': return this.hashtagChannelService;
- case 'roleTimeline': return this.roleTimelineChannelService;
- case 'antenna': return this.antennaChannelService;
- case 'channel': return this.channelChannelService;
- case 'drive': return this.driveChannelService;
- case 'serverStats': return this.serverStatsChannelService;
- case 'queueStats': return this.queueStatsChannelService;
- case 'admin': return this.adminChannelService;
- case 'chatUser': return this.chatUserChannelService;
- case 'chatRoom': return this.chatRoomChannelService;
- case 'reversi': return this.reversiChannelService;
- case 'reversiGame': return this.reversiGameChannelService;
+ case 'main': return MainChannel;
+ case 'homeTimeline': return HomeTimelineChannel;
+ case 'localTimeline': return LocalTimelineChannel;
+ case 'hybridTimeline': return HybridTimelineChannel;
+ case 'globalTimeline': return GlobalTimelineChannel;
+ case 'userList': return UserListChannel;
+ case 'hashtag': return HashtagChannel;
+ case 'roleTimeline': return RoleTimelineChannel;
+ case 'antenna': return AntennaChannel;
+ case 'channel': return ChannelChannel;
+ case 'drive': return DriveChannel;
+ case 'serverStats': return ServerStatsChannel;
+ case 'queueStats': return QueueStatsChannel;
+ case 'admin': return AdminChannel;
+ case 'chatUser': return ChatUserChannel;
+ case 'chatRoom': return ChatRoomChannel;
+ case 'reversi': return ReversiChannel;
+ case 'reversiGame': return ReversiGameChannel;
default:
throw new Error(`no such channel: ${name}`);
diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts
index 222086c960..5989409997 100644
--- a/packages/backend/src/server/api/stream/Connection.ts
+++ b/packages/backend/src/server/api/stream/Connection.ts
@@ -6,19 +6,39 @@
import * as WebSocket from 'ws';
import type { MiUser } from '@/models/User.js';
import type { MiAccessToken } from '@/models/AccessToken.js';
-import type { Packed } from '@/misc/json-schema.js';
-import type { NotificationService } from '@/core/NotificationService.js';
+import { NotificationService } from '@/core/NotificationService.js';
import { bindThis } from '@/decorators.js';
import { CacheService } from '@/core/CacheService.js';
import { MiFollowing, MiUserProfile } from '@/models/_.js';
import type { GlobalEvents, StreamEventEmitter } from '@/core/GlobalEventService.js';
import { ChannelFollowingService } from '@/core/ChannelFollowingService.js';
import { ChannelMutingService } from '@/core/ChannelMutingService.js';
-import { isJsonObject } from '@/misc/json-value.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
-import type { ChannelsService } from './ChannelsService.js';
+import { isJsonObject } from '@/misc/json-value.js';
import type { EventEmitter } from 'events';
import type Channel from './channel.js';
+import type { ChannelConstructor } from './channel.js';
+import type { ChannelRequest } from './channel.js';
+import { ContextIdFactory, ModuleRef, REQUEST } from '@nestjs/core';
+import { Inject, Injectable, Scope } from '@nestjs/common';
+import { MainChannel } from '@/server/api/stream/channels/main.js';
+import { HomeTimelineChannel } from '@/server/api/stream/channels/home-timeline.js';
+import { LocalTimelineChannel } from '@/server/api/stream/channels/local-timeline.js';
+import { HybridTimelineChannel } from '@/server/api/stream/channels/hybrid-timeline.js';
+import { GlobalTimelineChannel } from '@/server/api/stream/channels/global-timeline.js';
+import { UserListChannel } from '@/server/api/stream/channels/user-list.js';
+import { HashtagChannel } from '@/server/api/stream/channels/hashtag.js';
+import { RoleTimelineChannel } from '@/server/api/stream/channels/role-timeline.js';
+import { AntennaChannel } from '@/server/api/stream/channels/antenna.js';
+import { ChannelChannel } from '@/server/api/stream/channels/channel.js';
+import { DriveChannel } from '@/server/api/stream/channels/drive.js';
+import { ServerStatsChannel } from '@/server/api/stream/channels/server-stats.js';
+import { QueueStatsChannel } from '@/server/api/stream/channels/queue-stats.js';
+import { AdminChannel } from '@/server/api/stream/channels/admin.js';
+import { ChatUserChannel } from '@/server/api/stream/channels/chat-user.js';
+import { ChatRoomChannel } from '@/server/api/stream/channels/chat-room.js';
+import { ReversiChannel } from '@/server/api/stream/channels/reversi.js';
+import { ReversiGameChannel } from '@/server/api/stream/channels/reversi-game.js';
const MAX_CHANNELS_PER_CONNECTION = 32;
@@ -26,6 +46,7 @@ const MAX_CHANNELS_PER_CONNECTION = 32;
* Main stream connection
*/
// eslint-disable-next-line import/no-default-export
+@Injectable({ scope: Scope.TRANSIENT })
export default class Connection {
public user?: MiUser;
public token?: MiAccessToken;
@@ -44,16 +65,16 @@ export default class Connection {
private fetchIntervalId: NodeJS.Timeout | null = null;
constructor(
- private channelsService: ChannelsService,
+ private moduleRef: ModuleRef,
private notificationService: NotificationService,
private cacheService: CacheService,
private channelFollowingService: ChannelFollowingService,
private channelMutingService: ChannelMutingService,
- user: MiUser | null | undefined,
- token: MiAccessToken | null | undefined,
+ @Inject(REQUEST)
+ request: ConnectionRequest,
) {
- if (user) this.user = user;
- if (token) this.token = token;
+ if (request.user) this.user = request.user;
+ if (request.token) this.token = request.token;
}
@bindThis
@@ -118,7 +139,7 @@ export default class Connection {
try {
obj = JSON.parse(data.toString());
- } catch (e) {
+ } catch (_) {
return;
}
@@ -232,28 +253,34 @@ export default class Connection {
* チャンネルに接続
*/
@bindThis
- public connectChannel(id: string, params: JsonObject | undefined, channel: string, pong = false) {
+ public async connectChannel(id: string, params: JsonObject | undefined, channel: string, pong = false) {
if (this.channels.length >= MAX_CHANNELS_PER_CONNECTION) {
return;
}
- const channelService = this.channelsService.getChannelService(channel);
+ const channelConstructor = this.getChannelConstructor(channel);
- if (channelService.requireCredential && this.user == null) {
+ if (channelConstructor.requireCredential && this.user == null) {
return;
}
- if (this.token && ((channelService.kind && !this.token.permission.some(p => p === channelService.kind))
- || (!channelService.kind && channelService.requireCredential))) {
+ if (this.token && ((channelConstructor.kind && !this.token.permission.some(p => p === channelConstructor.kind))
+ || (!channelConstructor.kind && channelConstructor.requireCredential))) {
return;
}
// 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視
- if (channelService.shouldShare && this.channels.some(c => c.chName === channel)) {
+ if (channelConstructor.shouldShare && this.channels.some(c => c.chName === channel)) {
return;
}
- const ch: Channel = channelService.create(id, this);
+ const contextId = ContextIdFactory.create();
+ this.moduleRef.registerRequestByContextId<ChannelRequest>({
+ id: id,
+ connection: this,
+ }, contextId);
+ const ch: Channel = await this.moduleRef.create<Channel>(channelConstructor, contextId);
+
this.channels.push(ch);
ch.init(params ?? {});
@@ -264,6 +291,33 @@ export default class Connection {
}
}
+ @bindThis
+ public getChannelConstructor(name: string): ChannelConstructor<boolean> {
+ switch (name) {
+ case 'main': return MainChannel;
+ case 'homeTimeline': return HomeTimelineChannel;
+ case 'localTimeline': return LocalTimelineChannel;
+ case 'hybridTimeline': return HybridTimelineChannel;
+ case 'globalTimeline': return GlobalTimelineChannel;
+ case 'userList': return UserListChannel;
+ case 'hashtag': return HashtagChannel;
+ case 'roleTimeline': return RoleTimelineChannel;
+ case 'antenna': return AntennaChannel;
+ case 'channel': return ChannelChannel;
+ case 'drive': return DriveChannel;
+ case 'serverStats': return ServerStatsChannel;
+ case 'queueStats': return QueueStatsChannel;
+ case 'admin': return AdminChannel;
+ case 'chatUser': return ChatUserChannel;
+ case 'chatRoom': return ChatRoomChannel;
+ case 'reversi': return ReversiChannel;
+ case 'reversiGame': return ReversiGameChannel;
+
+ default:
+ throw new Error(`no such channel: ${name}`);
+ }
+ }
+
/**
* チャンネルから切断
* @param id チャンネルコネクションID
@@ -306,3 +360,8 @@ export default class Connection {
}
}
}
+
+export interface ConnectionRequest {
+ user: MiUser | null | undefined,
+ token: MiAccessToken | null | undefined,
+}
diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts
index 465ed4238c..86b073414d 100644
--- a/packages/backend/src/server/api/stream/channel.ts
+++ b/packages/backend/src/server/api/stream/channel.ts
@@ -22,7 +22,7 @@ export default abstract class Channel {
public abstract readonly chName: string;
public static readonly shouldShare: boolean;
public static readonly requireCredential: boolean;
- public static readonly kind?: string | null;
+ public static readonly kind: string | null;
protected get user() {
return this.connection.user;
@@ -85,9 +85,9 @@ export default abstract class Channel {
return false;
}
- constructor(id: string, connection: Connection) {
- this.id = id;
- this.connection = connection;
+ constructor(request: ChannelRequest) {
+ this.id = request.id;
+ this.connection = request.connection;
}
public send(payload: { type: string, body: JsonValue }): void;
@@ -111,9 +111,14 @@ export default abstract class Channel {
public onMessage?(type: string, body: JsonValue): void;
}
-export type MiChannelService<T extends boolean> = {
+export interface ChannelRequest {
+ id: string,
+ connection: Connection,
+}
+
+export interface ChannelConstructor<T extends boolean> {
+ new(...args: any[]): Channel;
shouldShare: boolean;
requireCredential: T;
kind: T extends true ? string : string | null | undefined;
- create: (id: string, connection: Connection) => Channel;
-};
+}
diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts
index 355d5dba21..821888cca0 100644
--- a/packages/backend/src/server/api/stream/channels/admin.ts
+++ b/packages/backend/src/server/api/stream/channels/admin.ts
@@ -3,17 +3,26 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class AdminChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class AdminChannel extends Channel {
public readonly chName = 'admin';
public static shouldShare = true;
public static requireCredential = true as const;
public static kind = 'read:admin:stream';
+ constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+ ) {
+ super(request);
+ }
+
@bindThis
public async init(params: JsonObject) {
// Subscribe admin stream
@@ -22,22 +31,3 @@ class AdminChannel extends Channel {
});
}
}
-
-@Injectable()
-export class AdminChannelService implements MiChannelService<true> {
- public readonly shouldShare = AdminChannel.shouldShare;
- public readonly requireCredential = AdminChannel.requireCredential;
- public readonly kind = AdminChannel.kind;
-
- constructor(
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): AdminChannel {
- return new AdminChannel(
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts
index e08562fdf9..ece9d2c8b1 100644
--- a/packages/backend/src/server/api/stream/channels/antenna.ts
+++ b/packages/backend/src/server/api/stream/channels/antenna.ts
@@ -3,14 +3,16 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class AntennaChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class AntennaChannel extends Channel {
public readonly chName = 'antenna';
public static shouldShare = false;
public static requireCredential = true as const;
@@ -18,12 +20,12 @@ class AntennaChannel extends Channel {
private antennaId: string;
constructor(
- private noteEntityService: NoteEntityService,
+ @Inject(REQUEST)
+ request: ChannelRequest,
- id: string,
- connection: Channel['connection'],
+ private noteEntityService: NoteEntityService,
) {
- super(id, connection);
+ super(request);
//this.onEvent = this.onEvent.bind(this);
}
@@ -55,24 +57,3 @@ class AntennaChannel extends Channel {
this.subscriber.off(`antennaStream:${this.antennaId}`, this.onEvent);
}
}
-
-@Injectable()
-export class AntennaChannelService implements MiChannelService<true> {
- public readonly shouldShare = AntennaChannel.shouldShare;
- public readonly requireCredential = AntennaChannel.requireCredential;
- public readonly kind = AntennaChannel.kind;
-
- constructor(
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): AntennaChannel {
- return new AntennaChannel(
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts
index c07eaac98d..1706b17526 100644
--- a/packages/backend/src/server/api/stream/channels/channel.ts
+++ b/packages/backend/src/server/api/stream/channels/channel.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
@@ -11,20 +11,23 @@ import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class ChannelChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class ChannelChannel extends Channel {
public readonly chName = 'channel';
public static shouldShare = false;
public static requireCredential = false as const;
private channelId: string;
constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+
private noteEntityService: NoteEntityService,
- id: string,
- connection: Channel['connection'],
) {
- super(id, connection);
+ super(request);
//this.onNote = this.onNote.bind(this);
}
@@ -92,24 +95,3 @@ class ChannelChannel extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
-
-@Injectable()
-export class ChannelChannelService implements MiChannelService<false> {
- public readonly shouldShare = ChannelChannel.shouldShare;
- public readonly requireCredential = ChannelChannel.requireCredential;
- public readonly kind = ChannelChannel.kind;
-
- constructor(
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): ChannelChannel {
- return new ChannelChannel(
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/chat-room.ts b/packages/backend/src/server/api/stream/channels/chat-room.ts
index eda333dd30..7f949032e2 100644
--- a/packages/backend/src/server/api/stream/channels/chat-room.ts
+++ b/packages/backend/src/server/api/stream/channels/chat-room.ts
@@ -3,14 +3,16 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import type { JsonObject } from '@/misc/json-value.js';
import { ChatService } from '@/core/ChatService.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class ChatRoomChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class ChatRoomChannel extends Channel {
public readonly chName = 'chatRoom';
public static shouldShare = false;
public static requireCredential = true as const;
@@ -18,12 +20,12 @@ class ChatRoomChannel extends Channel {
private roomId: string;
constructor(
- private chatService: ChatService,
+ @Inject(REQUEST)
+ request: ChannelRequest,
- id: string,
- connection: Channel['connection'],
+ private chatService: ChatService,
) {
- super(id, connection);
+ super(request);
}
@bindThis
@@ -55,24 +57,3 @@ class ChatRoomChannel extends Channel {
this.subscriber.off(`chatRoomStream:${this.roomId}`, this.onEvent);
}
}
-
-@Injectable()
-export class ChatRoomChannelService implements MiChannelService<true> {
- public readonly shouldShare = ChatRoomChannel.shouldShare;
- public readonly requireCredential = ChatRoomChannel.requireCredential;
- public readonly kind = ChatRoomChannel.kind;
-
- constructor(
- private chatService: ChatService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): ChatRoomChannel {
- return new ChatRoomChannel(
- this.chatService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/chat-user.ts b/packages/backend/src/server/api/stream/channels/chat-user.ts
index 5323484ed7..36f3f67b28 100644
--- a/packages/backend/src/server/api/stream/channels/chat-user.ts
+++ b/packages/backend/src/server/api/stream/channels/chat-user.ts
@@ -3,14 +3,16 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import type { JsonObject } from '@/misc/json-value.js';
import { ChatService } from '@/core/ChatService.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class ChatUserChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class ChatUserChannel extends Channel {
public readonly chName = 'chatUser';
public static shouldShare = false;
public static requireCredential = true as const;
@@ -18,12 +20,12 @@ class ChatUserChannel extends Channel {
private otherId: string;
constructor(
- private chatService: ChatService,
+ @Inject(REQUEST)
+ request: ChannelRequest,
- id: string,
- connection: Channel['connection'],
+ private chatService: ChatService,
) {
- super(id, connection);
+ super(request);
}
@bindThis
@@ -55,24 +57,3 @@ class ChatUserChannel extends Channel {
this.subscriber.off(`chatUserStream:${this.user!.id}-${this.otherId}`, this.onEvent);
}
}
-
-@Injectable()
-export class ChatUserChannelService implements MiChannelService<true> {
- public readonly shouldShare = ChatUserChannel.shouldShare;
- public readonly requireCredential = ChatUserChannel.requireCredential;
- public readonly kind = ChatUserChannel.kind;
-
- constructor(
- private chatService: ChatService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): ChatUserChannel {
- return new ChatUserChannel(
- this.chatService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts
index 03768f3d23..6f2eb2c8f9 100644
--- a/packages/backend/src/server/api/stream/channels/drive.ts
+++ b/packages/backend/src/server/api/stream/channels/drive.ts
@@ -3,17 +3,26 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class DriveChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class DriveChannel extends Channel {
public readonly chName = 'drive';
public static shouldShare = true;
public static requireCredential = true as const;
public static kind = 'read:account';
+ constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+ ) {
+ super(request);
+ }
+
@bindThis
public async init(params: JsonObject) {
// Subscribe drive stream
@@ -22,22 +31,3 @@ class DriveChannel extends Channel {
});
}
}
-
-@Injectable()
-export class DriveChannelService implements MiChannelService<true> {
- public readonly shouldShare = DriveChannel.shouldShare;
- public readonly requireCredential = DriveChannel.requireCredential;
- public readonly kind = DriveChannel.kind;
-
- constructor(
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): DriveChannel {
- return new DriveChannel(
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index d7c781ad12..be6be1b1e7 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import type { Packed } from '@/misc/json-schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
@@ -11,9 +11,11 @@ import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class GlobalTimelineChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class GlobalTimelineChannel extends Channel {
public readonly chName = 'globalTimeline';
public static shouldShare = false;
public static requireCredential = false as const;
@@ -21,14 +23,14 @@ class GlobalTimelineChannel extends Channel {
private withFiles: boolean;
constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+
private metaService: MetaService,
private roleService: RoleService,
private noteEntityService: NoteEntityService,
-
- id: string,
- connection: Channel['connection'],
) {
- super(id, connection);
+ super(request);
//this.onNote = this.onNote.bind(this);
}
@@ -74,28 +76,3 @@ class GlobalTimelineChannel extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
-
-@Injectable()
-export class GlobalTimelineChannelService implements MiChannelService<false> {
- public readonly shouldShare = GlobalTimelineChannel.shouldShare;
- public readonly requireCredential = GlobalTimelineChannel.requireCredential;
- public readonly kind = GlobalTimelineChannel.kind;
-
- constructor(
- private metaService: MetaService,
- private roleService: RoleService,
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): GlobalTimelineChannel {
- return new GlobalTimelineChannel(
- this.metaService,
- this.roleService,
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts
index c911d63642..1456b4f262 100644
--- a/packages/backend/src/server/api/stream/channels/hashtag.ts
+++ b/packages/backend/src/server/api/stream/channels/hashtag.ts
@@ -3,28 +3,30 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class HashtagChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class HashtagChannel extends Channel {
public readonly chName = 'hashtag';
public static shouldShare = false;
public static requireCredential = false as const;
private q: string[][];
constructor(
- private noteEntityService: NoteEntityService,
+ @Inject(REQUEST)
+ request: ChannelRequest,
- id: string,
- connection: Channel['connection'],
+ private noteEntityService: NoteEntityService,
) {
- super(id, connection);
+ super(request);
//this.onNote = this.onNote.bind(this);
}
@@ -62,24 +64,3 @@ class HashtagChannel extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
-
-@Injectable()
-export class HashtagChannelService implements MiChannelService<false> {
- public readonly shouldShare = HashtagChannel.shouldShare;
- public readonly requireCredential = HashtagChannel.requireCredential;
- public readonly kind = HashtagChannel.kind;
-
- constructor(
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): HashtagChannel {
- return new HashtagChannel(
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index eb5b4a8c6c..665c11b692 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -3,15 +3,17 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class HomeTimelineChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class HomeTimelineChannel extends Channel {
public readonly chName = 'homeTimeline';
public static shouldShare = false;
public static requireCredential = true as const;
@@ -20,12 +22,12 @@ class HomeTimelineChannel extends Channel {
private withFiles: boolean;
constructor(
- private noteEntityService: NoteEntityService,
+ @Inject(REQUEST)
+ request: ChannelRequest,
- id: string,
- connection: Channel['connection'],
+ private noteEntityService: NoteEntityService,
) {
- super(id, connection);
+ super(request);
//this.onNote = this.onNote.bind(this);
}
@@ -98,24 +100,3 @@ class HomeTimelineChannel extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
-
-@Injectable()
-export class HomeTimelineChannelService implements MiChannelService<true> {
- public readonly shouldShare = HomeTimelineChannel.shouldShare;
- public readonly requireCredential = HomeTimelineChannel.requireCredential;
- public readonly kind = HomeTimelineChannel.kind;
-
- constructor(
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): HomeTimelineChannel {
- return new HomeTimelineChannel(
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index 2155e02012..54250d2a90 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import type { Packed } from '@/misc/json-schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
@@ -11,9 +11,11 @@ import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class HybridTimelineChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class HybridTimelineChannel extends Channel {
public readonly chName = 'hybridTimeline';
public static shouldShare = false;
public static requireCredential = true as const;
@@ -23,14 +25,14 @@ class HybridTimelineChannel extends Channel {
private withFiles: boolean;
constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+
private metaService: MetaService,
private roleService: RoleService,
private noteEntityService: NoteEntityService,
-
- id: string,
- connection: Channel['connection'],
) {
- super(id, connection);
+ super(request);
//this.onNote = this.onNote.bind(this);
}
@@ -118,28 +120,3 @@ class HybridTimelineChannel extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
-
-@Injectable()
-export class HybridTimelineChannelService implements MiChannelService<true> {
- public readonly shouldShare = HybridTimelineChannel.shouldShare;
- public readonly requireCredential = HybridTimelineChannel.requireCredential;
- public readonly kind = HybridTimelineChannel.kind;
-
- constructor(
- private metaService: MetaService,
- private roleService: RoleService,
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): HybridTimelineChannel {
- return new HybridTimelineChannel(
- this.metaService,
- this.roleService,
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts
index 3d7ed6acdb..b394e9663f 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import type { Packed } from '@/misc/json-schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
@@ -11,25 +11,27 @@ import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class LocalTimelineChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class LocalTimelineChannel extends Channel {
public readonly chName = 'localTimeline';
- public static shouldShare = false;
+ public static shouldShare = false as const;
public static requireCredential = false as const;
private withRenotes: boolean;
private withReplies: boolean;
private withFiles: boolean;
constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+
private metaService: MetaService,
private roleService: RoleService,
private noteEntityService: NoteEntityService,
-
- id: string,
- connection: Channel['connection'],
) {
- super(id, connection);
+ super(request);
//this.onNote = this.onNote.bind(this);
}
@@ -84,28 +86,3 @@ class LocalTimelineChannel extends Channel {
this.subscriber.off('notesStream', this.onNote);
}
}
-
-@Injectable()
-export class LocalTimelineChannelService implements MiChannelService<false> {
- public readonly shouldShare = LocalTimelineChannel.shouldShare;
- public readonly requireCredential = LocalTimelineChannel.requireCredential;
- public readonly kind = LocalTimelineChannel.kind;
-
- constructor(
- private metaService: MetaService,
- private roleService: RoleService,
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): LocalTimelineChannel {
- return new LocalTimelineChannel(
- this.metaService,
- this.roleService,
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts
index 525f24c105..2ce53ac288 100644
--- a/packages/backend/src/server/api/stream/channels/main.ts
+++ b/packages/backend/src/server/api/stream/channels/main.ts
@@ -3,26 +3,28 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class MainChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class MainChannel extends Channel {
public readonly chName = 'main';
public static shouldShare = true;
public static requireCredential = true as const;
public static kind = 'read:account';
constructor(
- private noteEntityService: NoteEntityService,
+ @Inject(REQUEST)
+ request: ChannelRequest,
- id: string,
- connection: Channel['connection'],
+ private noteEntityService: NoteEntityService,
) {
- super(id, connection);
+ super(request);
}
@bindThis
@@ -61,24 +63,3 @@ class MainChannel extends Channel {
});
}
}
-
-@Injectable()
-export class MainChannelService implements MiChannelService<true> {
- public readonly shouldShare = MainChannel.shouldShare;
- public readonly requireCredential = MainChannel.requireCredential;
- public readonly kind = MainChannel.kind;
-
- constructor(
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): MainChannel {
- return new MainChannel(
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts
index 91b62255b4..a87863f26c 100644
--- a/packages/backend/src/server/api/stream/channels/queue-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts
@@ -4,21 +4,26 @@
*/
import Xev from 'xev';
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import { isJsonObject } from '@/misc/json-value.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
const ev = new Xev();
-class QueueStatsChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class QueueStatsChannel extends Channel {
public readonly chName = 'queueStats';
public static shouldShare = true;
public static requireCredential = false as const;
- constructor(id: string, connection: Channel['connection']) {
- super(id, connection);
+ constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+ ) {
+ super(request);
//this.onStats = this.onStats.bind(this);
//this.onMessage = this.onMessage.bind(this);
}
@@ -56,22 +61,3 @@ class QueueStatsChannel extends Channel {
ev.removeListener('queueStats', this.onStats);
}
}
-
-@Injectable()
-export class QueueStatsChannelService implements MiChannelService<false> {
- public readonly shouldShare = QueueStatsChannel.shouldShare;
- public readonly requireCredential = QueueStatsChannel.requireCredential;
- public readonly kind = QueueStatsChannel.kind;
-
- constructor(
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): QueueStatsChannel {
- return new QueueStatsChannel(
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/reversi-game.ts b/packages/backend/src/server/api/stream/channels/reversi-game.ts
index 7597a1cfa3..58fc16e98c 100644
--- a/packages/backend/src/server/api/stream/channels/reversi-game.ts
+++ b/packages/backend/src/server/api/stream/channels/reversi-game.ts
@@ -3,31 +3,32 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Inject, Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import type { MiReversiGame } from '@/models/_.js';
-import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { ReversiService } from '@/core/ReversiService.js';
import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js';
import { isJsonObject } from '@/misc/json-value.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
import { reversiUpdateKeys } from 'misskey-js';
+import { REQUEST } from '@nestjs/core';
-class ReversiGameChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class ReversiGameChannel extends Channel {
public readonly chName = 'reversiGame';
public static shouldShare = false;
public static requireCredential = false as const;
private gameId: MiReversiGame['id'] | null = null;
constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+
private reversiService: ReversiService,
private reversiGameEntityService: ReversiGameEntityService,
-
- id: string,
- connection: Channel['connection'],
) {
- super(id, connection);
+ super(request);
}
@bindThis
@@ -107,25 +108,3 @@ class ReversiGameChannel extends Channel {
}
}
-@Injectable()
-export class ReversiGameChannelService implements MiChannelService<false> {
- public readonly shouldShare = ReversiGameChannel.shouldShare;
- public readonly requireCredential = ReversiGameChannel.requireCredential;
- public readonly kind = ReversiGameChannel.kind;
-
- constructor(
- private reversiService: ReversiService,
- private reversiGameEntityService: ReversiGameEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): ReversiGameChannel {
- return new ReversiGameChannel(
- this.reversiService,
- this.reversiGameEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/reversi.ts b/packages/backend/src/server/api/stream/channels/reversi.ts
index 6e88939724..5eff73eeef 100644
--- a/packages/backend/src/server/api/stream/channels/reversi.ts
+++ b/packages/backend/src/server/api/stream/channels/reversi.ts
@@ -3,22 +3,24 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class ReversiChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class ReversiChannel extends Channel {
public readonly chName = 'reversi';
public static shouldShare = true;
public static requireCredential = true as const;
public static kind = 'read:account';
constructor(
- id: string,
- connection: Channel['connection'],
+ @Inject(REQUEST)
+ request: ChannelRequest,
) {
- super(id, connection);
+ super(request);
}
@bindThis
@@ -32,22 +34,3 @@ class ReversiChannel extends Channel {
this.subscriber.off(`reversiStream:${this.user!.id}`, this.send);
}
}
-
-@Injectable()
-export class ReversiChannelService implements MiChannelService<true> {
- public readonly shouldShare = ReversiChannel.shouldShare;
- public readonly requireCredential = ReversiChannel.requireCredential;
- public readonly kind = ReversiChannel.kind;
-
- constructor(
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): ReversiChannel {
- return new ReversiChannel(
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts
index fcfa26c38b..99e0b69023 100644
--- a/packages/backend/src/server/api/stream/channels/role-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts
@@ -3,28 +3,30 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class RoleTimelineChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class RoleTimelineChannel extends Channel {
public readonly chName = 'roleTimeline';
public static shouldShare = false;
public static requireCredential = false as const;
private roleId: string;
constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+
private noteEntityService: NoteEntityService,
private roleservice: RoleService,
-
- id: string,
- connection: Channel['connection'],
) {
- super(id, connection);
+ super(request);
//this.onNote = this.onNote.bind(this);
}
@@ -60,26 +62,3 @@ class RoleTimelineChannel extends Channel {
this.subscriber.off(`roleTimelineStream:${this.roleId}`, this.onEvent);
}
}
-
-@Injectable()
-export class RoleTimelineChannelService implements MiChannelService<false> {
- public readonly shouldShare = RoleTimelineChannel.shouldShare;
- public readonly requireCredential = RoleTimelineChannel.requireCredential;
- public readonly kind = RoleTimelineChannel.kind;
-
- constructor(
- private noteEntityService: NoteEntityService,
- private roleservice: RoleService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): RoleTimelineChannel {
- return new RoleTimelineChannel(
- this.noteEntityService,
- this.roleservice,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts
index ec5352d12d..aece5435b0 100644
--- a/packages/backend/src/server/api/stream/channels/server-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/server-stats.ts
@@ -4,21 +4,26 @@
*/
import Xev from 'xev';
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
import { isJsonObject } from '@/misc/json-value.js';
import type { JsonObject, JsonValue } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
const ev = new Xev();
-class ServerStatsChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class ServerStatsChannel extends Channel {
public readonly chName = 'serverStats';
public static shouldShare = true;
public static requireCredential = false as const;
- constructor(id: string, connection: Channel['connection']) {
- super(id, connection);
+ constructor(
+ @Inject(REQUEST)
+ request: ChannelRequest,
+ ) {
+ super(request);
//this.onStats = this.onStats.bind(this);
//this.onMessage = this.onMessage.bind(this);
}
@@ -54,22 +59,3 @@ class ServerStatsChannel extends Channel {
ev.removeListener('serverStats', this.onStats);
}
}
-
-@Injectable()
-export class ServerStatsChannelService implements MiChannelService<false> {
- public readonly shouldShare = ServerStatsChannel.shouldShare;
- public readonly requireCredential = ServerStatsChannel.requireCredential;
- public readonly kind = ServerStatsChannel.kind;
-
- constructor(
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): ServerStatsChannel {
- return new ServerStatsChannel(
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts
index 5bfd8fa68c..2f7345e150 100644
--- a/packages/backend/src/server/api/stream/channels/user-list.ts
+++ b/packages/backend/src/server/api/stream/channels/user-list.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { Inject, Injectable } from '@nestjs/common';
+import { Inject, Injectable, Scope } from '@nestjs/common';
import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
@@ -11,9 +11,11 @@ import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { JsonObject } from '@/misc/json-value.js';
-import Channel, { type MiChannelService } from '../channel.js';
+import Channel, { type ChannelRequest } from '../channel.js';
+import { REQUEST } from '@nestjs/core';
-class UserListChannel extends Channel {
+@Injectable({ scope: Scope.TRANSIENT })
+export class UserListChannel extends Channel {
public readonly chName = 'userList';
public static shouldShare = false;
public static requireCredential = false as const;
@@ -24,14 +26,18 @@ class UserListChannel extends Channel {
private withRenotes: boolean;
constructor(
+ @Inject(DI.userListsRepository)
private userListsRepository: UserListsRepository,
+
+ @Inject(DI.userListMembershipsRepository)
private userListMembershipsRepository: UserListMembershipsRepository,
- private noteEntityService: NoteEntityService,
- id: string,
- connection: Channel['connection'],
+ @Inject(REQUEST)
+ request: ChannelRequest,
+
+ private noteEntityService: NoteEntityService,
) {
- super(id, connection);
+ super(request);
//this.updateListUsers = this.updateListUsers.bind(this);
//this.onNote = this.onNote.bind(this);
}
@@ -130,32 +136,3 @@ class UserListChannel extends Channel {
clearInterval(this.listUsersClock);
}
}
-
-@Injectable()
-export class UserListChannelService implements MiChannelService<false> {
- public readonly shouldShare = UserListChannel.shouldShare;
- public readonly requireCredential = UserListChannel.requireCredential;
- public readonly kind = UserListChannel.kind;
-
- constructor(
- @Inject(DI.userListsRepository)
- private userListsRepository: UserListsRepository,
-
- @Inject(DI.userListMembershipsRepository)
- private userListMembershipsRepository: UserListMembershipsRepository,
-
- private noteEntityService: NoteEntityService,
- ) {
- }
-
- @bindThis
- public create(id: string, connection: Channel['connection']): UserListChannel {
- return new UserListChannel(
- this.userListsRepository,
- this.userListMembershipsRepository,
- this.noteEntityService,
- id,
- connection,
- );
- }
-}
diff --git a/packages/backend/src/server/file/FileServerDriveHandler.ts b/packages/backend/src/server/file/FileServerDriveHandler.ts
new file mode 100644
index 0000000000..51b527b146
--- /dev/null
+++ b/packages/backend/src/server/file/FileServerDriveHandler.ts
@@ -0,0 +1,116 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as fs from 'node:fs';
+import rename from 'rename';
+import type { Config } from '@/config.js';
+import type { IImageStreamable } from '@/core/ImageProcessingService.js';
+import { contentDisposition } from '@/misc/content-disposition.js';
+import { correctFilename } from '@/misc/correct-filename.js';
+import { isMimeImage } from '@/misc/is-mime-image.js';
+import { VideoProcessingService } from '@/core/VideoProcessingService.js';
+import { attachStreamCleanup, handleRangeRequest, setFileResponseHeaders, getSafeContentType, needsCleanup } from './FileServerUtils.js';
+import type { FileServerFileResolver } from './FileServerFileResolver.js';
+import type { FastifyReply, FastifyRequest } from 'fastify';
+
+export class FileServerDriveHandler {
+ constructor(
+ private config: Config,
+ private fileResolver: FileServerFileResolver,
+ private assetsPath: string,
+ private videoProcessingService: VideoProcessingService,
+ ) {}
+
+ public async handle(request: FastifyRequest<{ Params: { key: string } }>, reply: FastifyReply) {
+ const key = request.params.key;
+ const file = await this.fileResolver.resolveFileByAccessKey(key);
+
+ if (file.kind === 'not-found') {
+ reply.code(404);
+ reply.header('Cache-Control', 'max-age=86400');
+ return reply.sendFile('/dummy.png', this.assetsPath);
+ }
+
+ if (file.kind === 'unavailable') {
+ reply.code(204);
+ reply.header('Cache-Control', 'max-age=86400');
+ return;
+ }
+
+ try {
+ if (file.kind === 'remote') {
+ let image: IImageStreamable | null = null;
+
+ if (file.fileRole === 'thumbnail') {
+ if (isMimeImage(file.mime, 'sharp-convertible-image-with-bmp')) {
+ reply.header('Cache-Control', 'max-age=31536000, immutable');
+
+ const url = new URL(`${this.config.mediaProxy}/static.webp`);
+ url.searchParams.set('url', file.url);
+ url.searchParams.set('static', '1');
+
+ file.cleanup();
+ return await reply.redirect(url.toString(), 301);
+ } else if (file.mime.startsWith('video/')) {
+ const externalThumbnail = this.videoProcessingService.getExternalVideoThumbnailUrl(file.url);
+ if (externalThumbnail) {
+ file.cleanup();
+ return await reply.redirect(externalThumbnail, 301);
+ }
+
+ image = await this.videoProcessingService.generateVideoThumbnail(file.path);
+ }
+ }
+
+ if (file.fileRole === 'webpublic') {
+ if (['image/svg+xml'].includes(file.mime)) {
+ reply.header('Cache-Control', 'max-age=31536000, immutable');
+
+ const url = new URL(`${this.config.mediaProxy}/svg.webp`);
+ url.searchParams.set('url', file.url);
+
+ file.cleanup();
+ return await reply.redirect(url.toString(), 301);
+ }
+ }
+
+ image ??= {
+ data: handleRangeRequest(reply, request.headers.range as string | undefined, file.file.size, file.path),
+ ext: file.ext,
+ type: file.mime,
+ };
+
+ attachStreamCleanup(image.data, file.cleanup);
+
+ reply.header('Content-Type', getSafeContentType(image.type));
+ reply.header('Content-Length', file.file.size);
+ reply.header('Cache-Control', 'max-age=31536000, immutable');
+ reply.header('Content-Disposition',
+ contentDisposition(
+ 'inline',
+ correctFilename(file.filename, image.ext),
+ ),
+ );
+ return image.data;
+ }
+
+ if (file.fileRole !== 'original') {
+ const filename = rename(file.filename, {
+ suffix: file.fileRole === 'thumbnail' ? '-thumb' : '-web',
+ extname: file.ext ? `.${file.ext}` : '.unknown',
+ }).toString();
+
+ setFileResponseHeaders(reply, { mime: file.mime, filename });
+ return handleRangeRequest(reply, request.headers.range as string | undefined, file.file.size, file.path);
+ } else {
+ setFileResponseHeaders(reply, { mime: file.file.type, filename: file.filename, size: file.file.size });
+ return handleRangeRequest(reply, request.headers.range as string | undefined, file.file.size, file.path);
+ }
+ } catch (e) {
+ if (file.kind === 'remote') file.cleanup();
+ throw e;
+ }
+ }
+}
diff --git a/packages/backend/src/server/file/FileServerFileResolver.ts b/packages/backend/src/server/file/FileServerFileResolver.ts
new file mode 100644
index 0000000000..687d486efd
--- /dev/null
+++ b/packages/backend/src/server/file/FileServerFileResolver.ts
@@ -0,0 +1,126 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as fs from 'node:fs';
+import type { DriveFilesRepository, MiDriveFile } from '@/models/_.js';
+import { createTemp } from '@/misc/create-temp.js';
+import type { DownloadService } from '@/core/DownloadService.js';
+import type { FileInfoService } from '@/core/FileInfoService.js';
+import type { InternalStorageService } from '@/core/InternalStorageService.js';
+
+export type DownloadedFileResult = {
+ kind: 'downloaded';
+ mime: string;
+ ext: string | null;
+ path: string;
+ cleanup: () => void;
+ filename: string;
+};
+
+export type FileResolveResult =
+ | { kind: 'not-found' }
+ | { kind: 'unavailable' }
+ | {
+ kind: 'stored';
+ fileRole: 'thumbnail' | 'webpublic' | 'original';
+ file: MiDriveFile;
+ filename: string;
+ mime: string;
+ ext: string | null;
+ path: string;
+ }
+ | {
+ kind: 'remote';
+ fileRole: 'thumbnail' | 'webpublic' | 'original';
+ file: MiDriveFile;
+ filename: string;
+ url: string;
+ mime: string;
+ ext: string | null;
+ path: string;
+ cleanup: () => void;
+ };
+
+export class FileServerFileResolver {
+ constructor(
+ private driveFilesRepository: DriveFilesRepository,
+ private fileInfoService: FileInfoService,
+ private downloadService: DownloadService,
+ private internalStorageService: InternalStorageService,
+ ) {}
+
+ public async downloadAndDetectTypeFromUrl(url: string): Promise<DownloadedFileResult> {
+ const [path, cleanup] = await createTemp();
+ try {
+ const { filename } = await this.downloadService.downloadUrl(url, path);
+
+ const { mime, ext } = await this.fileInfoService.detectType(path);
+
+ return {
+ kind: 'downloaded',
+ mime, ext,
+ path, cleanup,
+ filename,
+ };
+ } catch (e) {
+ cleanup();
+ throw e;
+ }
+ }
+
+ public async resolveFileByAccessKey(key: string): Promise<FileResolveResult> {
+ // Fetch drive file
+ const file = await this.driveFilesRepository.createQueryBuilder('file')
+ .where('file.accessKey = :accessKey', { accessKey: key })
+ .orWhere('file.thumbnailAccessKey = :thumbnailAccessKey', { thumbnailAccessKey: key })
+ .orWhere('file.webpublicAccessKey = :webpublicAccessKey', { webpublicAccessKey: key })
+ .getOne();
+
+ if (file == null) return { kind: 'not-found' };
+
+ const isThumbnail = file.thumbnailAccessKey === key;
+ const isWebpublic = file.webpublicAccessKey === key;
+
+ if (!file.storedInternal) {
+ if (!(file.isLink && file.uri)) return { kind: 'unavailable' };
+ const result = await this.downloadAndDetectTypeFromUrl(file.uri);
+ const { kind: _kind, ...downloaded } = result;
+ file.size = (await fs.promises.stat(downloaded.path)).size; // DB file.sizeは正確とは限らないので
+ return {
+ kind: 'remote',
+ ...downloaded,
+ url: file.uri,
+ fileRole: isThumbnail ? 'thumbnail' : isWebpublic ? 'webpublic' : 'original',
+ file,
+ filename: file.name,
+ };
+ }
+
+ const path = this.internalStorageService.resolvePath(key);
+
+ if (isThumbnail || isWebpublic) {
+ const { mime, ext } = await this.fileInfoService.detectType(path);
+ return {
+ kind: 'stored',
+ fileRole: isThumbnail ? 'thumbnail' : 'webpublic',
+ file,
+ filename: file.name,
+ mime, ext,
+ path,
+ };
+ }
+
+ return {
+ kind: 'stored',
+ fileRole: 'original',
+ file,
+ filename: file.name,
+ // 古いファイルは修正前のmimeを持っているのでできるだけ修正してあげる
+ mime: this.fileInfoService.fixMime(file.type),
+ ext: null,
+ path,
+ };
+ }
+}
diff --git a/packages/backend/src/server/file/FileServerProxyHandler.ts b/packages/backend/src/server/file/FileServerProxyHandler.ts
new file mode 100644
index 0000000000..41e8e47ba5
--- /dev/null
+++ b/packages/backend/src/server/file/FileServerProxyHandler.ts
@@ -0,0 +1,272 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as fs from 'node:fs';
+import sharp from 'sharp';
+import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
+import type { Config } from '@/config.js';
+import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
+import { StatusError } from '@/misc/status-error.js';
+import { contentDisposition } from '@/misc/content-disposition.js';
+import { correctFilename } from '@/misc/correct-filename.js';
+import { isMimeImage } from '@/misc/is-mime-image.js';
+import { IImageStreamable, ImageProcessingService, webpDefault } from '@/core/ImageProcessingService.js';
+import { createRangeStream, attachStreamCleanup, needsCleanup } from './FileServerUtils.js';
+import type { DownloadedFileResult, FileResolveResult, FileServerFileResolver } from './FileServerFileResolver.js';
+import type { FastifyReply, FastifyRequest } from 'fastify';
+
+type ProxySource = DownloadedFileResult | FileResolveResult;
+type CleanupableFile = ProxySource & { cleanup: () => void };
+type AvailableFile = Exclude<ProxySource, { kind: 'not-found' | 'unavailable' }>;
+type ProxyQuery = {
+ emoji?: string;
+ avatar?: string;
+ static?: string;
+ preview?: string;
+ badge?: string;
+ origin?: string;
+ url?: string;
+};
+
+export class FileServerProxyHandler {
+ constructor(
+ private config: Config,
+ private fileResolver: FileServerFileResolver,
+ private assetsPath: string,
+ private imageProcessingService: ImageProcessingService,
+ ) {}
+
+ public async handle(request: FastifyRequest<{ Params: { url: string }; Querystring: ProxyQuery }>, reply: FastifyReply) {
+ const url = 'url' in request.query ? request.query.url : 'https://' + request.params.url;
+
+ if (typeof url !== 'string') {
+ reply.code(400);
+ return;
+ }
+
+ // アバタークロップなど、どうしてもオリジンである必要がある場合
+ const mustOrigin = 'origin' in request.query;
+
+ if (this.config.externalMediaProxyEnabled && !mustOrigin) {
+ return await this.redirectToExternalProxy(request, reply);
+ }
+
+ this.validateUserAgent(request);
+
+ // Create temp file
+ const file = await this.getStreamAndTypeFromUrl(url);
+ if (file.kind === 'not-found') {
+ reply.code(404);
+ reply.header('Cache-Control', 'max-age=86400');
+ return reply.sendFile('/dummy.png', this.assetsPath);
+ }
+
+ if (file.kind === 'unavailable') {
+ reply.code(204);
+ reply.header('Cache-Control', 'max-age=86400');
+ return;
+ }
+
+ try {
+ const image = await this.processImage(file, request, reply);
+
+ if (needsCleanup(file)) {
+ attachStreamCleanup(image.data, file.cleanup);
+ }
+
+ reply.header('Content-Type', image.type);
+ reply.header('Cache-Control', 'max-age=31536000, immutable');
+ reply.header('Content-Disposition',
+ contentDisposition(
+ 'inline',
+ correctFilename(file.filename, image.ext),
+ ),
+ );
+ return image.data;
+ } catch (e) {
+ if (needsCleanup(file)) file.cleanup();
+ throw e;
+ }
+ }
+
+ /**
+ * 外部メディアプロキシにリダイレクトする
+ */
+ private async redirectToExternalProxy(
+ request: FastifyRequest<{ Params: { url: string }; Querystring: ProxyQuery }>,
+ reply: FastifyReply,
+ ) {
+ reply.header('Cache-Control', 'public, max-age=259200'); // 3 days
+
+ const url = new URL(`${this.config.mediaProxy}/${request.params.url || ''}`);
+
+ for (const [key, value] of Object.entries(request.query)) {
+ url.searchParams.append(key, value);
+ }
+
+ return reply.redirect(url.toString(), 301);
+ }
+
+ /**
+ * User-Agent を検証する
+ */
+ private validateUserAgent(request: FastifyRequest): void {
+ if (!request.headers['user-agent']) {
+ throw new StatusError('User-Agent is required', 400, 'User-Agent is required');
+ }
+ if (request.headers['user-agent'].toLowerCase().indexOf('misskey/') !== -1) {
+ throw new StatusError('Refusing to proxy a request from another proxy', 403, 'Proxy is recursive');
+ }
+ }
+
+ /**
+ * 画像を処理してストリーム可能な形式に変換する
+ */
+ private async processImage(
+ file: AvailableFile,
+ request: FastifyRequest<{ Params: { url: string }; Querystring: ProxyQuery }>,
+ reply: FastifyReply,
+ ): Promise<IImageStreamable> {
+ const query = request.query;
+
+ const requiresImageConversion = 'emoji' in query || 'avatar' in query || 'static' in query || 'preview' in query || 'badge' in query;
+ const isConvertibleImage = isMimeImage(file.mime, 'sharp-convertible-image-with-bmp');
+ if (requiresImageConversion && !isConvertibleImage) {
+ throw new StatusError('Unexpected mime', 404);
+ }
+
+ if ('emoji' in query || 'avatar' in query) {
+ return this.processEmojiOrAvatar(file, query);
+ }
+
+ if ('static' in query) {
+ return this.imageProcessingService.convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 498, 422);
+ }
+
+ if ('preview' in query) {
+ return this.imageProcessingService.convertSharpToWebpStream(await sharpBmp(file.path, file.mime), 200, 200);
+ }
+
+ if ('badge' in query) {
+ return this.processBadge(file);
+ }
+
+ if (file.mime === 'image/svg+xml') {
+ return this.imageProcessingService.convertToWebpStream(file.path, 2048, 2048);
+ }
+
+ if (!file.mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(file.mime)) {
+ throw new StatusError('Rejected type', 403, 'Rejected type');
+ }
+
+ return this.createDefaultStream(file, request, reply);
+ }
+
+ /**
+ * 絵文字またはアバター用の画像を処理する
+ */
+ private async processEmojiOrAvatar(
+ file: AvailableFile,
+ query: Pick<ProxyQuery, 'emoji' | 'avatar' | 'static'>,
+ ): Promise<IImageStreamable> {
+ const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image-with-bmp');
+ if (!isAnimationConvertibleImage && !('static' in query)) {
+ return {
+ data: fs.createReadStream(file.path),
+ ext: file.ext,
+ type: file.mime,
+ };
+ }
+
+ const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in query) }))
+ .resize({
+ height: 'emoji' in query ? 128 : 320,
+ withoutEnlargement: true,
+ })
+ .webp(webpDefault);
+
+ return {
+ data,
+ ext: 'webp',
+ type: 'image/webp',
+ };
+ }
+
+ /**
+ * バッジ用の画像を処理する
+ */
+ private async processBadge(file: AvailableFile): Promise<IImageStreamable> {
+ const mask = (await sharpBmp(file.path, file.mime))
+ .resize(96, 96, {
+ fit: 'contain',
+ position: 'centre',
+ withoutEnlargement: false,
+ })
+ .greyscale()
+ .normalise()
+ .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast
+ .flatten({ background: '#000' })
+ .toColorspace('b-w');
+
+ const stats = await mask.clone().stats();
+
+ if (stats.entropy < 0.1) {
+ throw new StatusError('Skip to provide badge', 404);
+ }
+
+ const data = sharp({
+ create: { width: 96, height: 96, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } },
+ })
+ .pipelineColorspace('b-w')
+ .boolean(await mask.png().toBuffer(), 'eor');
+
+ return {
+ data: await data.png().toBuffer(),
+ ext: 'png',
+ type: 'image/png',
+ };
+ }
+
+ /**
+ * デフォルトのストリームを作成する(Range リクエスト対応)
+ */
+ private createDefaultStream(
+ file: AvailableFile,
+ request: FastifyRequest,
+ reply: FastifyReply,
+ ): IImageStreamable {
+ if (request.headers.range && 'file' in file && file.file.size > 0) {
+ const { stream, start, end, chunksize } = createRangeStream(request.headers.range as string, file.file.size, file.path);
+
+ reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
+ reply.header('Accept-Ranges', 'bytes');
+ reply.header('Content-Length', chunksize);
+ reply.code(206);
+
+ return {
+ data: stream,
+ ext: file.ext,
+ type: file.mime,
+ };
+ }
+
+ return {
+ data: fs.createReadStream(file.path),
+ ext: file.ext,
+ type: file.mime,
+ };
+ }
+
+ private async getStreamAndTypeFromUrl(url: string): Promise<ProxySource> {
+ if (url.startsWith(`${this.config.url}/files/`)) {
+ const key = url.replace(`${this.config.url}/files/`, '').split('/').shift();
+ if (!key) throw new StatusError('Invalid File Key', 400, 'Invalid File Key');
+
+ return await this.fileResolver.resolveFileByAccessKey(key);
+ }
+
+ return await this.fileResolver.downloadAndDetectTypeFromUrl(url);
+ }
+}
diff --git a/packages/backend/src/server/file/FileServerUtils.ts b/packages/backend/src/server/file/FileServerUtils.ts
new file mode 100644
index 0000000000..c5995a2cca
--- /dev/null
+++ b/packages/backend/src/server/file/FileServerUtils.ts
@@ -0,0 +1,107 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as fs from 'node:fs';
+import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
+import { contentDisposition } from '@/misc/content-disposition.js';
+import type { IImageStreamable } from '@/core/ImageProcessingService.js';
+import type { FastifyReply } from 'fastify';
+
+export type RangeStream = {
+ stream: fs.ReadStream;
+ start: number;
+ end: number;
+ chunksize: number;
+};
+
+/**
+ * Range リクエストに対応したストリームを作成する
+ */
+export function createRangeStream(rangeHeader: string, size: number, path: string): RangeStream {
+ const parts = rangeHeader.replace(/bytes=/, '').split('-');
+ const start = parseInt(parts[0], 10);
+ let end = parts[1] ? parseInt(parts[1], 10) : size - 1;
+ if (end > size) {
+ end = size - 1;
+ }
+ const chunksize = end - start + 1;
+
+ return {
+ stream: fs.createReadStream(path, { start, end }),
+ start,
+ end,
+ chunksize,
+ };
+}
+
+/**
+ * ストリームにcleanupハンドラを設定する
+ * ストリームでない場合は即座にcleanupを実行する
+ */
+export function attachStreamCleanup(data: IImageStreamable['data'], cleanup: () => void): void {
+ if ('pipe' in data && typeof data.pipe === 'function') {
+ data.on('end', cleanup);
+ data.on('close', cleanup);
+ } else {
+ cleanup();
+ }
+}
+
+/**
+ * MIME タイプがブラウザセーフかどうかに応じて Content-Type を返す
+ */
+export function getSafeContentType(mime: string): string {
+ return FILE_TYPE_BROWSERSAFE.includes(mime) ? mime : 'application/octet-stream';
+}
+
+/**
+ * Range リクエストを処理してストリームを返す
+ * Range ヘッダーがない場合は通常のストリームを返す
+ */
+export function handleRangeRequest(
+ reply: FastifyReply,
+ rangeHeader: string | undefined,
+ size: number,
+ path: string,
+): fs.ReadStream {
+ if (rangeHeader && size > 0) {
+ const { stream, start, end, chunksize } = createRangeStream(rangeHeader, size, path);
+ reply.header('Content-Range', `bytes ${start}-${end}/${size}`);
+ reply.header('Accept-Ranges', 'bytes');
+ reply.header('Content-Length', chunksize);
+ reply.code(206);
+ return stream;
+ }
+ return fs.createReadStream(path);
+}
+
+export type FileResponseOptions = {
+ mime: string;
+ filename: string;
+ size?: number;
+ cacheControl?: string;
+};
+
+/**
+ * ファイルレスポンス用の共通ヘッダーを設定する
+ */
+export function setFileResponseHeaders(
+ reply: FastifyReply,
+ options: FileResponseOptions,
+): void {
+ reply.header('Content-Type', getSafeContentType(options.mime));
+ reply.header('Cache-Control', options.cacheControl ?? 'max-age=31536000, immutable');
+ reply.header('Content-Disposition', contentDisposition('inline', options.filename));
+ if (options.size !== undefined) {
+ reply.header('Content-Length', options.size);
+ }
+}
+
+/**
+ * cleanup が必要なファイルかどうかを判定する型ガード
+ */
+export function needsCleanup<T extends { kind?: string; cleanup?: () => void }>(file: T): file is T & { cleanup: () => void } {
+ return 'cleanup' in file && typeof file.cleanup === 'function';
+}
diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
index d2391c43ab..840c34b806 100644
--- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts
+++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
@@ -123,41 +123,86 @@ function parseMicroformats(doc: htmlParser.HTMLElement, baseUrl: string, id: str
return { name, logo };
}
-// https://indieauth.spec.indieweb.org/#client-information-discovery
-// "Authorization servers SHOULD support parsing the [h-app] Microformat from the client_id,
-// and if there is an [h-app] with a url property matching the client_id URL,
-// then it should use the name and icon and display them on the authorization prompt."
-// (But we don't display any icon for now)
-// https://indieauth.spec.indieweb.org/#redirect-url
-// "The client SHOULD publish one or more <link> tags or Link HTTP headers with a rel attribute
-// of redirect_uri at the client_id URL.
-// Authorization endpoints verifying that a redirect_uri is allowed for use by a client MUST
-// look for an exact match of the given redirect_uri in the request against the list of
-// redirect_uris discovered after resolving any relative URLs."
async function discoverClientInformation(logger: Logger, httpRequestService: HttpRequestService, id: string): Promise<ClientInformation> {
try {
const res = await httpRequestService.send(id);
+
const redirectUris: string[] = [];
+ let name = id;
+ let logo: string | null = null;
+ // https://indieauth.spec.indieweb.org/#redirect-url
+ // "The client SHOULD publish one or more <link> tags or Link HTTP headers with a rel attribute
+ // of redirect_uri at the client_id URL.
+ // Authorization endpoints verifying that a redirect_uri is allowed for use by a client MUST
+ // look for an exact match of the given redirect_uri in the request against the list of
+ // redirect_uris discovered after resolving any relative URLs."
const linkHeader = res.headers.get('link');
if (linkHeader) {
redirectUris.push(...httpLinkHeader.parse(linkHeader).get('rel', 'redirect_uri').map(r => r.uri));
}
- const text = await res.text();
- const doc = htmlParser.parse(`<div>${text}</div>`);
+ const contentType = res.headers.get('content-type');
+ const mediaType = contentType ? contentType.split(';')[0].trim() : null;
+ if (mediaType === 'application/json') {
+ // Client discovery via JSON document (11 July 2024 spec)
+ // https://indieauth.spec.indieweb.org/#client-metadata
+ // "Clients SHOULD have a JSON [RFC7159] document at their client_id URL containing
+ // client metadata defined in [RFC7591], the minimum properties for an IndieAuth
+ // client defined below."
- redirectUris.push(...[...doc.querySelectorAll('link[rel=redirect_uri][href]')].map(el => el.attributes.href));
+ const json = await res.json() as {
+ client_id: string;
+ client_name?: string;
+ client_uri: string;
+ logo_uri?: string;
+ redirect_uris?: string[];
+ };
- let name = id;
- let logo: string | null = null;
- if (text) {
- const microformats = parseMicroformats(doc, res.url, id);
- if (typeof microformats.name === 'string') {
- name = microformats.name;
+ // https://indieauth.spec.indieweb.org/#client-metadata-li-1
+ // "The authorization server MUST verify that the client_id in the document matches the
+ // client_id of the URL where the document was retrieved."
+ if (json.client_id !== id) {
+ throw new AuthorizationError('client_id in the document does not match the client_id URL', 'invalid_request');
+ }
+
+ // https://indieauth.spec.indieweb.org/#client-metadata-li-1
+ // "The client_uri MUST be a prefix of the client_id."
+ if (!json.client_uri || !id.startsWith(json.client_uri)) {
+ throw new AuthorizationError('client_uri is not a prefix of client_id', 'invalid_request');
+ }
+
+ if (typeof json.client_name === 'string') {
+ name = json.client_name;
}
- if (typeof microformats.logo === 'string') {
- logo = microformats.logo;
+
+ if (typeof json.logo_uri === 'string') {
+ // Since uri can be relative, resolve it against the document URL
+ logo = new URL(json.logo_uri, res.url).toString();
+ }
+
+ if (Array.isArray(json.redirect_uris)) {
+ redirectUris.push(...json.redirect_uris.filter((uri): uri is string => typeof uri === 'string'));
+ }
+ } else {
+ // Client discovery via HTML microformats (12 February 2022 spec)
+ // https://indieauth.spec.indieweb.org/20220212/#client-information-discovery
+ // "Authorization servers SHOULD support parsing the [h-app] Microformat from the client_id,
+ // and if there is an [h-app] with a url property matching the client_id URL,
+ // then it should use the name and icon and display them on the authorization prompt."
+ const text = await res.text();
+ const doc = htmlParser.parse(`<div>${text}</div>`);
+
+ redirectUris.push(...[...doc.querySelectorAll('link[rel=redirect_uri][href]')].map(el => el.attributes.href));
+
+ if (text) {
+ const microformats = parseMicroformats(doc, res.url, id);
+ if (typeof microformats.name === 'string') {
+ name = microformats.name;
+ }
+ if (typeof microformats.logo === 'string') {
+ logo = microformats.logo;
+ }
}
}
@@ -172,6 +217,8 @@ async function discoverClientInformation(logger: Logger, httpRequestService: Htt
logger.error('Error while fetching client information', { err });
if (err instanceof StatusError) {
throw new AuthorizationError('Failed to fetch client information', 'invalid_request');
+ } else if (err instanceof AuthorizationError) {
+ throw err;
} else {
throw new AuthorizationError('Failed to parse client information', 'server_error');
}
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index bcea935409..24bc619e79 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -4,8 +4,9 @@
*/
import { randomUUID } from 'node:crypto';
-import { dirname } from 'node:path';
+import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
+import * as fs from 'node:fs';
import { Inject, Injectable } from '@nestjs/common';
import ms from 'ms';
import sharp from 'sharp';
@@ -69,13 +70,28 @@ import type { FastifyError, FastifyInstance, FastifyPluginOptions, FastifyReply
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
-const staticAssets = `${_dirname}/../../../assets/`;
-const clientAssets = `${_dirname}/../../../../frontend/assets/`;
-const assets = `${_dirname}/../../../../../built/_frontend_dist_/`;
-const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`;
-const frontendViteOut = `${_dirname}/../../../../../built/_frontend_vite_/`;
-const frontendEmbedViteOut = `${_dirname}/../../../../../built/_frontend_embed_vite_/`;
-const tarball = `${_dirname}/../../../../../built/tarball/`;
+let rootDir = _dirname;
+// 見つかるまで上に遡る
+while (!fs.existsSync(resolve(rootDir, 'packages'))) {
+ const parentDir = dirname(rootDir);
+ if (parentDir === rootDir) {
+ throw new Error('Cannot find root directory');
+ }
+ rootDir = parentDir;
+}
+
+const backendRootDir = resolve(rootDir, 'packages/backend');
+const frontendRootDir = resolve(rootDir, 'packages/frontend');
+
+const staticAssets = resolve(backendRootDir, 'assets');
+const clientAssets = resolve(frontendRootDir, 'assets');
+const assets = resolve(rootDir, 'built/_frontend_dist_');
+const swAssets = resolve(rootDir, 'built/_sw_dist_');
+const fluentEmojisDir = resolve(rootDir, 'fluent-emojis/dist');
+const twemojiDir = resolve(backendRootDir, 'node_modules/@discordapp/twemoji/dist/svg');
+const frontendViteOut = resolve(rootDir, 'built/_frontend_vite_');
+const frontendEmbedViteOut = resolve(rootDir, 'built/_frontend_embed_vite_');
+const tarball = resolve(rootDir, 'built/tarball');
@Injectable()
export class ClientServerService {
@@ -207,6 +223,7 @@ export class ClientServerService {
//#region vite assets
if (this.config.frontendEmbedManifestExists) {
+ console.log(`[ClientServerService] Using built frontend vite assets. ${frontendViteOut}`);
fastify.register((fastify, options, done) => {
fastify.register(fastifyStatic, {
root: frontendViteOut,
@@ -226,6 +243,7 @@ export class ClientServerService {
done();
});
} else {
+ console.log('[ClientServerService] Proxying to Vite dev server.');
const urlOriginWithoutPort = configUrl.origin.replace(/:\d+$/, '');
const port = (process.env.VITE_PORT ?? '5173');
@@ -297,7 +315,7 @@ export class ClientServerService {
reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\'');
- return await reply.sendFile(path, `${_dirname}/../../../../../fluent-emojis/dist/`, {
+ return reply.sendFile(path, fluentEmojisDir, {
maxAge: ms('30 days'),
});
});
@@ -312,7 +330,7 @@ export class ClientServerService {
reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\'');
- return await reply.sendFile(path, `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/`, {
+ return reply.sendFile(path, twemojiDir, {
maxAge: ms('30 days'),
});
});
@@ -326,7 +344,7 @@ export class ClientServerService {
}
const mask = await sharp(
- `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/${path.replace('.png', '')}.svg`,
+ `${twemojiDir}/${path.replace('.png', '')}.svg`,
{ density: 1000 },
)
.resize(488, 488)
@@ -854,9 +872,6 @@ export class ClientServerService {
}));
});
- const override = (source: string, target: string, depth = 0) =>
- [, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/');
-
fastify.get('/flush', async (request, reply) => {
let sendHeader = true;
diff --git a/packages/backend/test-federation/compose.tpl.yml b/packages/backend/test-federation/compose.tpl.yml
index 1404345e2a..ac93b24b87 100644
--- a/packages/backend/test-federation/compose.tpl.yml
+++ b/packages/backend/test-federation/compose.tpl.yml
@@ -35,6 +35,10 @@ services:
target: /misskey/packages/backend/built
read_only: true
- type: bind
+ source: ../src-js
+ target: /misskey/packages/backend/src-js
+ read_only: true
+ - type: bind
source: ../migration
target: /misskey/packages/backend/migration
read_only: true
diff --git a/packages/backend/test-federation/compose.yml b/packages/backend/test-federation/compose.yml
index 25475a89ab..4d1b4b0d60 100644
--- a/packages/backend/test-federation/compose.yml
+++ b/packages/backend/test-federation/compose.yml
@@ -143,7 +143,7 @@ services:
bash -c "
npm install -g pnpm
pnpm -F backend i --frozen-lockfile
- pnpm exec tsc -p ./packages/backend/test-federation
+ pnpm exec tsgo -p ./packages/backend/test-federation
node ./packages/backend/test-federation/built/daemon.js
"
diff --git a/packages/backend/test-federation/test/utils.ts b/packages/backend/test-federation/test/utils.ts
index 056a16ba15..6f09f13f17 100644
--- a/packages/backend/test-federation/test/utils.ts
+++ b/packages/backend/test-federation/test/utils.ts
@@ -234,30 +234,26 @@ export async function isFired<C extends keyof Misskey.Channels, T extends keyof
cond: (msg: Parameters<Misskey.Channels[C]['events'][T]>[0]) => boolean,
params?: Misskey.Channels[C]['params'],
): Promise<boolean> {
- return new Promise<boolean>(async (resolve, reject) => {
- const stream = new Misskey.Stream(`wss://${host}`, { token: user.i }, { WebSocket });
+ const stream = new Misskey.Stream(`wss://${host}`, { token: user.i }, { WebSocket });
+ try {
const connection = stream.useChannel(channel, params);
- connection.on(type as any, ((msg: any) => {
- if (cond(msg)) {
- stream.close();
- clearTimeout(timer);
- resolve(true);
- }
- }) as any);
-
- let timer: NodeJS.Timeout | undefined;
- await trigger().then(() => {
- timer = setTimeout(() => {
- stream.close();
- resolve(false);
- }, 500);
- }).catch(err => {
- stream.close();
- clearTimeout(timer);
- reject(err);
+ const receivePromise = new Promise<boolean>((resolve) => {
+ connection.on(type as never, ((msg: any) => {
+ if (cond(msg)) {
+ resolve(true);
+ }
+ }) as any);
});
- });
+
+ await trigger();
+ return await Promise.race([
+ receivePromise,
+ sleep(500).then(() => false),
+ ]);
+ } finally {
+ stream.close();
+ }
};
export async function isNoteUpdatedEventFired(
@@ -267,30 +263,27 @@ export async function isNoteUpdatedEventFired(
trigger: () => Promise<unknown>,
cond: (msg: Parameters<Misskey.StreamEvents['noteUpdated']>[0]) => boolean,
): Promise<boolean> {
- return new Promise<boolean>(async (resolve, reject) => {
- const stream = new Misskey.Stream(`wss://${host}`, { token: user.i }, { WebSocket });
+ const stream = new Misskey.Stream(`wss://${host}`, { token: user.i }, { WebSocket });
+ try {
stream.send('s', { id: noteId });
- stream.on('noteUpdated', msg => {
- if (cond(msg)) {
- stream.close();
- clearTimeout(timer);
- resolve(true);
- }
+
+ const receivePromise = new Promise<boolean>((resolve) => {
+ stream.on('noteUpdated', msg => {
+ if (cond(msg)) {
+ resolve(true);
+ }
+ });
});
- let timer: NodeJS.Timeout | undefined;
+ await trigger();
- await trigger().then(() => {
- timer = setTimeout(() => {
- stream.close();
- resolve(false);
- }, 500);
- }).catch(err => {
- stream.close();
- clearTimeout(timer);
- reject(err);
- });
- });
+ return await Promise.race([
+ receivePromise,
+ sleep(500).then(() => false),
+ ]);
+ } finally {
+ stream.close();
+ }
};
export async function assertNotificationReceived(
diff --git a/packages/backend/test-server/.swcrc b/packages/backend/test-server/.swcrc
index eeac7eabc6..3859603da3 100644
--- a/packages/backend/test-server/.swcrc
+++ b/packages/backend/test-server/.swcrc
@@ -13,7 +13,7 @@
"experimental": {
"keepImportAssertions": true
},
- "baseUrl": "../built",
+ "baseUrl": "../src-js",
"paths": {
"@/*": ["*"]
},
diff --git a/packages/backend/test/compose.yml b/packages/backend/test/compose.yml
index fe96616fc0..4f1dba6428 100644
--- a/packages/backend/test/compose.yml
+++ b/packages/backend/test/compose.yml
@@ -11,3 +11,11 @@ services:
environment:
POSTGRES_DB: "test-misskey"
POSTGRES_HOST_AUTH_METHOD: trust
+
+ meilisearchtest:
+ image: getmeili/meilisearch:v1.3.4
+ ports:
+ - "127.0.0.1:57712:7700"
+ environment:
+ - MEILI_NO_ANALYTICS=true
+ - MEILI_ENV=development
diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts
index 96a6311a5a..67a9026eb5 100644
--- a/packages/backend/test/e2e/oauth.ts
+++ b/packages/backend/test/e2e/oauth.ts
@@ -28,6 +28,7 @@ const host = `http://127.0.0.1:${port}`;
const clientPort = port + 1;
const redirect_uri = `http://127.0.0.1:${clientPort}/redirect`;
+const redirect_uri2 = `http://127.0.0.1:${clientPort}/redirect2`;
const basicAuthParams: AuthorizationParamsExtended = {
redirect_uri,
@@ -807,45 +808,193 @@ describe('OAuth', () => {
});
});
- // https://indieauth.spec.indieweb.org/#client-information-discovery
describe('Client Information Discovery', () => {
- describe('Redirection', () => {
- const tests: Record<string, (reply: FastifyReply) => void> = {
- 'Read HTTP header': reply => {
- reply.header('Link', '</redirect>; rel="redirect_uri"');
- reply.send(`
- <!DOCTYPE html>
- <div class="h-app"><a href="/" class="u-url p-name">Misklient
- `);
- },
- 'Mixed links': reply => {
- reply.header('Link', '</redirect>; rel="redirect_uri"');
- reply.send(`
- <!DOCTYPE html>
- <link rel="redirect_uri" href="/redirect2" />
- <div class="h-app"><a href="/" class="u-url p-name">Misklient
- `);
- },
- 'Multiple items in Link header': reply => {
- reply.header('Link', '</redirect2>; rel="redirect_uri",</redirect>; rel="redirect_uri"');
- reply.send(`
- <!DOCTYPE html>
- <div class="h-app"><a href="/" class="u-url p-name">Misklient
- `);
- },
- 'Multiple items in HTML': reply => {
- reply.send(`
- <!DOCTYPE html>
- <link rel="redirect_uri" href="/redirect2" />
- <link rel="redirect_uri" href="/redirect" />
- <div class="h-app"><a href="/" class="u-url p-name">Misklient
- `);
- },
- };
+ // https://indieauth.spec.indieweb.org/#client-information-discovery
+ describe('JSON client metadata (11 July 2024)', () => {
+ test('Read JSON document', async () => {
+ sender = (reply): void => {
+ reply.header('content-type', 'application/json');
+ reply.send({
+ client_id: `http://127.0.0.1:${clientPort}/`,
+ client_uri: `http://127.0.0.1:${clientPort}/`,
+ client_name: 'Misklient JSON',
+ logo_uri: '/logo.png',
+ redirect_uris: ['/redirect'],
+ });
+ };
- for (const [title, replyFunc] of Object.entries(tests)) {
- test(title, async () => {
- sender = replyFunc;
+ const client = new AuthorizationCode(clientConfig);
+
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(response.status, 200);
+ const meta = getMeta(await response.text());
+ assert.strictEqual(meta.clientName, 'Misklient JSON');
+ assert.strictEqual(meta.clientLogo, `http://127.0.0.1:${clientPort}/logo.png`);
+ });
+
+ test('Merge Link header redirect_uri with JSON redirect_uris', async () => {
+ sender = (reply): void => {
+ reply.header('Link', '</redirect2>; rel="redirect_uri"');
+ reply.header('content-type', 'application/json');
+ reply.send({
+ client_id: `http://127.0.0.1:${clientPort}/`,
+ client_uri: `http://127.0.0.1:${clientPort}/`,
+ client_name: 'Misklient JSON',
+ redirect_uris: ['/redirect'],
+ });
+ };
+
+ const client = new AuthorizationCode(clientConfig);
+
+ const ok1 = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(ok1.status, 200);
+
+ const ok2 = await fetch(client.authorizeURL({
+ redirect_uri: redirect_uri2,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(ok2.status, 200);
+ });
+
+ test('Reject when client_id does not match retrieved URL', async () => {
+ sender = (reply): void => {
+ reply.header('content-type', 'application/json');
+ reply.send({
+ client_id: `http://127.0.0.1:${clientPort}/mismatch`,
+ client_uri: `http://127.0.0.1:${clientPort}/`,
+ redirect_uris: ['/redirect'],
+ });
+ };
+
+ const client = new AuthorizationCode(clientConfig);
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ await assertDirectError(response, 400, 'invalid_request');
+ });
+
+ test('Reject when client_uri is not a prefix of client_id', async () => {
+ sender = (reply): void => {
+ reply.header('content-type', 'application/json');
+ reply.send({
+ client_id: `http://127.0.0.1:${clientPort}/`,
+ client_uri: `http://127.0.0.1:${clientPort}/no-prefix/`,
+ redirect_uris: ['/redirect'],
+ });
+ };
+
+ const client = new AuthorizationCode(clientConfig);
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ await assertDirectError(response, 400, 'invalid_request');
+ });
+
+ test('Reject when JSON metadata has no redirect_uris and no Link header', async () => {
+ sender = (reply): void => {
+ reply.header('content-type', 'application/json');
+ reply.send({
+ client_id: `http://127.0.0.1:${clientPort}/`,
+ client_uri: `http://127.0.0.1:${clientPort}/`,
+ client_name: 'Misklient JSON',
+ });
+ };
+
+ const client = new AuthorizationCode(clientConfig);
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ await assertDirectError(response, 400, 'invalid_request');
+ });
+ });
+
+ // https://indieauth.spec.indieweb.org/20220212/#client-information-discovery
+ describe('HTML link client metadata (12 Feb 2022)', () => {
+ describe('Redirection', () => {
+ const tests: Record<string, (reply: FastifyReply) => void> = {
+ 'Read HTTP header': reply => {
+ reply.header('Link', '</redirect>; rel="redirect_uri"');
+ reply.send(`
+ <!DOCTYPE html>
+ <div class="h-app"><a href="/" class="u-url p-name">Misklient
+ `);
+ },
+ 'Mixed links': reply => {
+ reply.header('Link', '</redirect>; rel="redirect_uri"');
+ reply.send(`
+ <!DOCTYPE html>
+ <link rel="redirect_uri" href="/redirect2" />
+ <div class="h-app"><a href="/" class="u-url p-name">Misklient
+ `);
+ },
+ 'Multiple items in Link header': reply => {
+ reply.header('Link', '</redirect2>; rel="redirect_uri",</redirect>; rel="redirect_uri"');
+ reply.send(`
+ <!DOCTYPE html>
+ <div class="h-app"><a href="/" class="u-url p-name">Misklient
+ `);
+ },
+ 'Multiple items in HTML': reply => {
+ reply.send(`
+ <!DOCTYPE html>
+ <link rel="redirect_uri" href="/redirect2" />
+ <link rel="redirect_uri" href="/redirect" />
+ <div class="h-app"><a href="/" class="u-url p-name">Misklient
+ `);
+ },
+ };
+
+ for (const [title, replyFunc] of Object.entries(tests)) {
+ test(title, async () => {
+ sender = replyFunc;
+
+ const client = new AuthorizationCode(clientConfig);
+
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(response.status, 200);
+ });
+ }
+
+ test('No item', async () => {
+ sender = (reply): void => {
+ reply.send(`
+ <!DOCTYPE html>
+ <div class="h-app"><a href="/" class="u-url p-name">Misklient
+ `);
+ };
const client = new AuthorizationCode(clientConfig);
@@ -856,20 +1005,17 @@ describe('OAuth', () => {
code_challenge: 'code',
code_challenge_method: 'S256',
} as AuthorizationParamsExtended));
- assert.strictEqual(response.status, 200);
+
+ // direct error because there's no redirect URI to ping
+ await assertDirectError(response, 400, 'invalid_request');
});
- }
+ });
- test('No item', async () => {
- sender = (reply): void => {
- reply.send(`
- <!DOCTYPE html>
- <div class="h-app"><a href="/" class="u-url p-name">Misklient
- `);
- };
- const client = new AuthorizationCode(clientConfig);
+ test('Disallow loopback', async () => {
+ await sendEnvUpdateRequest({ key: 'MISSKEY_TEST_CHECK_IP_RANGE', value: '1' });
+ const client = new AuthorizationCode(clientConfig);
const response = await fetch(client.authorizeURL({
redirect_uri,
scope: 'write:notes',
@@ -877,119 +1023,103 @@ describe('OAuth', () => {
code_challenge: 'code',
code_challenge_method: 'S256',
} as AuthorizationParamsExtended));
-
- // direct error because there's no redirect URI to ping
await assertDirectError(response, 400, 'invalid_request');
});
- });
- test('Disallow loopback', async () => {
- await sendEnvUpdateRequest({ key: 'MISSKEY_TEST_CHECK_IP_RANGE', value: '1' });
-
- const client = new AuthorizationCode(clientConfig);
- const response = await fetch(client.authorizeURL({
- redirect_uri,
- scope: 'write:notes',
- state: 'state',
- code_challenge: 'code',
- code_challenge_method: 'S256',
- } as AuthorizationParamsExtended));
- await assertDirectError(response, 400, 'invalid_request');
- });
-
- test('Missing name', async () => {
- sender = (reply): void => {
- reply.header('Link', '</redirect>; rel="redirect_uri"');
- reply.send();
- };
+ test('Missing name', async () => {
+ sender = (reply): void => {
+ reply.header('Link', '</redirect>; rel="redirect_uri"');
+ reply.send();
+ };
- const client = new AuthorizationCode(clientConfig);
+ const client = new AuthorizationCode(clientConfig);
- const response = await fetch(client.authorizeURL({
- redirect_uri,
- scope: 'write:notes',
- state: 'state',
- code_challenge: 'code',
- code_challenge_method: 'S256',
- } as AuthorizationParamsExtended));
- assert.strictEqual(response.status, 200);
- assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
- });
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(response.status, 200);
+ assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
+ });
- test('With Logo', async () => {
- sender = (reply): void => {
- reply.header('Link', '</redirect>; rel="redirect_uri"');
- reply.send(`
- <!DOCTYPE html>
- <div class="h-app">
- <a href="/" class="u-url p-name">Misklient</a>
- <img src="/logo.png" class="u-logo" />
- </div>
- `);
- reply.send();
- };
+ test('With Logo', async () => {
+ sender = (reply): void => {
+ reply.header('Link', '</redirect>; rel="redirect_uri"');
+ reply.send(`
+ <!DOCTYPE html>
+ <div class="h-app">
+ <a href="/" class="u-url p-name">Misklient</a>
+ <img src="/logo.png" class="u-logo" />
+ </div>
+ `);
+ reply.send();
+ };
- const client = new AuthorizationCode(clientConfig);
+ const client = new AuthorizationCode(clientConfig);
- const response = await fetch(client.authorizeURL({
- redirect_uri,
- scope: 'write:notes',
- state: 'state',
- code_challenge: 'code',
- code_challenge_method: 'S256',
- } as AuthorizationParamsExtended));
- assert.strictEqual(response.status, 200);
- const meta = getMeta(await response.text());
- assert.strictEqual(meta.clientName, 'Misklient');
- assert.strictEqual(meta.clientLogo, `http://127.0.0.1:${clientPort}/logo.png`);
- });
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(response.status, 200);
+ const meta = getMeta(await response.text());
+ assert.strictEqual(meta.clientName, 'Misklient');
+ assert.strictEqual(meta.clientLogo, `http://127.0.0.1:${clientPort}/logo.png`);
+ });
- test('Missing Logo', async () => {
- sender = (reply): void => {
- reply.header('Link', '</redirect>; rel="redirect_uri"');
- reply.send(`
- <!DOCTYPE html>
- <div class="h-app"><a href="/" class="u-url p-name">Misklient
- `);
- reply.send();
- };
+ test('Missing Logo', async () => {
+ sender = (reply): void => {
+ reply.header('Link', '</redirect>; rel="redirect_uri"');
+ reply.send(`
+ <!DOCTYPE html>
+ <div class="h-app"><a href="/" class="u-url p-name">Misklient
+ `);
+ reply.send();
+ };
- const client = new AuthorizationCode(clientConfig);
+ const client = new AuthorizationCode(clientConfig);
- const response = await fetch(client.authorizeURL({
- redirect_uri,
- scope: 'write:notes',
- state: 'state',
- code_challenge: 'code',
- code_challenge_method: 'S256',
- } as AuthorizationParamsExtended));
- assert.strictEqual(response.status, 200);
- const meta = getMeta(await response.text());
- assert.strictEqual(meta.clientName, 'Misklient');
- assert.strictEqual(meta.clientLogo, undefined);
- });
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(response.status, 200);
+ const meta = getMeta(await response.text());
+ assert.strictEqual(meta.clientName, 'Misklient');
+ assert.strictEqual(meta.clientLogo, undefined);
+ });
- test('Mismatching URL in h-app', async () => {
- sender = (reply): void => {
- reply.header('Link', '</redirect>; rel="redirect_uri"');
- reply.send(`
- <!DOCTYPE html>
- <div class="h-app"><a href="/foo" class="u-url p-name">Misklient
- `);
- reply.send();
- };
+ test('Mismatching URL in h-app', async () => {
+ sender = (reply): void => {
+ reply.header('Link', '</redirect>; rel="redirect_uri"');
+ reply.send(`
+ <!DOCTYPE html>
+ <div class="h-app"><a href="/foo" class="u-url p-name">Misklient
+ `);
+ reply.send();
+ };
- const client = new AuthorizationCode(clientConfig);
+ const client = new AuthorizationCode(clientConfig);
- const response = await fetch(client.authorizeURL({
- redirect_uri,
- scope: 'write:notes',
- state: 'state',
- code_challenge: 'code',
- code_challenge_method: 'S256',
- } as AuthorizationParamsExtended));
- assert.strictEqual(response.status, 200);
- assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
+ const response = await fetch(client.authorizeURL({
+ redirect_uri,
+ scope: 'write:notes',
+ state: 'state',
+ code_challenge: 'code',
+ code_challenge_method: 'S256',
+ } as AuthorizationParamsExtended));
+ assert.strictEqual(response.status, 200);
+ assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
+ });
});
});
diff --git a/packages/backend/test/resources/dummy-for-file-server-service.png b/packages/backend/test/resources/dummy-for-file-server-service.png
new file mode 100644
index 0000000000..39332b0c1b
--- /dev/null
+++ b/packages/backend/test/resources/dummy-for-file-server-service.png
Binary files differ
diff --git a/packages/backend/test/tsconfig.json b/packages/backend/test/tsconfig.json
index c6754c4802..a2a86c696e 100644
--- a/packages/backend/test/tsconfig.json
+++ b/packages/backend/test/tsconfig.json
@@ -25,7 +25,6 @@
"isolatedModules": true,
"jsx": "react-jsx",
"jsxImportSource": "@kitajs/html",
- "baseUrl": "./",
"paths": {
"@/*": ["../src/*"]
},
diff --git a/packages/backend/test/unit/SearchService.ts b/packages/backend/test/unit/SearchService.ts
new file mode 100644
index 0000000000..6e17bef1c3
--- /dev/null
+++ b/packages/backend/test/unit/SearchService.ts
@@ -0,0 +1,483 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { afterAll, afterEach, beforeAll, describe, expect, test } from '@jest/globals';
+import { Test, TestingModule } from '@nestjs/testing';
+import type { Index, MeiliSearch } from 'meilisearch';
+import { type Config, loadConfig } from '@/config.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { CoreModule } from '@/core/CoreModule.js';
+import { SearchService } from '@/core/SearchService.js';
+import { CacheService } from '@/core/CacheService.js';
+import { IdService } from '@/core/IdService.js';
+import { DI } from '@/di-symbols.js';
+import {
+ type BlockingsRepository,
+ type ChannelsRepository,
+ type FollowingsRepository,
+ type MutingsRepository,
+ type NotesRepository,
+ type UserProfilesRepository,
+ type UsersRepository,
+ type MiChannel,
+ type MiNote,
+ type MiUser,
+} from '@/models/_.js';
+
+describe('SearchService', () => {
+ type TestContext = {
+ app: TestingModule;
+ service: SearchService;
+ cacheService: CacheService;
+ idService: IdService;
+ mutingsRepository: MutingsRepository;
+ blockingsRepository: BlockingsRepository;
+ usersRepository: UsersRepository;
+ userProfilesRepository: UserProfilesRepository;
+ notesRepository: NotesRepository;
+ channelsRepository: ChannelsRepository;
+ followingsRepository: FollowingsRepository;
+ indexer?: (note: MiNote) => Promise<void>;
+ };
+
+ const meilisearchSettings = {
+ searchableAttributes: [
+ 'text',
+ 'cw',
+ ],
+ sortableAttributes: [
+ 'createdAt',
+ ],
+ filterableAttributes: [
+ 'createdAt',
+ 'userId',
+ 'userHost',
+ 'channelId',
+ 'tags',
+ ],
+ typoTolerance: {
+ enabled: false,
+ },
+ pagination: {
+ maxTotalHits: 10000,
+ },
+ };
+
+ async function buildContext(configOverride?: Config): Promise<TestContext> {
+ const builder = Test.createTestingModule({
+ imports: [
+ GlobalModule,
+ CoreModule,
+ ],
+ });
+
+ if (configOverride) {
+ builder.overrideProvider(DI.config).useValue(configOverride);
+ }
+
+ const app = await builder.compile();
+
+ app.enableShutdownHooks();
+
+ return {
+ app,
+ service: app.get(SearchService),
+ cacheService: app.get(CacheService),
+ idService: app.get(IdService),
+ mutingsRepository: app.get(DI.mutingsRepository),
+ blockingsRepository: app.get(DI.blockingsRepository),
+ usersRepository: app.get(DI.usersRepository),
+ userProfilesRepository: app.get(DI.userProfilesRepository),
+ notesRepository: app.get(DI.notesRepository),
+ channelsRepository: app.get(DI.channelsRepository),
+ followingsRepository: app.get(DI.followingsRepository),
+ };
+ }
+
+ async function cleanupContext(ctx: TestContext) {
+ await ctx.notesRepository.createQueryBuilder().delete().execute();
+ await ctx.mutingsRepository.createQueryBuilder().delete().execute();
+ await ctx.blockingsRepository.createQueryBuilder().delete().execute();
+ await ctx.followingsRepository.createQueryBuilder().delete().execute();
+ await ctx.channelsRepository.createQueryBuilder().delete().execute();
+ await ctx.userProfilesRepository.createQueryBuilder().delete().execute();
+ await ctx.usersRepository.createQueryBuilder().delete().execute();
+ }
+
+ async function createUser(ctx: TestContext, data: Partial<MiUser> = {}) {
+ const id = ctx.idService.gen();
+ const username = data.username ?? `user_${id}`;
+ const usernameLower = data.usernameLower ?? username.toLowerCase();
+
+ const user = await ctx.usersRepository
+ .insert({
+ id,
+ username,
+ usernameLower,
+ ...data,
+ })
+ .then(x => ctx.usersRepository.findOneByOrFail(x.identifiers[0]));
+
+ await ctx.userProfilesRepository.insert({
+ userId: id,
+ });
+
+ return user;
+ }
+
+ async function createChannel(ctx: TestContext, user: MiUser, data: Partial<MiChannel> = {}) {
+ const id = ctx.idService.gen();
+ const channel = await ctx.channelsRepository
+ .insert({
+ id,
+ userId: user.id,
+ name: data.name ?? `channel_${id}`,
+ ...data,
+ })
+ .then(x => ctx.channelsRepository.findOneByOrFail(x.identifiers[0]));
+
+ return channel;
+ }
+
+ async function createNote(ctx: TestContext, user: MiUser, data: Partial<MiNote> = {}, time?: number) {
+ const id = time == null ? ctx.idService.gen() : ctx.idService.gen(time);
+ const note = await ctx.notesRepository
+ .insert({
+ id,
+ text: 'hello',
+ userId: user.id,
+ userHost: user.host,
+ visibility: 'public',
+ tags: [],
+ ...data,
+ })
+ .then(x => ctx.notesRepository.findOneByOrFail(x.identifiers[0]));
+
+ if (ctx.indexer) {
+ await ctx.indexer(note);
+ }
+
+ return note;
+ }
+
+ async function createFollowing(ctx: TestContext, follower: MiUser, followee: MiUser) {
+ await ctx.followingsRepository.insert({
+ id: ctx.idService.gen(),
+ followerId: follower.id,
+ followeeId: followee.id,
+ followerHost: follower.host,
+ followeeHost: followee.host,
+ });
+ }
+
+ function clearUserCaches(ctx: TestContext, userId: MiUser['id']) {
+ ctx.cacheService.userMutingsCache.delete(userId);
+ ctx.cacheService.userBlockedCache.delete(userId);
+ ctx.cacheService.userBlockingCache.delete(userId);
+ }
+
+ async function createMuting(ctx: TestContext, muter: MiUser, mutee: MiUser) {
+ await ctx.mutingsRepository.insert({
+ id: ctx.idService.gen(),
+ muterId: muter.id,
+ muteeId: mutee.id,
+ });
+ clearUserCaches(ctx, muter.id);
+ }
+
+ async function createBlocking(ctx: TestContext, blocker: MiUser, blockee: MiUser) {
+ await ctx.blockingsRepository.insert({
+ id: ctx.idService.gen(),
+ blockerId: blocker.id,
+ blockeeId: blockee.id,
+ });
+ clearUserCaches(ctx, blocker.id);
+ clearUserCaches(ctx, blockee.id);
+ }
+
+ function defineSearchNoteTests(
+ getCtx: () => TestContext,
+ {
+ supportsFollowersVisibility,
+ sinceIdOrder,
+ }: {
+ supportsFollowersVisibility: boolean;
+ sinceIdOrder: 'asc' | 'desc';
+ },
+ ) {
+ describe('searchNote', () => {
+ test('filters notes by visibility (followers only visible to followers)', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const author = await createUser(ctx, { username: 'author', usernameLower: 'author', host: null });
+
+ const publicNote = await createNote(ctx, author, { text: 'hello public', visibility: 'public' });
+ const followersNote = await createNote(ctx, author, { text: 'hello followers', visibility: 'followers' });
+
+ const beforeFollow = await ctx.service.searchNote('hello', me, {}, { limit: 10 });
+ expect(beforeFollow.map(note => note.id)).toEqual([publicNote.id]);
+
+ await createFollowing(ctx, me, author);
+
+ const afterFollow = await ctx.service.searchNote('hello', me, {}, { limit: 10 });
+ const expectedIds = supportsFollowersVisibility
+ ? [followersNote.id, publicNote.id]
+ : [publicNote.id];
+ expect(afterFollow.map(note => note.id).sort()).toEqual(expectedIds.sort());
+ });
+
+ test('filters out suspended users via base note filtering', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const active = await createUser(ctx, { username: 'active', usernameLower: 'active', host: null });
+ const suspended = await createUser(ctx, { username: 'suspended', usernameLower: 'suspended', host: null, isSuspended: true });
+
+ const activeNote = await createNote(ctx, active, { text: 'hello active', visibility: 'public' });
+ await createNote(ctx, suspended, { text: 'hello suspended', visibility: 'public' });
+
+ const result = await ctx.service.searchNote('hello', me, {}, { limit: 10 });
+ expect(result.map(note => note.id)).toEqual([activeNote.id]);
+ });
+
+ test('filters by userId', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const alice = await createUser(ctx, { username: 'alice', usernameLower: 'alice', host: null });
+ const bob = await createUser(ctx, { username: 'bob', usernameLower: 'bob', host: null });
+
+ const aliceNote = await createNote(ctx, alice, { text: 'hello alice', visibility: 'public' });
+ await createNote(ctx, bob, { text: 'hello bob', visibility: 'public' });
+
+ const result = await ctx.service.searchNote('hello', me, { userId: alice.id }, { limit: 10 });
+ expect(result.map(note => note.id)).toEqual([aliceNote.id]);
+ });
+
+ test('filters by channelId', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const author = await createUser(ctx, { username: 'author', usernameLower: 'author', host: null });
+ const channelA = await createChannel(ctx, author, { name: 'channel-a' });
+ const channelB = await createChannel(ctx, author, { name: 'channel-b' });
+
+ const channelNote = await createNote(ctx, author, { text: 'hello channel', channelId: channelA.id, visibility: 'public' });
+ await createNote(ctx, author, { text: 'hello other', channelId: channelB.id, visibility: 'public' });
+
+ const result = await ctx.service.searchNote('hello', me, { channelId: channelA.id }, { limit: 10 });
+ expect(result.map(note => note.id)).toEqual([channelNote.id]);
+ });
+
+ test('filters by host', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const local = await createUser(ctx, { username: 'local', usernameLower: 'local', host: null });
+ const remote = await createUser(ctx, { username: 'remote', usernameLower: 'remote', host: 'example.com' });
+
+ const localNote = await createNote(ctx, local, { text: 'hello local', visibility: 'public' });
+ const remoteNote = await createNote(ctx, remote, { text: 'hello remote', visibility: 'public', userHost: 'example.com' });
+
+ const localResult = await ctx.service.searchNote('hello', me, { host: '.' }, { limit: 10 });
+ expect(localResult.map(note => note.id)).toEqual([localNote.id]);
+
+ const remoteResult = await ctx.service.searchNote('hello', me, { host: 'example.com' }, { limit: 10 });
+ expect(remoteResult.map(note => note.id)).toEqual([remoteNote.id]);
+ });
+
+ describe('muting and blocking', () => {
+ test('filters out muted users', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const muted = await createUser(ctx, { username: 'muted', usernameLower: 'muted', host: null });
+ const other = await createUser(ctx, { username: 'other', usernameLower: 'other', host: null });
+
+ await createNote(ctx, muted, { text: 'hello muted', visibility: 'public' });
+ const otherNote = await createNote(ctx, other, { text: 'hello other', visibility: 'public' });
+
+ await createMuting(ctx, me, muted);
+
+ const result = await ctx.service.searchNote('hello', me, {}, { limit: 10 });
+
+ expect(result.map(note => note.id)).toEqual([otherNote.id]);
+ });
+
+ test('filters out users who block me', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const blocker = await createUser(ctx, { username: 'blocker', usernameLower: 'blocker', host: null });
+ const other = await createUser(ctx, { username: 'other', usernameLower: 'other', host: null });
+
+ await createNote(ctx, blocker, { text: 'hello blocker', visibility: 'public' });
+ const otherNote = await createNote(ctx, other, { text: 'hello other', visibility: 'public' });
+
+ await createBlocking(ctx, blocker, me);
+
+ const result = await ctx.service.searchNote('hello', me, {}, { limit: 10 });
+
+ expect(result.map(note => note.id)).toEqual([otherNote.id]);
+ });
+
+ test('filters no out users I block', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const blocked = await createUser(ctx, { username: 'blocked', usernameLower: 'blocked', host: null });
+ const other = await createUser(ctx, { username: 'other', usernameLower: 'other', host: null });
+
+ const blockedNote = await createNote(ctx, blocked, { text: 'hello blocked', visibility: 'public' });
+ const otherNote = await createNote(ctx, other, { text: 'hello other', visibility: 'public' });
+
+ await createBlocking(ctx, me, blocked);
+
+ const result = await ctx.service.searchNote('hello', me, {}, { limit: 10 });
+ expect(result.map(note => note.id).sort()).toEqual([otherNote.id, blockedNote.id].sort());
+ });
+ });
+
+ describe('pagination', () => {
+ test('paginates with sinceId', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const author = await createUser(ctx, { username: 'author', usernameLower: 'author', host: null });
+
+ const t1 = Date.now() - 3000;
+ const t2 = Date.now() - 2000;
+ const t3 = Date.now() - 1000;
+
+ const note1 = await createNote(ctx, author, { text: 'hello' }, t1);
+ const note2 = await createNote(ctx, author, { text: 'hello' }, t2);
+ const note3 = await createNote(ctx, author, { text: 'hello' }, t3);
+
+ const result = await ctx.service.searchNote('hello', me, {}, { limit: 10, sinceId: note1.id });
+
+ const expected = sinceIdOrder === 'asc'
+ ? [note2.id, note3.id]
+ : [note3.id, note2.id];
+ expect(result.map(note => note.id)).toEqual(expected);
+ });
+
+ test('paginates with untilId', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const author = await createUser(ctx, { username: 'author', usernameLower: 'author', host: null });
+
+ const t1 = Date.now() - 3000;
+ const t2 = Date.now() - 2000;
+ const t3 = Date.now() - 1000;
+
+ const note1 = await createNote(ctx, author, { text: 'hello' }, t1);
+ const note2 = await createNote(ctx, author, { text: 'hello' }, t2);
+ const note3 = await createNote(ctx, author, { text: 'hello' }, t3);
+
+ const result = await ctx.service.searchNote('hello', me, {}, { limit: 10, untilId: note3.id });
+
+ expect(result.map(note => note.id)).toEqual([note2.id, note1.id]);
+ });
+
+ test('paginates with sinceId and untilId together', async () => {
+ const ctx = getCtx();
+ const me = await createUser(ctx, { username: 'me', usernameLower: 'me', host: null });
+ const author = await createUser(ctx, { username: 'author', usernameLower: 'author', host: null });
+
+ const t1 = Date.now() - 4000;
+ const t2 = Date.now() - 3000;
+ const t3 = Date.now() - 2000;
+ const t4 = Date.now() - 1000;
+
+ const note1 = await createNote(ctx, author, { text: 'hello' }, t1);
+ const note2 = await createNote(ctx, author, { text: 'hello' }, t2);
+ const note3 = await createNote(ctx, author, { text: 'hello' }, t3);
+ const note4 = await createNote(ctx, author, { text: 'hello' }, t4);
+
+ const result = await ctx.service.searchNote('hello', me, {}, { limit: 10, sinceId: note1.id, untilId: note4.id });
+
+ expect(result.map(note => note.id)).toEqual([note3.id, note2.id]);
+ });
+ });
+ });
+ }
+
+ describe('sqlLike', () => {
+ let ctx: TestContext;
+
+ beforeAll(async () => {
+ ctx = await buildContext();
+ });
+
+ afterAll(async () => {
+ await ctx.app.close();
+ });
+
+ afterEach(async () => {
+ await cleanupContext(ctx);
+ });
+
+ defineSearchNoteTests(() => ctx, { supportsFollowersVisibility: true, sinceIdOrder: 'asc' });
+ });
+
+ describe('meilisearch', () => {
+ let ctx: TestContext;
+ let meilisearch: MeiliSearch;
+ let meilisearchIndex: Index;
+ let meiliConfig: Config;
+
+ beforeAll(async () => {
+ const baseConfig = loadConfig();
+ meiliConfig = {
+ ...baseConfig,
+ fulltextSearch: {
+ provider: 'meilisearch',
+ },
+ meilisearch: {
+ host: '127.0.0.1',
+ port: '57712',
+ apiKey: '',
+ index: 'test-search-service',
+ scope: 'global',
+ ssl: false,
+ },
+ };
+
+ ctx = await buildContext(meiliConfig);
+ meilisearch = ctx.app.get(DI.meilisearch) as MeiliSearch;
+ meilisearchIndex = meilisearch.index(`${meiliConfig.meilisearch!.index}---notes`);
+
+ const settingsTask = await meilisearchIndex.updateSettings(meilisearchSettings);
+ await meilisearch.tasks.waitForTask(settingsTask.taskUid);
+
+ const clearTask = await meilisearchIndex.deleteAllDocuments();
+ await meilisearch.tasks.waitForTask(clearTask.taskUid);
+
+ ctx.indexer = async (note: MiNote) => {
+ if (note.text == null && note.cw == null) return;
+ if (!['home', 'public'].includes(note.visibility)) return;
+ if (meiliConfig.meilisearch?.scope === 'local' && note.userHost != null) return;
+
+ const task = await meilisearchIndex.addDocuments([{
+ id: note.id,
+ createdAt: ctx.idService.parse(note.id).date.getTime(),
+ userId: note.userId,
+ userHost: note.userHost,
+ channelId: note.channelId,
+ cw: note.cw,
+ text: note.text,
+ tags: note.tags,
+ }], {
+ primaryKey: 'id',
+ });
+ await meilisearch.tasks.waitForTask(task.taskUid);
+ };
+ });
+
+ afterAll(async () => {
+ await ctx.app.close();
+ });
+
+ afterEach(async () => {
+ await cleanupContext(ctx);
+ const clearTask = await meilisearchIndex.deleteAllDocuments();
+ await meilisearch.tasks.waitForTask(clearTask.taskUid);
+ });
+
+ defineSearchNoteTests(() => ctx, { supportsFollowersVisibility: false, sinceIdOrder: 'desc' });
+ });
+});
diff --git a/packages/backend/test/unit/entities/DriveFileEntityService.ts b/packages/backend/test/unit/entities/DriveFileEntityService.ts
new file mode 100644
index 0000000000..2e416326ee
--- /dev/null
+++ b/packages/backend/test/unit/entities/DriveFileEntityService.ts
@@ -0,0 +1,227 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+process.env.NODE_ENV = 'test';
+
+import { afterAll, beforeAll, beforeEach, describe, expect, jest, test } from '@jest/globals';
+import { Test } from '@nestjs/testing';
+import type { TestingModule } from '@nestjs/testing';
+import type { DriveFilesRepository, DriveFoldersRepository, UsersRepository } from '@/models/_.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { CoreModule } from '@/core/CoreModule.js';
+import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
+import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { DI } from '@/di-symbols.js';
+import { genAidx } from '@/misc/id/aidx.js';
+import { secureRndstr } from '@/misc/secure-rndstr.js';
+
+const describeBenchmark = process.env.RUN_BENCHMARKS === '1' ? describe : describe.skip;
+
+describe('DriveFileEntityService', () => {
+ let app: TestingModule;
+ let service: DriveFileEntityService;
+ let driveFolderEntityService: DriveFolderEntityService;
+ let driveFilesRepository: DriveFilesRepository;
+ let driveFoldersRepository: DriveFoldersRepository;
+ let usersRepository: UsersRepository;
+ let idCounter = 0;
+
+ const userEntityServiceMock = {
+ packMany: jest.fn(async (users: Array<string | { id: string }>) => {
+ return users.map(u => ({
+ id: typeof u === 'string' ? u : u.id,
+ username: 'user',
+ }));
+ }),
+ pack: jest.fn(async (user: string | { id: string }) => {
+ return {
+ id: typeof user === 'string' ? user : user.id,
+ username: 'user',
+ };
+ }),
+ };
+
+ const nextId = () => genAidx(Date.now() + (idCounter++));
+
+ const createUser = async () => {
+ const un = secureRndstr(16);
+ const id = nextId();
+ await usersRepository.insert({
+ id,
+ username: un,
+ usernameLower: un.toLowerCase(),
+ });
+ return usersRepository.findOneByOrFail({ id });
+ };
+
+ const createFolder = async (name: string, parentId: string | null) => {
+ const id = nextId();
+ await driveFoldersRepository.insert({
+ id,
+ name,
+ userId: null,
+ parentId,
+ });
+ return driveFoldersRepository.findOneByOrFail({ id });
+ };
+
+ const createFile = async (folderId: string | null, userId: string | null) => {
+ const id = nextId();
+ await driveFilesRepository.insert({
+ id,
+ userId,
+ userHost: null,
+ md5: secureRndstr(32),
+ name: `file-${id}`,
+ type: 'text/plain',
+ size: 1,
+ comment: null,
+ blurhash: null,
+ properties: {},
+ storedInternal: true,
+ url: `https://example.com/${id}`,
+ thumbnailUrl: null,
+ webpublicUrl: null,
+ webpublicType: null,
+ accessKey: null,
+ thumbnailAccessKey: null,
+ webpublicAccessKey: null,
+ uri: null,
+ src: null,
+ folderId,
+ isSensitive: false,
+ maybeSensitive: false,
+ maybePorn: false,
+ isLink: false,
+ requestHeaders: null,
+ requestIp: null,
+ });
+ return driveFilesRepository.findOneByOrFail({ id });
+ };
+
+ beforeAll(async () => {
+ const moduleBuilder = Test.createTestingModule({
+ imports: [GlobalModule, CoreModule],
+ });
+ moduleBuilder.overrideProvider(UserEntityService).useValue(userEntityServiceMock as any);
+
+ app = await moduleBuilder.compile();
+ await app.init();
+ app.enableShutdownHooks();
+
+ service = app.get<DriveFileEntityService>(DriveFileEntityService);
+ driveFolderEntityService = app.get<DriveFolderEntityService>(DriveFolderEntityService);
+ driveFilesRepository = app.get<DriveFilesRepository>(DI.driveFilesRepository);
+ driveFoldersRepository = app.get<DriveFoldersRepository>(DI.driveFoldersRepository);
+ usersRepository = app.get<UsersRepository>(DI.usersRepository);
+ });
+
+ beforeEach(() => {
+ userEntityServiceMock.packMany.mockClear();
+ userEntityServiceMock.pack.mockClear();
+ });
+
+ afterAll(async () => {
+ await app.close();
+ });
+
+ describe('pack', () => {
+ test('detail: false', async () => {
+ const user = await createUser();
+ const folder = await createFolder('pack-root', null);
+ const file = await createFile(folder.id, user.id);
+
+ const packed = await service.pack(file, { detail: false, self: true }) as any;
+ expect(packed.id).toBe(file.id);
+ expect(packed.folder).toBeNull();
+ expect(packed.user).toBeNull();
+ expect(packed.userId).toBeNull();
+ });
+
+ test('detail: true', async () => {
+ const folder = await createFolder('pack-parent', null);
+ const child = await createFolder('pack-child', folder.id);
+ const file = await createFile(child.id, null);
+
+ const packed = await service.pack(file, { detail: true, self: true }) as any;
+ expect(packed.folder?.id).toBe(child.id);
+ expect(packed.folder?.parent?.id).toBe(folder.id);
+ });
+ });
+
+ describe('packNullable', () => {
+ test('returns null for missing', async () => {
+ const packed = await service.packNullable('non-existent' as any, { detail: false });
+ expect(packed).toBeNull();
+ });
+
+ test('uses packedUser hint when withUser', async () => {
+ const user = await createUser();
+ const file = await createFile(null, user.id);
+
+ const packed = await service.packNullable(file, { withUser: true, self: true }, {
+ packedUser: { id: user.id, username: 'hint' } as any,
+ });
+ expect(packed?.user?.id).toBe(user.id);
+ expect(packed?.user?.username).toBe('hint');
+ });
+ });
+
+ describe('packMany', () => {
+ test('withUser: true uses deduped packMany', async () => {
+ const user = await createUser();
+ const fileA = await createFile(null, user.id);
+ const fileB = await createFile(null, user.id);
+
+ const packed = await service.packMany([fileA, fileB], { withUser: true, self: true });
+ expect(packed.length).toBe(2);
+ expect(userEntityServiceMock.packMany).toHaveBeenCalledTimes(1);
+ expect(userEntityServiceMock.packMany.mock.calls[0]?.[0]?.length).toBe(1);
+ expect(packed[0]?.user?.id).toBe(user.id);
+ });
+
+ test('detail: true packs folder', async () => {
+ const folder = await createFolder('packmany-root', null);
+ const file = await createFile(folder.id, null);
+
+ const packed = await service.packMany([file], { detail: true, self: true });
+ expect(packed[0]?.folder?.id).toBe(folder.id);
+ expect(packed[0]?.folder?.parent).toBeUndefined();
+ });
+
+ test('detail: true uses DriveFolderEntityService pack', async () => {
+ const folder = await createFolder('packmany-folder', null);
+ const file = await createFile(folder.id, null);
+ const packSpy = jest.spyOn(driveFolderEntityService, 'pack');
+
+ await service.packMany([file], { detail: true, self: true });
+ expect(packSpy).toHaveBeenCalled();
+ packSpy.mockRestore();
+ });
+ });
+
+ describeBenchmark('benchmark', () => {
+ test('packMany', async () => {
+ const user = await createUser();
+ const folders = [];
+ for (let i = 0; i < 100; i++) {
+ folders.push(await createFolder(`bench-${i}`, null));
+ }
+ const files = [];
+ for (const folder of folders) {
+ for (let j = 0; j < 20; j++) {
+ files.push(await createFile(folder.id, user.id));
+ }
+ }
+
+ const start = Date.now();
+ await service.packMany(files, { detail: true, withUser: true, self: true });
+ const elapsed = Date.now() - start;
+
+ console.log(`DriveFileEntityService.packMany benchmark: ${elapsed}ms`);
+ });
+ });
+});
diff --git a/packages/backend/test/unit/entities/DriveFolderEntityService.ts b/packages/backend/test/unit/entities/DriveFolderEntityService.ts
new file mode 100644
index 0000000000..299ee5f42b
--- /dev/null
+++ b/packages/backend/test/unit/entities/DriveFolderEntityService.ts
@@ -0,0 +1,171 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+process.env.NODE_ENV = 'test';
+
+import { afterAll, beforeAll, describe, expect, test } from '@jest/globals';
+import { Test } from '@nestjs/testing';
+import type { TestingModule } from '@nestjs/testing';
+import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/_.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { CoreModule } from '@/core/CoreModule.js';
+import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js';
+import { DI } from '@/di-symbols.js';
+import { genAidx } from '@/misc/id/aidx.js';
+import { secureRndstr } from '@/misc/secure-rndstr.js';
+
+const describeBenchmark = process.env.RUN_BENCHMARKS === '1' ? describe : describe.skip;
+
+describe('DriveFolderEntityService', () => {
+ let app: TestingModule;
+ let service: DriveFolderEntityService;
+ let driveFoldersRepository: DriveFoldersRepository;
+ let driveFilesRepository: DriveFilesRepository;
+ let idCounter = 0;
+
+ const nextId = () => genAidx(Date.now() + (idCounter++));
+
+ const createFolder = async (name: string, parentId: string | null) => {
+ const id = nextId();
+ await driveFoldersRepository.insert({
+ id,
+ name,
+ userId: null,
+ parentId,
+ });
+ return driveFoldersRepository.findOneByOrFail({ id });
+ };
+
+ const createFile = async (folderId: string | null) => {
+ const id = nextId();
+ await driveFilesRepository.insert({
+ id,
+ userId: null,
+ userHost: null,
+ md5: secureRndstr(32),
+ name: `file-${id}`,
+ type: 'text/plain',
+ size: 1,
+ comment: null,
+ blurhash: null,
+ properties: {},
+ storedInternal: true,
+ url: `https://example.com/${id}`,
+ thumbnailUrl: null,
+ webpublicUrl: null,
+ webpublicType: null,
+ accessKey: null,
+ thumbnailAccessKey: null,
+ webpublicAccessKey: null,
+ uri: null,
+ src: null,
+ folderId,
+ isSensitive: false,
+ maybeSensitive: false,
+ maybePorn: false,
+ isLink: false,
+ requestHeaders: null,
+ requestIp: null,
+ });
+ };
+
+ beforeAll(async () => {
+ app = await Test.createTestingModule({
+ imports: [GlobalModule, CoreModule],
+ }).compile();
+ await app.init();
+ app.enableShutdownHooks();
+
+ service = app.get<DriveFolderEntityService>(DriveFolderEntityService);
+ driveFoldersRepository = app.get<DriveFoldersRepository>(DI.driveFoldersRepository);
+ driveFilesRepository = app.get<DriveFilesRepository>(DI.driveFilesRepository);
+ });
+
+ afterAll(async () => {
+ await app.close();
+ });
+
+ describe('pack', () => {
+ test('detail: false', async () => {
+ const root = await createFolder('root', null);
+ const child = await createFolder('child', root.id);
+
+ const packed = await service.pack(child, { detail: false }) as any;
+ expect(packed.id).toBe(child.id);
+ expect(packed.parentId).toBe(root.id);
+ expect(packed.parent).toBeUndefined();
+ expect(packed.foldersCount).toBeUndefined();
+ expect(packed.filesCount).toBeUndefined();
+ });
+
+ test('detail: true', async () => {
+ const root = await createFolder('root-detail', null);
+ const child = await createFolder('child-detail', root.id);
+ await createFolder('grandchild-detail', child.id);
+ await createFile(child.id);
+ await createFile(child.id);
+
+ const packed = await service.pack(child, { detail: true }) as any;
+ expect(packed.id).toBe(child.id);
+ expect(packed.foldersCount).toBe(1);
+ expect(packed.filesCount).toBe(2);
+ expect(packed.parent?.id).toBe(root.id);
+ expect(packed.parent?.parent).toBeUndefined();
+ });
+
+ test('detail: true reaches root for deep hierarchy', async () => {
+ const root = await createFolder('root-deep', null);
+ const level1 = await createFolder('level-1', root.id);
+ const level2 = await createFolder('level-2', level1.id);
+ const level3 = await createFolder('level-3', level2.id);
+ const level4 = await createFolder('level-4', level3.id);
+ const level5 = await createFolder('level-5', level4.id);
+
+ const packed = await service.pack(level5, { detail: true }) as any;
+ expect(packed.id).toBe(level5.id);
+ expect(packed.parent?.id).toBe(level4.id);
+ expect(packed.parent?.parent?.id).toBe(level3.id);
+ expect(packed.parent?.parent?.parent?.id).toBe(level2.id);
+ expect(packed.parent?.parent?.parent?.parent?.id).toBe(level1.id);
+ expect(packed.parent?.parent?.parent?.parent?.parent?.id).toBe(root.id);
+ expect(packed.parent?.parent?.parent?.parent?.parent?.parent).toBeUndefined();
+ });
+ });
+
+ describe('packMany', () => {
+ test('preserves order and packs parents', async () => {
+ const root = await createFolder('root-many', null);
+ const childA = await createFolder('child-a', root.id);
+ const childB = await createFolder('child-b', root.id);
+ await createFolder('child-a-sub', childA.id);
+ await createFile(childA.id);
+
+ const packed = await service.packMany([childB, childA], { detail: true }) as any;
+ expect(packed[0].id).toBe(childB.id);
+ expect(packed[1].id).toBe(childA.id);
+ expect(packed[0].parent?.id).toBe(root.id);
+ expect(packed[1].parent?.id).toBe(root.id);
+ expect(packed[0].filesCount).toBe(0);
+ expect(packed[1].filesCount).toBe(1);
+ expect(packed[0].foldersCount).toBe(0);
+ expect(packed[1].foldersCount).toBe(1);
+ });
+ });
+
+ describeBenchmark('benchmark', () => {
+ test('packMany', async () => {
+ const root = await createFolder('bench-root', null);
+ const folders = [];
+ for (let i = 0; i < 200; i++) {
+ folders.push(await createFolder(`bench-${i}`, root.id));
+ }
+
+ const start = Date.now();
+ await service.packMany(folders, { detail: true });
+ const elapsed = Date.now() - start;
+ console.log(`DriveFolderEntityService.packMany benchmark: ${elapsed}ms`);
+ });
+ });
+});
diff --git a/packages/backend/test/unit/server/FileServerService.ts b/packages/backend/test/unit/server/FileServerService.ts
new file mode 100644
index 0000000000..c88175c5c7
--- /dev/null
+++ b/packages/backend/test/unit/server/FileServerService.ts
@@ -0,0 +1,770 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import fastifyStatic from '@fastify/static';
+import Fastify, { type FastifyInstance } from 'fastify';
+import { describe, expect, test } from '@jest/globals';
+import sharp from 'sharp';
+import { DataSource, type Repository } from 'typeorm';
+import { initTestDb, randomString } from '../../utils.js';
+import type { AiService } from '@/core/AiService.js';
+import { DownloadService } from '@/core/DownloadService.js';
+import { FileInfoService } from '@/core/FileInfoService.js';
+import { HttpRequestService } from '@/core/HttpRequestService.js';
+import { ImageProcessingService } from '@/core/ImageProcessingService.js';
+import { InternalStorageService } from '@/core/InternalStorageService.js';
+import { IdService } from '@/core/IdService.js';
+import { LoggerService } from '@/core/LoggerService.js';
+import { VideoProcessingService } from '@/core/VideoProcessingService.js';
+import { loadConfig, type Config } from '@/config.js';
+import { MiDriveFile } from '@/models/DriveFile.js';
+import { FileServerService } from '@/server/FileServerService.js';
+
+const dummyPath = path.resolve('test/resources/dummy-for-file-server-service.png');
+const dummySize = fs.statSync(dummyPath).size;
+const dummyBuffer = fs.readFileSync(dummyPath);
+const svgBuffer = Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8"></svg>', 'utf8');
+const textBuffer = Buffer.from('dummy text', 'utf8');
+
+async function createRemoteFileServer() {
+ const flatPngBuffer = await sharp({
+ create: { width: 8, height: 8, channels: 3, background: { r: 0, g: 0, b: 0 } },
+ }).png().toBuffer();
+ const server = Fastify();
+
+ server.get('/dummy.png', async (_request, reply) => {
+ reply.header('Content-Type', 'image/png');
+ reply.header('Content-Length', String(dummyBuffer.length));
+ return reply.send(dummyBuffer);
+ });
+
+ server.get('/dummy.svg', async (_request, reply) => {
+ reply.header('Content-Type', 'image/svg+xml');
+ reply.header('Content-Length', String(svgBuffer.length));
+ return reply.send(svgBuffer);
+ });
+
+ server.get('/dummy.txt', async (_request, reply) => {
+ reply.header('Content-Type', 'text/plain');
+ reply.header('Content-Length', String(textBuffer.length));
+ return reply.send(textBuffer);
+ });
+
+ server.get('/flat.png', async (_request, reply) => {
+ reply.header('Content-Type', 'image/png');
+ reply.header('Content-Length', String(flatPngBuffer.length));
+ return reply.send(flatPngBuffer);
+ });
+
+ const baseUrl = await server.listen({ port: 0, host: '127.0.0.1' });
+
+ return {
+ server,
+ pngUrl: `${baseUrl}/dummy.png`,
+ svgUrl: `${baseUrl}/dummy.svg`,
+ textUrl: `${baseUrl}/dummy.txt`,
+ flatPngUrl: `${baseUrl}/flat.png`,
+ };
+}
+
+describe('FileServerService', () => {
+ let db: DataSource;
+ let fastify: FastifyInstance;
+ let externalFastify: FastifyInstance;
+ let driveFilesRepository: Repository<MiDriveFile>;
+ let internalStorageService: InternalStorageService;
+ let idService: IdService;
+ let config: Config;
+ let fileServerService: FileServerService;
+ let externalFileServerService: FileServerService;
+ let remoteServer: FastifyInstance;
+ let remotePngUrl: string;
+ let remoteSvgUrl: string;
+ let remoteTextUrl: string;
+ let remoteFlatPngUrl: string;
+ const storedPaths: string[] = [];
+ let createdFallbackAssets = false;
+ let fallbackAssetsDir = '';
+
+ function writeInternalFile(key: string) {
+ const dest = internalStorageService.resolvePath(key);
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
+ fs.copyFileSync(dummyPath, dest);
+ storedPaths.push(dest);
+ }
+
+ async function insertDriveFile(params: {
+ accessKey: string;
+ thumbnailAccessKey?: string | null;
+ webpublicAccessKey?: string | null;
+ storedInternal: boolean;
+ isLink: boolean;
+ uri?: string | null;
+ name?: string;
+ type?: string;
+ size?: number;
+ }) {
+ const accessKey = params.accessKey;
+ const url = params.uri ?? `${config.url}/files/${accessKey}`;
+ await driveFilesRepository.insert({
+ id: idService.gen(),
+ userId: null,
+ userHost: null,
+ md5: '00000000000000000000000000000000',
+ name: params.name ?? 'dummy.png',
+ type: params.type ?? 'image/png',
+ size: params.size ?? dummySize,
+ comment: null,
+ blurhash: null,
+ properties: {},
+ storedInternal: params.storedInternal,
+ url,
+ thumbnailUrl: null,
+ webpublicUrl: null,
+ webpublicType: null,
+ accessKey,
+ thumbnailAccessKey: params.thumbnailAccessKey ?? null,
+ webpublicAccessKey: params.webpublicAccessKey ?? null,
+ uri: params.uri ?? null,
+ src: null,
+ folderId: null,
+ isSensitive: false,
+ maybeSensitive: false,
+ maybePorn: false,
+ isLink: params.isLink,
+ requestHeaders: {},
+ requestIp: null,
+ });
+ }
+
+ beforeAll(async () => {
+ config = loadConfig();
+ db = await initTestDb(false);
+ driveFilesRepository = db.getRepository(MiDriveFile);
+
+ const loggerService = new LoggerService();
+ const aiService = {
+ detectSensitive: async () => null,
+ } as unknown as AiService;
+ const fileInfoService = new FileInfoService(aiService, loggerService);
+ const httpRequestService = new HttpRequestService(config);
+ const downloadService = new DownloadService(config, httpRequestService, loggerService);
+ const imageProcessingService = new ImageProcessingService();
+ const videoProcessingService = new VideoProcessingService(config, imageProcessingService);
+ internalStorageService = new InternalStorageService(config);
+ idService = new IdService(config);
+ fileServerService = new FileServerService(
+ config,
+ driveFilesRepository as any,
+ fileInfoService,
+ downloadService,
+ imageProcessingService,
+ videoProcessingService,
+ internalStorageService,
+ loggerService,
+ );
+
+ fastify = Fastify();
+ await fastify.register(fastifyStatic, {
+ root: path.resolve('src/server/assets'),
+ serve: false,
+ });
+ fileServerService.createServer(fastify, {}, () => {});
+ await fastify.ready();
+
+ const externalConfig = {
+ ...config,
+ mediaProxy: 'https://media-proxy.test',
+ externalMediaProxyEnabled: true,
+ } as Config;
+ externalFileServerService = new FileServerService(
+ externalConfig,
+ driveFilesRepository as any,
+ fileInfoService,
+ downloadService,
+ imageProcessingService,
+ videoProcessingService,
+ internalStorageService,
+ loggerService,
+ );
+ externalFastify = Fastify();
+ await externalFastify.register(fastifyStatic, {
+ root: path.resolve('src/server/assets'),
+ serve: false,
+ });
+ externalFileServerService.createServer(externalFastify, {}, () => {});
+ await externalFastify.ready();
+
+ const remoteServerInfo = await createRemoteFileServer();
+ remoteServer = remoteServerInfo.server;
+ remotePngUrl = remoteServerInfo.pngUrl;
+ remoteSvgUrl = remoteServerInfo.svgUrl;
+ remoteTextUrl = remoteServerInfo.textUrl;
+ remoteFlatPngUrl = remoteServerInfo.flatPngUrl;
+
+ fallbackAssetsDir = path.resolve('src/server/file/assets');
+ if (!fs.existsSync(fallbackAssetsDir)) {
+ fs.mkdirSync(fallbackAssetsDir, { recursive: true });
+ fs.copyFileSync(dummyPath, path.join(fallbackAssetsDir, 'dummy.png'));
+ createdFallbackAssets = true;
+ }
+ });
+
+ afterEach(async () => {
+ await driveFilesRepository.createQueryBuilder().delete().execute();
+ for (const filePath of storedPaths) {
+ try {
+ fs.unlinkSync(filePath);
+ } catch {
+ // NOP
+ }
+ }
+ storedPaths.length = 0;
+ });
+
+ afterAll(async () => {
+ await fastify.close();
+ await externalFastify.close();
+ await remoteServer.close();
+ await db.destroy();
+ if (createdFallbackAssets) {
+ fs.rmSync(fallbackAssetsDir, { recursive: true, force: true });
+ }
+ });
+
+ describe('GET /files/app-default.jpg', () => {
+ test('GET /files/app-default.jpg ヘッダを検証する', async () => {
+ const prevNodeEnv = process.env.NODE_ENV;
+ process.env.NODE_ENV = 'test';
+
+ try {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/files/app-default.jpg',
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-security-policy']).toBe('default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-type']).toBe('image/jpeg');
+ expect(res.headers['access-control-allow-origin']).toBeUndefined();
+ } finally {
+ process.env.NODE_ENV = prevNodeEnv;
+ }
+ });
+
+ test('GET /files/app-default.jpg development で CORS を許可する', async () => {
+ const prevNodeEnv = process.env.NODE_ENV;
+ process.env.NODE_ENV = 'development';
+
+ try {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/files/app-default.jpg',
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['access-control-allow-origin']).toBe('*');
+ } finally {
+ process.env.NODE_ENV = prevNodeEnv;
+ }
+ });
+
+ test('GET /files/app-default.jpg?x=1 クエリを除去してリダイレクトする', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/files/app-default.jpg?x=1',
+ });
+
+ expect(res.statusCode).toBe(301);
+ expect(res.headers.location).toBe('/files/app-default.jpg');
+ expect(res.headers['content-security-policy']).toBe('default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
+ });
+ });
+
+ describe('GET /files/:key', () => {
+ test('GET /files/:key 404 のときダミー画像を返す', async () => {
+ const accessKey = randomString();
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ });
+
+ expect(res.statusCode).toBe(404);
+ expect(res.headers['cache-control']).toBe('max-age=86400');
+ });
+
+ test('GET /files/:key 画像配信ヘッダを検証する', async () => {
+ const accessKey = randomString();
+ writeInternalFile(accessKey);
+ await insertDriveFile({
+ accessKey,
+ storedInternal: true,
+ isLink: false,
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-security-policy']).toBe('default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['content-length']).toBe(String(dummySize));
+ expect(res.headers['content-disposition'] ?? '').toMatch(/^inline;/);
+ });
+
+ test('GET /files/:key Range で部分配信する', async () => {
+ const accessKey = randomString();
+ writeInternalFile(accessKey);
+ await insertDriveFile({
+ accessKey,
+ storedInternal: true,
+ isLink: false,
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ headers: {
+ range: 'bytes=0-3',
+ },
+ });
+
+ expect(res.statusCode).toBe(206);
+ expect(res.headers['content-range']).toBe(`bytes 0-3/${dummySize}`);
+ expect(res.headers['accept-ranges']).toBe('bytes');
+ expect(res.headers['content-length']).toBe('4');
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ });
+
+ test('GET /files/:key Range の終端を補正する', async () => {
+ const accessKey = randomString();
+ writeInternalFile(accessKey);
+ await insertDriveFile({
+ accessKey,
+ storedInternal: true,
+ isLink: false,
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ headers: {
+ range: 'bytes=0-999999',
+ },
+ });
+
+ expect(res.statusCode).toBe(206);
+ expect(res.headers['content-range']).toBe(`bytes 0-${dummySize - 1}/${dummySize}`);
+ expect(res.headers['accept-ranges']).toBe('bytes');
+ expect(res.headers['content-length']).toBe(String(dummySize));
+ });
+
+ test('GET /files/:key thumbnail の Range で部分配信する', async () => {
+ const accessKey = randomString();
+ const thumbnailKey = randomString();
+ writeInternalFile(thumbnailKey);
+ await insertDriveFile({
+ accessKey,
+ thumbnailAccessKey: thumbnailKey,
+ storedInternal: true,
+ isLink: false,
+ name: 'sample.png',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${thumbnailKey}`,
+ headers: {
+ range: 'bytes=0-3',
+ },
+ });
+
+ expect(res.statusCode).toBe(206);
+ expect(res.headers['content-range']).toBe(`bytes 0-3/${dummySize}`);
+ expect(res.headers['accept-ranges']).toBe('bytes');
+ expect(res.headers['content-length']).toBe('4');
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ });
+
+ test('GET /files/:key thumbnail のファイル名を整形する', async () => {
+ const accessKey = randomString();
+ const thumbnailKey = randomString();
+ writeInternalFile(thumbnailKey);
+ await insertDriveFile({
+ accessKey,
+ thumbnailAccessKey: thumbnailKey,
+ storedInternal: true,
+ isLink: false,
+ name: 'sample.png',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${thumbnailKey}`,
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('sample-thumb.png');
+ });
+
+ test('GET /files/:key webpublic のファイル名を整形する', async () => {
+ const accessKey = randomString();
+ const webpublicKey = randomString();
+ writeInternalFile(webpublicKey);
+ await insertDriveFile({
+ accessKey,
+ webpublicAccessKey: webpublicKey,
+ storedInternal: true,
+ isLink: false,
+ name: 'sample.png',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${webpublicKey}`,
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('sample-web.png');
+ });
+
+ test('GET /files/:key browsersafe でない MIME は octet-stream になる', async () => {
+ const accessKey = randomString();
+ writeInternalFile(accessKey);
+ await insertDriveFile({
+ accessKey,
+ storedInternal: true,
+ isLink: false,
+ type: 'application/x-msdownload',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('application/octet-stream');
+ });
+
+ test('GET /files/:key 204 のときキャッシュ制御を返す', async () => {
+ const accessKey = randomString();
+ await insertDriveFile({
+ accessKey,
+ storedInternal: false,
+ isLink: false,
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ });
+
+ expect(res.statusCode).toBe(204);
+ expect(res.headers['cache-control']).toBe('max-age=86400');
+ });
+
+ test('GET /files/:key 外部リンクを取得して配信する', async () => {
+ const accessKey = randomString();
+ await insertDriveFile({
+ accessKey,
+ storedInternal: false,
+ isLink: true,
+ uri: remotePngUrl,
+ name: 'remote.png',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-length']).toBe(String(dummyBuffer.length));
+ expect(res.headers['content-disposition'] ?? '').toContain('remote.png');
+ });
+
+ test('GET /files/:key 外部リンクを Range で部分配信する', async () => {
+ const accessKey = randomString();
+ await insertDriveFile({
+ accessKey,
+ storedInternal: false,
+ isLink: true,
+ uri: remotePngUrl,
+ name: 'remote.png',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${accessKey}`,
+ headers: {
+ range: 'bytes=0-3',
+ },
+ });
+
+ expect(res.statusCode).toBe(206);
+ expect(res.headers['content-range']).toBe(`bytes 0-3/${dummyBuffer.length}`);
+ expect(res.headers['accept-ranges']).toBe('bytes');
+ expect(res.headers['content-length']).toBe(String(dummyBuffer.length));
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ });
+
+ test('GET /files/:key thumbnail は mediaProxy/static.webp にリダイレクトする', async () => {
+ const accessKey = randomString();
+ const thumbnailKey = randomString();
+ await insertDriveFile({
+ accessKey,
+ thumbnailAccessKey: thumbnailKey,
+ storedInternal: false,
+ isLink: true,
+ uri: remotePngUrl,
+ name: 'remote.png',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${thumbnailKey}`,
+ });
+
+ expect(res.statusCode).toBe(301);
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers.location).toContain(`${config.mediaProxy}/static.webp`);
+ expect(res.headers.location).toContain('static=1');
+ });
+
+ test('GET /files/:key webpublic svg は mediaProxy/svg.webp にリダイレクトする', async () => {
+ const accessKey = randomString();
+ const webpublicKey = randomString();
+ await insertDriveFile({
+ accessKey,
+ webpublicAccessKey: webpublicKey,
+ storedInternal: false,
+ isLink: true,
+ uri: remoteSvgUrl,
+ name: 'vector.svg',
+ type: 'image/svg+xml',
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/files/${webpublicKey}`,
+ });
+
+ expect(res.statusCode).toBe(301);
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers.location).toContain(`${config.mediaProxy}/svg.webp`);
+ });
+ });
+
+ describe('GET /files/:key/*', () => {
+ test('GET /files/:key/* 正規の /files/:key にリダイレクトする', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/files/testkey/extra/path',
+ });
+
+ expect(res.statusCode).toBe(301);
+ expect(res.headers.location).toBe(`${config.url}/files/testkey`);
+ expect(res.headers['content-security-policy']).toBe('default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
+ });
+ });
+
+ describe('GET /proxy/:url*', () => {
+ test('GET /proxy/:url* 外部メディアプロキシへリダイレクトする', async () => {
+ const res = await externalFastify.inject({
+ method: 'GET',
+ url: '/proxy/path-part?url=https%3A%2F%2Fexample.com%2Fimg.png&static=1',
+ });
+
+ expect(res.statusCode).toBe(301);
+ expect(res.headers['cache-control']).toBe('public, max-age=259200');
+ expect(res.headers.location).toContain('https://media-proxy.test/');
+ expect(res.headers.location).toContain('url=https%3A%2F%2Fexample.com%2Fimg.png');
+ expect(res.headers.location).toContain('static=1');
+ expect(res.headers['content-security-policy']).toBe('default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
+ });
+
+ test('GET /proxy/:url* misskey User-Agent を拒否する', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/proxy/any?url=https%3A%2F%2Fexample.com%2Fimg.png',
+ headers: {
+ 'user-agent': 'misskey/1.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(403);
+ expect(res.headers['cache-control']).toBe('max-age=300');
+ });
+
+ test('GET /proxy/:url* origin 指定時は User-Agent 必須を検証する', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: '/proxy/any?url=https%3A%2F%2Fexample.com%2Fimg.png&origin=1',
+ headers: {
+ 'user-agent': '',
+ },
+ });
+
+ expect(res.statusCode).toBe(400);
+ expect(res.headers['cache-control']).toBe('max-age=300');
+ expect(res.headers.location).toBeUndefined();
+ expect(res.headers['content-security-policy']).toBe('default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
+ });
+
+ test('GET /proxy/:url* emoji 指定で非画像は 404 を返す', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remoteTextUrl)}&emoji=1`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(404);
+ expect(res.headers['cache-control']).toBe('max-age=300');
+ });
+
+ test('GET /proxy/:url* 非画像は 403 を返す', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remoteTextUrl)}`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(403);
+ expect(res.headers['cache-control']).toBe('max-age=300');
+ });
+
+ test('GET /proxy/:url* emoji static で webp を返す', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remotePngUrl)}&emoji=1&static=1`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/webp');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('dummy.png.webp');
+ });
+
+ test('GET /proxy/:url* avatar static で webp を返す', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remotePngUrl)}&avatar=1&static=1`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/webp');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('dummy.png.webp');
+ });
+
+ test('GET /proxy/:url* static で webp を返す', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remotePngUrl)}&static=1`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/webp');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('dummy.png.webp');
+ });
+
+ test('GET /proxy/:url* preview で webp を返す', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remotePngUrl)}&preview=1`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/webp');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('dummy.png.webp');
+ });
+
+ test('GET /proxy/:url* svg を webp に変換する', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remoteSvgUrl)}`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/webp');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('dummy.svg.webp');
+ });
+
+ test('GET /proxy/:url* badge で低エントロピー画像は 404 を返す', async () => {
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(remoteFlatPngUrl)}&badge=1`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(404);
+ expect(res.headers['cache-control']).toBe('max-age=300');
+ });
+
+ test('GET /proxy/:url* 画像をそのまま返す', async () => {
+ const accessKey = randomString();
+ writeInternalFile(accessKey);
+ await insertDriveFile({
+ accessKey,
+ storedInternal: true,
+ isLink: false,
+ });
+
+ const res = await fastify.inject({
+ method: 'GET',
+ url: `/proxy/any?url=${encodeURIComponent(`${config.url}/files/${accessKey}`)}&origin=1`,
+ headers: {
+ 'user-agent': 'Mozilla/5.0',
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.headers['content-type']).toBe('image/png');
+ expect(res.headers['cache-control']).toBe('max-age=31536000, immutable');
+ expect(res.headers['content-disposition'] ?? '').toContain('dummy.png');
+ });
+ });
+});
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index ecca28b5af..f91fb7f9b1 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -404,37 +404,28 @@ export function connectStream<C extends keyof misskey.Channels>(user: UserToken,
}
export const waitFire = async <C extends keyof misskey.Channels>(user: UserToken, channel: C, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: misskey.Channels[C]['params']) => {
- return new Promise<boolean>(async (res, rej) => {
- let timer: NodeJS.Timeout | null = null;
+ let ws: WebSocket | undefined;
- let ws: WebSocket;
- try {
- ws = await connectStream(user, channel, msg => {
+ try {
+ let callback: (msg: Record<string, unknown>) => void;
+ const receivedPromise = new Promise<boolean>((resolve) => {
+ callback = (msg: Record<string, unknown>) => {
if (cond(msg)) {
- ws.close();
- if (timer) clearTimeout(timer);
- res(true);
+ resolve(true);
}
- }, params);
- } catch (e) {
- rej(e);
- }
-
- if (!ws!) return;
+ };
+ });
- timer = setTimeout(() => {
- ws.close();
- res(false);
- }, 3000);
+ ws = await connectStream(user, channel, callback!, params);
+ await trgr();
- try {
- await trgr();
- } catch (e) {
- ws.close();
- if (timer) clearTimeout(timer);
- rej(e);
- }
- });
+ return await Promise.race([
+ receivedPromise,
+ new Promise<void>((r) => setTimeout(() => r(), 3000)).then(() => false),
+ ]);
+ } finally {
+ if (ws) ws.close();
+ }
};
/**
diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json
index 25584e475d..dac56f25de 100644
--- a/packages/backend/tsconfig.json
+++ b/packages/backend/tsconfig.json
@@ -26,7 +26,6 @@
"jsx": "react-jsx",
"jsxImportSource": "@kitajs/html",
"rootDir": "./src",
- "baseUrl": "./",
"paths": {
"@/*": ["./src/*"]
},
diff --git a/packages/frontend-builder/eslint.config.js b/packages/frontend-builder/eslint.config.js
index a13490c97f..7337abe14c 100644
--- a/packages/frontend-builder/eslint.config.js
+++ b/packages/frontend-builder/eslint.config.js
@@ -34,7 +34,6 @@ export default [
},
},
rules: {
- '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-empty-interface': ['error', {
allowSingleExtends: true,
}],
diff --git a/packages/frontend-builder/package.json b/packages/frontend-builder/package.json
index c1d9e316e6..622f4f40be 100644
--- a/packages/frontend-builder/package.json
+++ b/packages/frontend-builder/package.json
@@ -3,7 +3,7 @@
"type": "module",
"scripts": {
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint"
},
"exports": {
@@ -11,16 +11,15 @@
},
"devDependencies": {
"@types/estree": "1.0.8",
- "@types/node": "24.10.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
- "rollup": "4.53.3",
- "typescript": "5.9.3"
+ "@types/node": "24.10.13",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "rollup": "4.59.0"
},
"dependencies": {
"i18n": "workspace:*",
"estree-walker": "3.0.3",
"magic-string": "0.30.21",
- "vite": "7.2.7"
+ "vite": "7.3.1"
}
}
diff --git a/packages/frontend-builder/tsconfig.json b/packages/frontend-builder/tsconfig.json
index 9250b2f3da..ab943fded4 100644
--- a/packages/frontend-builder/tsconfig.json
+++ b/packages/frontend-builder/tsconfig.json
@@ -17,7 +17,6 @@
"noImplicitReturns": true,
"esModuleInterop": true,
"verbatimModuleSyntax": true,
- "baseUrl": ".",
"typeRoots": [
"./@types",
"./node_modules/@types"
diff --git a/packages/frontend-embed/eslint.config.js b/packages/frontend-embed/eslint.config.js
index 46247e40d5..d1ca70617b 100644
--- a/packages/frontend-embed/eslint.config.js
+++ b/packages/frontend-embed/eslint.config.js
@@ -41,7 +41,6 @@ export default [
},
},
rules: {
- '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-empty-interface': ['error', {
allowSingleExtends: true,
}],
@@ -145,7 +144,15 @@ export default [
'vue/return-in-computed-property': 'warn',
'vue/no-setup-props-reactivity-loss': 'warn',
'vue/max-attributes-per-line': 'off',
- 'vue/html-self-closing': 'off',
+ 'vue/html-self-closing': ['error', {
+ html: {
+ void: 'any',
+ normal: 'never',
+ component: 'any',
+ },
+ svg: 'any',
+ math: 'any',
+ }],
'vue/singleline-html-element-content-newline': 'off',
'vue/v-on-event-hyphenation': ['error', 'never', {
autofix: true,
diff --git a/packages/frontend-embed/package.json b/packages/frontend-embed/package.json
index 808559f44a..14a3f27601 100644
--- a/packages/frontend-embed/package.json
+++ b/packages/frontend-embed/package.json
@@ -11,27 +11,27 @@
},
"dependencies": {
"@discordapp/twemoji": "16.0.1",
- "i18n": "workspace:*",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.3",
"@rollup/pluginutils": "5.3.0",
"@twemoji/parser": "16.0.0",
- "@vitejs/plugin-vue": "6.0.2",
+ "@vitejs/plugin-vue": "6.0.4",
"buraha": "0.0.1",
"estree-walker": "3.0.3",
"frontend-shared": "workspace:*",
+ "i18n": "workspace:*",
"icons-subsetter": "workspace:*",
"json5": "2.2.3",
"mfm-js": "0.25.0",
"misskey-js": "workspace:*",
"punycode.js": "2.3.1",
- "rollup": "4.53.3",
- "sass": "1.95.1",
- "shiki": "3.19.0",
+ "rollup": "4.59.0",
+ "sass": "1.97.3",
+ "shiki": "3.22.0",
"tinycolor2": "1.6.0",
"uuid": "13.0.0",
- "vite": "7.2.7",
- "vue": "3.5.25"
+ "vite": "7.3.1",
+ "vue": "3.5.28"
},
"devDependencies": {
"@misskey-dev/summaly": "5.2.5",
@@ -39,30 +39,29 @@
"@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8",
"@types/micromatch": "4.0.10",
- "@types/node": "24.10.2",
+ "@types/node": "24.10.13",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
- "@vitest/coverage-v8": "4.0.15",
- "@vue/runtime-core": "3.5.25",
- "acorn": "8.15.0",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "@vitest/coverage-v8": "4.0.18",
+ "@vue/runtime-core": "3.5.28",
+ "acorn": "8.16.0",
"cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0",
- "eslint-plugin-vue": "10.6.2",
- "happy-dom": "20.0.11",
+ "eslint-plugin-vue": "10.8.0",
+ "happy-dom": "20.7.0",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
- "msw": "2.12.4",
- "nodemon": "3.1.11",
- "prettier": "3.7.4",
+ "msw": "2.12.10",
+ "nodemon": "3.1.14",
+ "prettier": "3.8.1",
"start-server-and-test": "2.1.3",
"tsx": "4.21.0",
- "typescript": "5.9.3",
"vite-plugin-turbosnap": "1.0.3",
- "vue-component-type-helpers": "3.1.8",
- "vue-eslint-parser": "10.2.0",
- "vue-tsc": "3.1.8"
+ "vue-component-type-helpers": "3.2.4",
+ "vue-eslint-parser": "10.4.0",
+ "vue-tsc": "3.2.4"
}
}
diff --git a/packages/frontend-embed/src/components/EmAvatar.vue b/packages/frontend-embed/src/components/EmAvatar.vue
index 58c35c8ef0..3f91e14403 100644
--- a/packages/frontend-embed/src/components/EmAvatar.vue
+++ b/packages/frontend-embed/src/components/EmAvatar.vue
@@ -9,16 +9,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="user.isCat" :class="[$style.ears]">
<div :class="$style.earLeft">
<div v-if="false" :class="$style.layer">
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
</div>
</div>
<div :class="$style.earRight">
<div v-if="false" :class="$style.layer">
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
</div>
</div>
</div>
diff --git a/packages/frontend-embed/src/components/EmImgWithBlurhash.vue b/packages/frontend-embed/src/components/EmImgWithBlurhash.vue
index 71f0ee9294..be18ce79d5 100644
--- a/packages/frontend-embed/src/components/EmImgWithBlurhash.vue
+++ b/packages/frontend-embed/src/components/EmImgWithBlurhash.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div ref="root" :class="['chromatic-ignore', $style.root, { [$style.cover]: cover }]" :title="title ?? ''">
- <canvas v-show="hide" key="canvas" ref="canvas" :class="$style.canvas" :width="canvasWidth" :height="canvasHeight" :title="title ?? undefined" tabindex="-1"/>
+ <canvas v-show="hide" key="canvas" ref="canvas" :class="$style.canvas" :width="canvasWidth" :height="canvasHeight" :title="title ?? undefined" tabindex="-1"></canvas>
<img v-show="!hide" key="img" ref="img" :height="imgHeight ?? undefined" :width="imgWidth ?? undefined" :class="$style.img" :src="src ?? undefined" :title="title ?? undefined" :alt="alt ?? undefined" loading="eager" decoding="async" tabindex="-1"/>
</div>
</template>
diff --git a/packages/frontend-embed/src/components/EmMediaImage.vue b/packages/frontend-embed/src/components/EmMediaImage.vue
index 94f0268da4..8cb90247fc 100644
--- a/packages/frontend-embed/src/components/EmMediaImage.vue
+++ b/packages/frontend-embed/src/components/EmMediaImage.vue
@@ -64,7 +64,7 @@ const url = computed(() => (props.raw)
: props.image.thumbnailUrl,
);
-async function onclick(ev: MouseEvent) {
+async function onclick(ev: PointerEvent) {
if (hide.value) {
ev.stopPropagation();
hide.value = false;
diff --git a/packages/frontend-embed/src/components/EmMfm.ts b/packages/frontend-embed/src/components/EmMfm.ts
index 1f9ce9d4f4..5b9a53bbc2 100644
--- a/packages/frontend-embed/src/components/EmMfm.ts
+++ b/packages/frontend-embed/src/components/EmMfm.ts
@@ -299,7 +299,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
]);
}
case 'clickable': {
- return h('span', { onClick(ev: MouseEvent): void {
+ return h('span', { onClick(ev: PointerEvent): void {
ev.stopPropagation();
ev.preventDefault();
const clickEv = typeof token.props.args.ev === 'string' ? token.props.args.ev : '';
diff --git a/packages/frontend-embed/src/pages/clip.vue b/packages/frontend-embed/src/pages/clip.vue
index f4d4e8cf6f..a65f38aa7d 100644
--- a/packages/frontend-embed/src/pages/clip.vue
+++ b/packages/frontend-embed/src/pages/clip.vue
@@ -84,7 +84,7 @@ const pagination = computed(() => ({
const notesEl = useTemplateRef('notesEl');
-function top(ev: MouseEvent) {
+function top(ev: PointerEvent) {
const target = ev.target as HTMLElement | null;
if (target && isLink(target)) return;
diff --git a/packages/frontend-embed/src/pages/tag.vue b/packages/frontend-embed/src/pages/tag.vue
index 4b00ae7c2d..8c6cd32bab 100644
--- a/packages/frontend-embed/src/pages/tag.vue
+++ b/packages/frontend-embed/src/pages/tag.vue
@@ -40,15 +40,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { computed, inject, useTemplateRef } from 'vue';
import { scrollToTop } from '@@/js/scroll.js';
+import { url, instanceName } from '@@/js/config.js';
+import { isLink } from '@@/js/is-link.js';
+import { defaultEmbedParams } from '@@/js/embed-page.js';
import type { Paging } from '@/components/EmPagination.vue';
import EmNotes from '@/components/EmNotes.vue';
import XNotFound from '@/pages/not-found.vue';
import EmTimelineContainer from '@/components/EmTimelineContainer.vue';
import { i18n } from '@/i18n.js';
-import { url, instanceName } from '@@/js/config.js';
-import { isLink } from '@@/js/is-link.js';
import { DI } from '@/di.js';
-import { defaultEmbedParams } from '@@/js/embed-page.js';
const props = defineProps<{
tag: string;
@@ -67,7 +67,7 @@ const pagination = computed(() => ({
const notesEl = useTemplateRef('notesEl');
-function top(ev: MouseEvent) {
+function top(ev: PointerEvent) {
const target = ev.target as HTMLElement | null;
if (target && isLink(target)) return;
diff --git a/packages/frontend-embed/src/pages/user-timeline.vue b/packages/frontend-embed/src/pages/user-timeline.vue
index 348b1a7622..4b5daf54da 100644
--- a/packages/frontend-embed/src/pages/user-timeline.vue
+++ b/packages/frontend-embed/src/pages/user-timeline.vue
@@ -49,6 +49,8 @@ import { ref, computed, inject, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import { url, instanceName } from '@@/js/config.js';
import { defaultEmbedParams } from '@@/js/embed-page.js';
+import { scrollToTop } from '@@/js/scroll.js';
+import { isLink } from '@@/js/is-link.js';
import type { Paging } from '@/components/EmPagination.vue';
import EmNotes from '@/components/EmNotes.vue';
import EmAvatar from '@/components/EmAvatar.vue';
@@ -56,8 +58,6 @@ import EmUserName from '@/components/EmUserName.vue';
import I18n from '@/components/I18n.vue';
import XNotFound from '@/pages/not-found.vue';
import EmTimelineContainer from '@/components/EmTimelineContainer.vue';
-import { scrollToTop } from '@@/js/scroll.js';
-import { isLink } from '@@/js/is-link.js';
import { misskeyApi } from '@/misskey-api.js';
import { i18n } from '@/i18n.js';
import { assertServerContext } from '@/server-context.js';
@@ -101,7 +101,7 @@ const pagination = computed(() => ({
const notesEl = useTemplateRef('notesEl');
-function top(ev: MouseEvent) {
+function top(ev: PointerEvent) {
const target = ev.target as HTMLElement | null;
if (target && isLink(target)) return;
diff --git a/packages/frontend-embed/tsconfig.json b/packages/frontend-embed/tsconfig.json
index 63e637c844..24fa71de19 100644
--- a/packages/frontend-embed/tsconfig.json
+++ b/packages/frontend-embed/tsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"allowJs": true,
"noEmitOnError": false,
- "noImplicitAny": false,
+ "noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedParameters": false,
"noUnusedLocals": false,
@@ -22,7 +22,6 @@
"isolatedModules": true,
"useDefineForClassFields": true,
"verbatimModuleSyntax": true,
- "baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@@/*": ["../frontend-shared/*"]
diff --git a/packages/frontend-shared/build.js b/packages/frontend-shared/build.js
index 07e98ad182..1f98267468 100644
--- a/packages/frontend-shared/build.js
+++ b/packages/frontend-shared/build.js
@@ -60,7 +60,7 @@ async function buildSrc() {
function buildDts() {
return execa(
- 'tsc',
+ 'tsgo',
[
'--project', 'tsconfig.json',
'--outDir', 'js-built',
diff --git a/packages/frontend-shared/eslint.config.js b/packages/frontend-shared/eslint.config.js
index b972cfdb27..6168637f22 100644
--- a/packages/frontend-shared/eslint.config.js
+++ b/packages/frontend-shared/eslint.config.js
@@ -46,7 +46,6 @@ export default [
},
},
rules: {
- '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-empty-interface': ['error', {
allowSingleExtends: true,
}],
diff --git a/packages/frontend-shared/js/worker-multi-dispatch.ts b/packages/frontend-shared/js/worker-multi-dispatch.ts
index 5d393ed1ed..909aa1e81e 100644
--- a/packages/frontend-shared/js/worker-multi-dispatch.ts
+++ b/packages/frontend-shared/js/worker-multi-dispatch.ts
@@ -34,7 +34,7 @@ export class WorkerMultiDispatch<POST = unknown, RETURN = unknown> {
public postMessage(message: POST, options?: Transferable[] | StructuredSerializeOptions, useWorkerNumber: WorkerNumberGetter = this.getUseWorkerNumber) {
let workerNumber = useWorkerNumber(this.prevWorkerNumber, this.workers.length);
workerNumber = Math.abs(Math.round(workerNumber)) % this.workers.length;
- if (_DEV_) console.log('WorkerMultiDispatch: Posting message to worker', workerNumber, useWorkerNumber);
+ // if (_DEV_) console.log('WorkerMultiDispatch: Posting message to worker', workerNumber, useWorkerNumber);
this.prevWorkerNumber = workerNumber;
// 不毛だがunionをoverloadに突っ込めない
diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json
index 49cce0d707..abcdea9244 100644
--- a/packages/frontend-shared/package.json
+++ b/packages/frontend-shared/package.json
@@ -17,18 +17,17 @@
"build": "node ./build.js",
"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "24.10.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
- "esbuild": "0.27.1",
- "eslint-plugin-vue": "10.6.2",
- "nodemon": "3.1.11",
- "typescript": "5.9.3",
- "vue-eslint-parser": "10.2.0"
+ "@types/node": "24.10.13",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "esbuild": "0.27.3",
+ "eslint-plugin-vue": "10.8.0",
+ "nodemon": "3.1.14",
+ "vue-eslint-parser": "10.4.0"
},
"files": [
"js-built"
@@ -36,6 +35,6 @@
"dependencies": {
"i18n": "workspace:*",
"misskey-js": "workspace:*",
- "vue": "3.5.25"
+ "vue": "3.5.28"
}
}
diff --git a/packages/frontend-shared/tsconfig.json b/packages/frontend-shared/tsconfig.json
index 12f00eb503..6b1804a0fc 100644
--- a/packages/frontend-shared/tsconfig.json
+++ b/packages/frontend-shared/tsconfig.json
@@ -17,7 +17,6 @@
"noImplicitReturns": true,
"esModuleInterop": true,
"verbatimModuleSyntax": true,
- "baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@@/*": ["./*"]
diff --git a/packages/frontend/.storybook/charts.ts b/packages/frontend/.storybook/charts.ts
index 93e1287d69..7a92df8e86 100644
--- a/packages/frontend/.storybook/charts.ts
+++ b/packages/frontend/.storybook/charts.ts
@@ -33,7 +33,7 @@ export function getChartResolver(fields: string[], option?: { accumulate?: boole
const res = {};
for (const field of fields) {
const layers = field.split('.');
- let current = res;
+ let current = res as any;
while (layers.length > 1) {
const currentKey = layers.shift()!;
if (current[currentKey] == null) current[currentKey] = {};
diff --git a/packages/frontend/eslint.config.js b/packages/frontend/eslint.config.js
index a0fb80be92..adae471c0a 100644
--- a/packages/frontend/eslint.config.js
+++ b/packages/frontend/eslint.config.js
@@ -42,7 +42,6 @@ export default [
},
},
rules: {
- '@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-empty-interface': ['error', {
allowSingleExtends: true,
}],
@@ -148,7 +147,15 @@ export default [
'vue/return-in-computed-property': 'warn',
'vue/no-setup-props-reactivity-loss': 'warn',
'vue/max-attributes-per-line': 'off',
- 'vue/html-self-closing': 'off',
+ 'vue/html-self-closing': ['error', {
+ html: {
+ void: 'any',
+ normal: 'never',
+ component: 'any',
+ },
+ svg: 'any',
+ math: 'any',
+ }],
'vue/singleline-html-element-content-newline': 'off',
'vue/v-on-event-hyphenation': ['error', 'never', {
autofix: true,
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 730bf71789..b3244d0737 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -6,7 +6,7 @@
"watch": "vite",
"build": "tsx build.ts",
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
- "build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
+ "build-storybook-pre": "(tsgo -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
"build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static",
"chromatic": "chromatic",
"test": "vitest --run --globals",
@@ -19,20 +19,19 @@
"@analytics/google-analytics": "1.1.0",
"@discordapp/twemoji": "16.0.1",
"@github/webauthn-json": "2.1.1",
- "@mcaptcha/vanilla-glue": "0.1.0-rc2",
- "i18n": "workspace:*",
+ "@mcaptcha/core-glue": "0.1.0-alpha-5",
"@misskey-dev/browser-image-resizer": "2024.1.0",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.3",
"@rollup/pluginutils": "5.3.0",
- "@sentry/vue": "10.29.0",
- "@syuilo/aiscript": "1.2.0",
+ "@sentry/vue": "10.39.0",
+ "@syuilo/aiscript": "1.2.1",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0",
- "@vitejs/plugin-vue": "6.0.2",
+ "@vitejs/plugin-vue": "6.0.4",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.16",
"analytics": "0.8.19",
- "broadcast-channel": "7.2.0",
+ "broadcast-channel": "7.3.0",
"buraha": "0.0.1",
"canvas-confetti": "1.9.4",
"chart.js": "4.5.1",
@@ -40,14 +39,15 @@
"chartjs-chart-matrix": "3.0.0",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.2.0",
- "chromatic": "13.3.4",
+ "chromatic": "15.1.1",
"compare-versions": "6.1.1",
"cropperjs": "2.1.0",
"date-fns": "4.1.0",
- "eventemitter3": "5.0.1",
+ "eventemitter3": "5.0.4",
"execa": "9.6.1",
- "exifreader": "4.33.1",
+ "exifreader": "4.36.2",
"frontend-shared": "workspace:*",
+ "i18n": "workspace:*",
"icons-subsetter": "workspace:*",
"idb-keyval": "6.2.2",
"insert-text-at-cursor": "0.3.0",
@@ -55,7 +55,7 @@
"is-file-animated": "1.0.2",
"json5": "2.2.3",
"matter-js": "0.20.0",
- "mediabunny": "1.25.8",
+ "mediabunny": "1.34.4",
"mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*",
@@ -64,85 +64,85 @@
"punycode.js": "2.3.1",
"qr-code-styling": "1.9.2",
"qr-scanner": "1.4.2",
- "rollup": "4.53.3",
- "sanitize-html": "2.17.0",
- "sass": "1.95.1",
- "shiki": "3.19.0",
+ "rollup": "4.59.0",
+ "sanitize-html": "2.17.1",
+ "sass": "1.97.3",
+ "shiki": "3.22.0",
"textarea-caret": "3.1.0",
- "three": "0.181.2",
+ "three": "0.183.1",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"v-code-diff": "1.13.1",
- "vite": "7.2.7",
- "vue": "3.5.25",
- "vuedraggable": "next",
+ "vite": "7.3.1",
+ "vue": "3.5.28",
"wanakana": "5.3.1"
},
"devDependencies": {
"@misskey-dev/summaly": "5.2.5",
- "@storybook/addon-essentials": "8.6.14",
- "@storybook/addon-interactions": "8.6.14",
- "@storybook/addon-links": "10.1.5",
- "@storybook/addon-mdx-gfm": "8.6.14",
- "@storybook/addon-storysource": "8.6.14",
- "@storybook/blocks": "8.6.14",
- "@storybook/components": "8.6.14",
- "@storybook/core-events": "8.6.14",
- "@storybook/manager-api": "8.6.14",
- "@storybook/preview-api": "8.6.14",
- "@storybook/react": "10.1.5",
- "@storybook/react-vite": "10.1.5",
- "@storybook/test": "8.6.14",
- "@storybook/theming": "8.6.14",
- "@storybook/types": "8.6.14",
- "@storybook/vue3": "10.1.5",
- "@storybook/vue3-vite": "10.1.5",
+ "@storybook/addon-essentials": "8.6.17",
+ "@storybook/addon-interactions": "8.6.17",
+ "@storybook/addon-links": "10.2.10",
+ "@storybook/addon-mdx-gfm": "8.6.17",
+ "@storybook/addon-storysource": "8.6.17",
+ "@storybook/blocks": "8.6.17",
+ "@storybook/components": "8.6.17",
+ "@storybook/core-events": "8.6.17",
+ "@storybook/manager-api": "8.6.17",
+ "@storybook/preview-api": "8.6.17",
+ "@storybook/react": "10.2.10",
+ "@storybook/react-vite": "10.2.10",
+ "@storybook/test": "8.6.17",
+ "@storybook/theming": "8.6.17",
+ "@storybook/types": "8.6.17",
+ "@storybook/vue3": "10.2.10",
+ "@storybook/vue3-vite": "10.2.10",
"@tabler/icons-webfont": "3.35.0",
"@testing-library/vue": "8.1.0",
"@types/canvas-confetti": "1.9.0",
"@types/estree": "1.0.8",
+ "@types/insert-text-at-cursor": "0.3.2",
"@types/matter-js": "0.20.2",
"@types/micromatch": "4.0.10",
- "@types/node": "24.10.2",
+ "@types/node": "24.10.13",
"@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8",
+ "@types/textarea-caret": "3.0.4",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
- "@vitest/coverage-v8": "4.0.15",
- "@vue/compiler-core": "3.5.25",
- "acorn": "8.15.0",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "@vitest/coverage-v8": "4.0.18",
+ "@vue/compiler-core": "3.5.28",
+ "acorn": "8.16.0",
"astring": "1.9.0",
"cross-env": "10.1.0",
- "cypress": "15.7.1",
+ "cypress": "15.10.0",
"eslint-plugin-import": "2.32.0",
- "eslint-plugin-vue": "10.6.2",
+ "eslint-plugin-vue": "10.8.0",
"estree-walker": "3.0.3",
- "happy-dom": "20.0.11",
+ "happy-dom": "20.7.0",
"intersection-observer": "0.12.2",
"magic-string": "0.30.21",
"micromatch": "4.0.8",
- "minimatch": "10.1.1",
- "msw": "2.12.4",
+ "minimatch": "10.2.4",
+ "msw": "2.12.10",
"msw-storybook-addon": "2.0.6",
- "nodemon": "3.1.11",
- "prettier": "3.7.4",
- "react": "19.2.1",
- "react-dom": "19.2.1",
+ "nodemon": "3.1.14",
+ "prettier": "3.8.1",
+ "react": "19.2.4",
+ "react-dom": "19.2.4",
"seedrandom": "3.0.5",
"start-server-and-test": "2.1.3",
- "storybook": "10.1.5",
+ "storybook": "10.2.10",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"tsx": "4.21.0",
- "typescript": "5.9.3",
"vite-plugin-glsl": "1.5.5",
"vite-plugin-turbosnap": "1.0.3",
- "vitest": "4.0.15",
+ "vitest": "4.0.18",
"vitest-fetch-mock": "0.4.5",
- "vue-component-type-helpers": "3.1.8",
- "vue-eslint-parser": "10.2.0",
- "vue-tsc": "3.1.8"
+ "vue-component-type-helpers": "3.2.4",
+ "vue-eslint-parser": "10.4.0",
+ "vue-tsc": "3.2.4"
}
}
diff --git a/packages/frontend/src/accounts.ts b/packages/frontend/src/accounts.ts
index 79086c2b39..862ef4e113 100644
--- a/packages/frontend/src/accounts.ts
+++ b/packages/frontend/src/accounts.ts
@@ -126,10 +126,10 @@ export function updateCurrentAccount(accountData: Misskey.entities.MeDetailed) {
if (!$i) return;
const token = $i.token;
for (const key of Object.keys($i)) {
- delete $i[key];
+ delete $i[key as keyof typeof $i];
}
for (const [key, value] of Object.entries(accountData)) {
- $i[key] = value;
+ ($i[key as keyof typeof accountData] as any) = value;
}
store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + $i.id]: $i });
$i.token = token;
@@ -139,7 +139,7 @@ export function updateCurrentAccount(accountData: Misskey.entities.MeDetailed) {
export function updateCurrentAccountPartial(accountData: Partial<Misskey.entities.MeDetailed>) {
if (!$i) return;
for (const [key, value] of Object.entries(accountData)) {
- $i[key] = value;
+ ($i[key as keyof typeof accountData] as any) = value;
}
store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + $i.id]: $i });
diff --git a/packages/frontend/src/aiscript/ui.ts b/packages/frontend/src/aiscript/ui.ts
index 9c330da3c5..768425746d 100644
--- a/packages/frontend/src/aiscript/ui.ts
+++ b/packages/frontend/src/aiscript/ui.ts
@@ -531,7 +531,7 @@ function getPostFormOptions(def: values.Value | undefined, call: (fn: values.VFn
export function registerAsUiLib(components: Ref<AsUiComponent>[], done: (root: Ref<AsUiRoot>) => void) {
type OptionsConverter<T extends AsUiComponent, C> = (def: values.Value | undefined, call: C) => Options<T>;
- const instances = {};
+ const instances = {} as Record<string, values.VObj>;
function createComponentInstance<T extends AsUiComponent, C>(
type: T['type'],
@@ -555,7 +555,7 @@ export function registerAsUiLib(components: Ref<AsUiComponent>[], done: (root: R
const updates = getOptions(def, call);
for (const update of def.value.keys()) {
if (!Object.hasOwn(updates, update)) continue;
- component.value[update] = updates[update];
+ component.value[update] = updates[update as keyof Options<T>];
}
})],
]));
diff --git a/packages/frontend/src/analytics.ts b/packages/frontend/src/analytics.ts
index e07a4e9258..9889018ddb 100644
--- a/packages/frontend/src/analytics.ts
+++ b/packages/frontend/src/analytics.ts
@@ -90,6 +90,7 @@ export async function initAnalytics(instance: Misskey.entities.MetaDetailed) {
// Google Analytics
if (instance.googleAnalyticsMeasurementId) {
+ //@ts-expect-error Dynamic import
const { default: googleAnalytics } = await import('@analytics/google-analytics');
plugins.push(googleAnalytics({
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 10b1199bbb..3d4ecbf75b 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -29,6 +29,7 @@ import { prefer } from '@/preferences.js';
import { updateCurrentAccountPartial } from '@/accounts.js';
import { migrateOldSettings } from '@/pref-migrate.js';
import { unisonReload } from '@/utility/unison-reload.js';
+import { isBirthday } from '@/utility/is-birthday.js';
export async function mainBoot() {
const { isClientUpdated, lastVersion } = await common(async () => {
@@ -144,12 +145,8 @@ export async function mainBoot() {
const m = now.getMonth() + 1;
const d = now.getDate();
- if ($i.birthday) {
- const bm = parseInt($i.birthday.split('-')[1]);
- const bd = parseInt($i.birthday.split('-')[2]);
- if (m === bm && d === bd) {
- claimAchievement('loggedInOnBirthday');
- }
+ if (isBirthday($i, now)) {
+ claimAchievement('loggedInOnBirthday');
}
if (m === 1 && d === 1) {
diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index c7252e7c98..cbc5b27fca 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -115,7 +115,7 @@ watch(moderationNote, async () => {
});
});
-function resolve(resolvedAs) {
+function resolve(resolvedAs: 'accept' | 'reject' | null) {
os.apiWithDialog('admin/resolve-abuse-user-report', {
reportId: props.report.id,
resolvedAs,
@@ -132,7 +132,7 @@ function forward() {
});
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
os.popupMenu([{
icon: 'ti ti-hash',
text: 'Copy ID',
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index c786e9fe9f..fe6415eabb 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -23,13 +23,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="$style.body">
<div :class="$style.header">
- <span :class="$style.title">{{ i18n.ts._achievements._types['_' + achievement.name].title }}</span>
+ <span :class="$style.title">{{ i18n.ts._achievements._types[`_${achievement.name}`].title }}</span>
<span :class="$style.time">
<time v-tooltip="new Date(achievement.unlockedAt).toLocaleString()">{{ new Date(achievement.unlockedAt).getFullYear() }}/{{ new Date(achievement.unlockedAt).getMonth() + 1 }}/{{ new Date(achievement.unlockedAt).getDate() }}</time>
</span>
</div>
- <div :class="$style.description">{{ withDescription ? i18n.ts._achievements._types['_' + achievement.name].description : '???' }}</div>
- <div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor && withDescription" :class="$style.flavor">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div>
+ <div :class="$style.description">{{ withDescription ? i18n.ts._achievements._types[`_${achievement.name}`].description : '???' }}</div>
+ <div v-if="'flavor' in i18n.ts._achievements._types[`_${achievement.name}`] && withDescription" :class="$style.flavor">{{ (i18n.ts._achievements._types[`_${achievement.name}`] as { flavor: string; }).flavor }}</div>
</div>
</div>
<template v-if="withLocked">
@@ -54,7 +54,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
import { onMounted, ref, computed } from 'vue';
-import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/utility/achievements.js';
diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue
index 81c92bfb5c..da0f618e95 100644
--- a/packages/frontend/src/components/MkAnnouncementDialog.vue
+++ b/packages/frontend/src/components/MkAnnouncementDialog.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkModal ref="modal" :zPriority="'middle'" :preferType="'dialog'" @closed="$emit('closed')" @click="onBgClick">
+<MkModal ref="modal" :zPriority="'middle'" :preferType="'dialog'" @closed="emit('closed')" @click="onBgClick">
<div ref="rootEl" :class="$style.root">
<div :class="$style.header">
<span :class="$style.icon">
@@ -44,6 +44,10 @@ const props = defineProps<{
announcement: Misskey.entities.Announcement;
}>();
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
+
const rootEl = useTemplateRef('rootEl');
const bottomEl = useTemplateRef('bottomEl');
const modal = useTemplateRef('modal');
diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index a3b6112629..c66e9d176a 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -64,13 +64,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed } from 'vue';
import type { Ref } from 'vue';
+import type { AsUiComponent, AsUiRoot, AsUiPostFormButton } from '@/aiscript/ui.js';
import * as os from '@/os.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkSelect from '@/components/MkSelect.vue';
-import type { AsUiComponent, AsUiRoot, AsUiPostFormButton } from '@/aiscript/ui.js';
import MkFolder from '@/components/MkFolder.vue';
import MkPostForm from '@/components/MkPostForm.vue';
import { useMkSelect } from '@/composables/use-mkselect.js';
@@ -106,7 +106,7 @@ const containerStyle = computed(() => {
const isBordered = c.borderWidth ?? c.borderColor ?? c.borderStyle;
const border = isBordered ? {
- borderWidth: c.borderWidth ?? '1px',
+ borderWidth: `${c.borderWidth ?? 1}px`,
borderColor: c.borderColor ?? 'var(--MI_THEME-divider)',
borderStyle: c.borderStyle ?? 'solid',
} : undefined;
@@ -144,7 +144,7 @@ const {
initialValue: (c.type === 'select' && 'default' in c && typeof c.default !== 'boolean') ? c.default ?? null : null,
});
-function onSelectUpdate(v) {
+function onSelectUpdate(v: string | null) {
valueForSelect.value = v;
if ('onChange' in c && c.onChange) {
c.onChange(v as never);
diff --git a/packages/frontend/src/components/MkAuthConfirm.vue b/packages/frontend/src/components/MkAuthConfirm.vue
index 8744b50926..b1a29660ad 100644
--- a/packages/frontend/src/components/MkAuthConfirm.vue
+++ b/packages/frontend/src/components/MkAuthConfirm.vue
@@ -183,7 +183,7 @@ async function init() {
init();
-function clickAddAccount(ev: MouseEvent) {
+function clickAddAccount(ev: PointerEvent) {
selectedUser.value = null;
os.popupMenu([{
diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
index 15aab8daed..9104650752 100644
--- a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
+++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
@@ -3,15 +3,14 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from 'storybook/actions';
import { expect, userEvent, waitFor, within } from '@storybook/test';
-import type { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAutocomplete from './MkAutocomplete.vue';
import MkInput from './MkInput.vue';
+import type { StoryObj } from '@storybook/vue3';
import { tick } from '@/utility/test-utils.js';
const common = {
render(args) {
@@ -81,7 +80,7 @@ export const User = {
...common.args,
type: 'user',
},
- async play({ canvasElement }) {
+ async play({ canvasElement }: { canvasElement: HTMLElement }) {
const canvas = within(canvasElement);
const input = canvas.getByRole('combobox');
await waitFor(() => userEvent.hover(input));
@@ -114,7 +113,7 @@ export const Hashtag = {
...common.args,
type: 'hashtag',
},
- async play({ canvasElement }) {
+ async play({ canvasElement }: { canvasElement: HTMLElement }) {
const canvas = within(canvasElement);
const input = canvas.getByRole('combobox');
await waitFor(() => userEvent.hover(input));
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index cf5d95e11b..bfe66cdf8f 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -45,12 +45,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts">
import { markRaw, ref, useTemplateRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
+import * as Misskey from 'misskey-js';
import sanitizeHtml from 'sanitize-html';
import { emojilist, getEmojiName } from '@@/js/emojilist.js';
import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@@/js/emoji-base.js';
import { MFM_TAGS, MFM_PARAMS } from '@@/js/const.js';
import type { EmojiDef } from '@/utility/search-emoji.js';
-import contains from '@/utility/contains.js';
+import { elementContains } from '@/utility/element-contains.js';
import { acct } from '@/filters/user.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -63,7 +64,7 @@ import { prefer } from '@/preferences.js';
export type CompleteInfo = {
user: {
- payload: any;
+ payload: Misskey.entities.User;
query: string | null;
},
hashtag: {
@@ -185,9 +186,9 @@ const suggests = ref<Element>();
const rootEl = useTemplateRef('rootEl');
const fetching = ref(true);
-const users = ref<any[]>([]);
-const hashtags = ref<any[]>([]);
-const emojis = ref<(EmojiDef)[]>([]);
+const users = ref<Misskey.entities.User[]>([]);
+const hashtags = ref<string[]>([]);
+const emojis = ref<EmojiDef[]>([]);
const items = ref<Element[] | HTMLCollection>([]);
const mfmTags = ref<string[]>([]);
const mfmParams = ref<string[]>([]);
@@ -204,8 +205,8 @@ function complete<T extends keyof CompleteInfo>(type: T, value: CompleteInfo[T][
emit('closed');
if (type === 'emoji' || type === 'emojiComplete') {
let recents = store.s.recentlyUsedEmojis;
- recents = recents.filter((emoji: any) => emoji !== value);
- recents.unshift(value);
+ recents = recents.filter((emoji) => emoji !== value);
+ recents.unshift(value as string);
store.set('recentlyUsedEmojis', recents.splice(0, 32));
}
}
@@ -254,7 +255,7 @@ function exec() {
limit: 10,
detail: false,
}).then(searchedUsers => {
- users.value = searchedUsers as any[];
+ users.value = searchedUsers;
fetching.value = false;
// キャッシュ
sessionStorage.setItem(cacheKey, JSON.stringify(searchedUsers));
@@ -276,7 +277,7 @@ function exec() {
query: props.q,
limit: 30,
}).then(searchedHashtags => {
- hashtags.value = searchedHashtags as any[];
+ hashtags.value = searchedHashtags;
fetching.value = false;
// キャッシュ
sessionStorage.setItem(cacheKey, JSON.stringify(searchedHashtags));
@@ -310,8 +311,8 @@ function exec() {
}
}
-function onMousedown(event: Event) {
- if (!contains(rootEl.value, event.target) && (rootEl.value !== event.target)) props.close();
+function onMousedown(event: MouseEvent) {
+ if (!elementContains(rootEl.value, event.target as Element) && (rootEl.value !== event.target)) props.close();
}
function onKeydown(event: KeyboardEvent) {
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index b729128a21..854ed31ed5 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -63,7 +63,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (ev: 'click', payload: MouseEvent): void;
+ (ev: 'click', payload: PointerEvent): void;
}>();
const el = useTemplateRef('el');
@@ -77,11 +77,11 @@ onMounted(() => {
}
});
-function distance(p, q): number {
+function distance(p: { x: number; y: number }, q: { x: number; y: number }): number {
return Math.hypot(p.x - q.x, p.y - q.y);
}
-function calcCircleScale(boxW, boxH, circleCenterX, circleCenterY): number {
+function calcCircleScale(boxW: number, boxH: number, circleCenterX: number, circleCenterY: number): number {
const origin = { x: circleCenterX, y: circleCenterY };
const dist1 = distance({ x: 0, y: 0 }, origin);
const dist2 = distance({ x: boxW, y: 0 }, origin);
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue
index 30940a34a9..2fa1135398 100644
--- a/packages/frontend/src/components/MkCaptcha.vue
+++ b/packages/frontend/src/components/MkCaptcha.vue
@@ -7,8 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>
<span v-if="!available">Loading<MkEllipsis/></span>
<div v-if="props.provider == 'mcaptcha'">
- <div id="mcaptcha__widget-container" class="m-captcha-style"></div>
- <div ref="captchaEl"></div>
+ <iframe
+ v-if="mCaptchaIframeUrl != null"
+ ref="mCaptchaIframe"
+ :src="mCaptchaIframeUrl"
+ style="border: none; max-width: 320px; width: 100%; height: 100%; max-height: 80px;"
+ ></iframe>
</div>
<div v-if="props.provider == 'testcaptcha'" style="background: #eee; border: solid 1px #888; padding: 8px; color: #000; max-width: 320px; display: flex; gap: 10px; align-items: center; box-shadow: 2px 2px 6px #0004; border-radius: 4px;">
<img src="/client-assets/testcaptcha.png" style="width: 60px; height: 60px; "/>
@@ -26,7 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { ref, useTemplateRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue';
+import { ref, useTemplateRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted, nextTick } from 'vue';
+import type Reciever_typeReferenceOnly from '@mcaptcha/core-glue';
import { store } from '@/store.js';
// APIs provided by Captcha services
@@ -71,6 +76,19 @@ const available = ref(false);
const captchaEl = useTemplateRef('captchaEl');
const captchaWidgetId = ref<string | undefined>(undefined);
+
+let mCaptchaReciever: Reciever_typeReferenceOnly | null = null;
+const mCaptchaIframe = useTemplateRef('mCaptchaIframe');
+const mCaptchaRemoveState = ref(false);
+const mCaptchaIframeUrl = computed(() => {
+ if (props.provider === 'mcaptcha' && !mCaptchaRemoveState.value && props.instanceUrl && props.sitekey) {
+ const url = new URL('/widget', props.instanceUrl);
+ url.searchParams.set('sitekey', props.sitekey);
+ return url.toString();
+ }
+ return null;
+});
+
const testcaptchaInput = ref('');
const testcaptchaPassed = ref(false);
@@ -84,7 +102,7 @@ const variable = computed(() => {
}
});
-const loaded = !!window[variable.value];
+const loaded = !!(window as any)[variable.value];
const src = computed(() => {
switch (props.provider) {
@@ -98,7 +116,7 @@ const src = computed(() => {
const scriptId = computed(() => `script-${props.provider}`);
-const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown as Captcha);
+const captcha = computed<Captcha>(() => (window as any)[variable.value] ?? {} as unknown as Captcha);
watch(() => [props.instanceUrl, props.sitekey, props.secretKey], async () => {
// 変更があったときはリフレッシュと再レンダリングをしておかないと、変更後の値で再検証が出来ない
@@ -129,8 +147,14 @@ function reset() {
if (_DEV_) console.warn(error);
}
}
+
testcaptchaPassed.value = false;
testcaptchaInput.value = '';
+
+ if (mCaptchaReciever != null) {
+ mCaptchaReciever.destroy();
+ mCaptchaReciever = null;
+ }
}
function remove() {
@@ -143,6 +167,10 @@ function remove() {
if (_DEV_) console.warn(error);
}
}
+
+ if (props.provider === 'mcaptcha') {
+ mCaptchaRemoveState.value = true;
+ }
}
async function requestRender() {
@@ -160,32 +188,29 @@ async function requestRender() {
'error-callback': () => callback(undefined),
});
} else if (props.provider === 'mcaptcha' && props.instanceUrl && props.sitekey) {
- const { default: Widget } = await import('@mcaptcha/vanilla-glue');
- new Widget({
+ const { default: Reciever } = await import('@mcaptcha/core-glue');
+ mCaptchaReciever = new Reciever({
siteKey: {
- instanceUrl: new URL(props.instanceUrl),
key: props.sitekey,
+ instanceUrl: new URL(props.instanceUrl),
},
+ }, (token: string) => {
+ callback(token);
});
+ mCaptchaReciever.listen();
+ mCaptchaRemoveState.value = false;
} else {
- window.setTimeout(requestRender, 1);
+ window.setTimeout(requestRender, 50);
}
}
function clearWidget() {
- if (props.provider === 'mcaptcha') {
- const container = window.document.getElementById('mcaptcha__widget-container');
- if (container) {
- container.innerHTML = '';
- }
- } else {
- reset();
- remove();
+ reset();
+ remove();
- if (captchaEl.value) {
- // レンダリング先のコンテナの中身を掃除し、フォームが増殖するのを抑止
- captchaEl.value.innerHTML = '';
- }
+ if (captchaEl.value) {
+ // レンダリング先のコンテナの中身を掃除し、フォームが増殖するのを抑止
+ captchaEl.value.innerHTML = '';
}
}
diff --git a/packages/frontend/src/components/MkChannelList.vue b/packages/frontend/src/components/MkChannelList.vue
index 23bb32c6b9..af89ec8252 100644
--- a/packages/frontend/src/components/MkChannelList.vue
+++ b/packages/frontend/src/components/MkChannelList.vue
@@ -24,6 +24,6 @@ const props = withDefaults(defineProps<{
noGap?: boolean;
extractor?: ExtractorFunction<P, Misskey.entities.Channel>;
}>(), {
- extractor: (item) => item,
+ extractor: (item: any) => item as Misskey.entities.Channel,
});
</script>
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index c54081ad42..e418e729ca 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -94,8 +94,8 @@ const props = withDefaults(defineProps<{
const legendEl = useTemplateRef('legendEl');
-const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
-const negate = arr => arr.map(x => -x);
+const sum = (...arr: number[][]) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
+const negate = (arr: number[]) => arr.map((x) => -x);
const colors = {
blue: '#008FFB',
@@ -108,7 +108,7 @@ const colors = {
cyan: '#00e0e0',
};
const colorSets = [colors.blue, colors.green, colors.yellow, colors.red, colors.purple];
-const getColor = (i) => {
+const getColor = (i: number) => {
return colorSets[i % colorSets.length];
};
@@ -142,7 +142,7 @@ const getDate = (ago: number) => {
return props.span === 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago);
};
-const format = (arr) => {
+const format = (arr: number[]) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
@@ -371,7 +371,7 @@ const fetchApRequestChart = async (): Promise<typeof chartData> => {
};
};
-const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
+const fetchNotesChart = async (type: 'local' | 'remote' | 'combined'): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/notes', { limit: props.limit, span: props.span });
return {
series: [{
diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue
index 775964af50..0c856c57eb 100644
--- a/packages/frontend/src/components/MkClickerGame.vue
+++ b/packages/frontend/src/components/MkClickerGame.vue
@@ -20,9 +20,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref } from 'vue';
+import { useInterval } from '@@/js/use-interval.js';
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
import * as os from '@/os.js';
-import { useInterval } from '@@/js/use-interval.js';
import * as game from '@/utility/clicker-game.js';
import number from '@/filters/number.js';
import { claimAchievement } from '@/utility/achievements.js';
@@ -32,7 +32,7 @@ const cookies = computed(() => saveData.value?.cookies);
const cps = ref(0);
const prevCookies = ref(0);
-function onClick(ev: MouseEvent) {
+function onClick(ev: PointerEvent) {
const x = ev.clientX;
const y = ev.clientY;
const { dispose } = os.popup(MkPlusOneEffect, { x, y }, {
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index bdb2ba6a44..dda5a14716 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -40,7 +40,7 @@ import XCode from '@/components/MkCode.core.vue';
const props = withDefaults(defineProps<{
modelValue: string | null;
- lang: string;
+ lang?: string;
required?: boolean;
readonly?: boolean;
disabled?: boolean;
@@ -51,7 +51,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'change', _ev: KeyboardEvent): void;
+ (ev: 'change', _ev: InputEvent): void;
(ev: 'keydown', _ev: KeyboardEvent): void;
(ev: 'enter'): void;
(ev: 'update:modelValue', value: string): void;
@@ -63,15 +63,17 @@ const focused = ref(false);
const changed = ref(false);
const inputEl = useTemplateRef('inputEl');
-const focus = () => inputEl.value?.focus();
+function focus() {
+ inputEl.value?.focus();
+}
-const onInput = (ev) => {
- v.value = ev.target?.value ?? v.value;
+function onInput(ev: InputEvent) {
+ v.value = (inputEl.value?.value) ?? '';
changed.value = true;
emit('change', ev);
-};
+}
-const onKeydown = (ev: KeyboardEvent) => {
+function onKeydown(ev: KeyboardEvent) {
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return;
emit('keydown', ev);
@@ -102,12 +104,12 @@ const onKeydown = (ev: KeyboardEvent) => {
});
ev.preventDefault();
}
-};
+}
-const updated = () => {
+function updated() {
changed.value = false;
emit('update:modelValue', v.value);
-};
+}
const debouncedUpdated = debounce(1000, updated);
diff --git a/packages/frontend/src/components/MkContextMenu.stories.impl.ts b/packages/frontend/src/components/MkContextMenu.stories.impl.ts
index 7a5e36131b..fc9fd9bc49 100644
--- a/packages/frontend/src/components/MkContextMenu.stories.impl.ts
+++ b/packages/frontend/src/components/MkContextMenu.stories.impl.ts
@@ -3,11 +3,9 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-/* eslint-disable @typescript-eslint/explicit-function-return-type */
-/* eslint-disable import/no-default-export */
-import type { StoryObj } from '@storybook/vue3';
import { userEvent, within } from '@storybook/test';
import MkContextMenu from './MkContextMenu.vue';
+import type { StoryObj } from '@storybook/vue3';
import * as os from '@/os.js';
export const Empty = {
render(args) {
@@ -25,7 +23,7 @@ export const Empty = {
},
},
methods: {
- onContextmenu(ev: MouseEvent) {
+ onContextmenu(ev: PointerEvent) {
os.contextMenu(args.items, ev);
},
},
diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index 9c6397a72c..6678c8fb91 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only
import { onMounted, onBeforeUnmount, useTemplateRef, ref } from 'vue';
import MkMenu from './MkMenu.vue';
import type { MenuItem } from '@/types/menu.js';
-import contains from '@/utility/contains.js';
+import { elementContains } from '@/utility/element-contains.js';
import { prefer } from '@/preferences.js';
import * as os from '@/os.js';
const props = defineProps<{
items: MenuItem[];
- ev: MouseEvent;
+ ev: PointerEvent;
}>();
const emit = defineEmits<{
@@ -75,8 +75,8 @@ onBeforeUnmount(() => {
window.document.body.removeEventListener('mousedown', onMousedown);
});
-function onMousedown(evt: Event) {
- if (!contains(rootEl.value, evt.target) && (rootEl.value !== evt.target)) emit('closed');
+function onMousedown(evt: MouseEvent) {
+ if (!elementContains(rootEl.value, evt.target as Element) && (rootEl.value !== evt.target)) emit('closed');
}
</script>
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 6c07eac47a..1fad936d16 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkModalWindow>
</template>
-<script lang="ts" setup>
+<script lang="ts" setup generic="F extends File | Blob">
import { onMounted, useTemplateRef, ref, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js';
import Cropper from 'cropperjs';
@@ -38,13 +38,13 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
const props = defineProps<{
- imageFile: File | Blob;
+ imageFile: F;
aspectRatio: number | null;
uploadFolder?: string | null;
}>();
const emit = defineEmits<{
- (ev: 'ok', cropped: File | Blob): void;
+ (ev: 'ok', cropped: F): void;
(ev: 'cancel'): void;
(ev: 'closed'): void;
}>();
@@ -74,8 +74,14 @@ async function ok() {
});
const f = await promise;
+ let finalFile: F;
+ if (props.imageFile instanceof File) {
+ finalFile = new File([f], props.imageFile.name, { type: f.type }) as F;
+ } else {
+ finalFile = f as F;
+ }
- emit('ok', f);
+ emit('ok', finalFile);
if (dialogEl.value != null) dialogEl.value.close();
}
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 705301a6a6..fb8b38de6d 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -25,8 +25,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
<template #caption>
- <span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.tsx._dialog.charactersExceeded({ current: (inputValue as string)?.length ?? 0, max: input.maxLength ?? 'NaN' })"/>
- <span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.tsx._dialog.charactersBelow({ current: (inputValue as string)?.length ?? 0, min: input.minLength ?? 'NaN' })"/>
+ <span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.tsx._dialog.charactersExceeded({ current: (inputValue as string)?.length ?? 0, max: input.maxLength ?? 'NaN' })"></span>
+ <span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.tsx._dialog.charactersBelow({ current: (inputValue as string)?.length ?? 0, min: input.minLength ?? 'NaN' })"></span>
</template>
</MkInput>
<MkSelect v-if="select" v-model="selectedValue" :items="selectDef" autofocus></MkSelect>
@@ -41,13 +41,19 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkModal>
</template>
+<script lang="ts">
+export type Result = string | number | true | null;
+export type MkDialogReturnType<T = Result> = { canceled: true, result: undefined } | { canceled: false, result: T };
+</script>
+
<script lang="ts" setup>
import { ref, useTemplateRef, computed } from 'vue';
import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
-import type { MkSelectItem, OptionValue } from '@/components/MkSelect.vue';
+import type { MkSelectItem } from '@/components/MkSelect.vue';
+import type { OptionValue } from '@/types/option-value.js';
import { useMkSelect } from '@/composables/use-mkselect.js';
import { i18n } from '@/i18n.js';
@@ -65,8 +71,6 @@ type Select = {
default: OptionValue | null;
};
-type Result = string | number | true | null;
-
const props = withDefaults(defineProps<{
type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting';
title?: string;
@@ -93,7 +97,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'done', v: { canceled: true } | { canceled: false, result: Result }): void;
+ (ev: 'done', v: MkDialogReturnType): void;
(ev: 'closed'): void;
}>();
@@ -131,7 +135,7 @@ function done(canceled: true): void;
function done(canceled: false, result: Result): void; // eslint-disable-line no-redeclare
function done(canceled: boolean, result?: Result): void { // eslint-disable-line no-redeclare
- emit('done', { canceled, result } as { canceled: true } | { canceled: false, result: Result });
+ emit('done', { canceled, result } as MkDialogReturnType);
modal.value?.close();
}
diff --git a/packages/frontend/src/components/MkDivider.vue b/packages/frontend/src/components/MkDivider.vue
index f72f091383..808a9ae2f8 100644
--- a/packages/frontend/src/components/MkDivider.vue
+++ b/packages/frontend/src/components/MkDivider.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
borderWidth ? { borderWidth: borderWidth } : {},
borderColor ? { borderColor: borderColor } : {},
]"
-/>
+></div>
</template>
<script setup lang="ts">
diff --git a/packages/frontend/src/components/MkDraggable.vue b/packages/frontend/src/components/MkDraggable.vue
new file mode 100644
index 0000000000..6e2e038f87
--- /dev/null
+++ b/packages/frontend/src/components/MkDraggable.vue
@@ -0,0 +1,311 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<TransitionGroup
+ tag="div"
+ :enterActiveClass="$style.transition_items_enterActive"
+ :leaveActiveClass="$style.transition_items_leaveActive"
+ :enterFromClass="$style.transition_items_enterFrom"
+ :leaveToClass="$style.transition_items_leaveTo"
+ :moveClass="$style.transition_items_move"
+ :class="[$style.items, { [$style.dragging]: dragging, [$style.horizontal]: direction === 'horizontal', [$style.vertical]: direction === 'vertical', [$style.withGaps]: withGaps, [$style.canNest]: canNest }]"
+>
+ <slot name="header"></slot>
+ <div
+ v-if="modelValue.length === 0"
+ :class="$style.emptyDropArea"
+ @dragover.prevent.stop="() => {}"
+ @dragleave="() => {}"
+ @drop.prevent.stop="onEmptyDrop($event)"
+ >
+ </div>
+ <div
+ v-for="(item, i) in modelValue"
+ :key="`MkDraggableRoot:${item.id}`"
+ :class="$style.item"
+ :draggable="!manualDragStart"
+ @dragstart.stop="onDragstart($event, item)"
+ >
+ <div
+ :class="[$style.forwardArea, { [$style.dropReady]: dropReadyArea[0] === item.id && dropReadyArea[1] === 'forward' }]"
+ @dragover.prevent.stop="onDragover($event, item, false)"
+ @dragleave="onDragleave($event, item)"
+ @drop.prevent.stop="onDrop($event, item, false)"
+ ></div>
+ <div :key="`MkDraggableItem:${item.id}`" style="position: relative; z-index: 0;">
+ <slot :item="item" :index="i" :dragStart="(ev) => onDragstart(ev, item)"></slot>
+ </div>
+ <div
+ :class="[$style.backwardArea, { [$style.dropReady]: dropReadyArea[0] === item.id && dropReadyArea[1] === 'backward' }]"
+ @dragover.prevent.stop="onDragover($event, item, true)"
+ @dragleave="onDragleave($event, item)"
+ @drop.prevent.stop="onDrop($event, item, true)"
+ ></div>
+ </div>
+ <slot name="footer"></slot>
+</TransitionGroup>
+</template>
+
+<script lang="ts">
+import { ref } from 'vue';
+
+// 別々のコンポーネントインスタンス間でD&Dを融通するためにグローバルに状態を持っておく必要がある
+const dragging = ref(false);
+let dropCallback: ((targetInstanceId: string) => void) | null = null;
+</script>
+
+<script lang="ts" setup generic="T extends { id: string; }">
+import { nextTick } from 'vue';
+import { getDragData, setDragData } from '@/drag-and-drop.js';
+import { genId } from '@/utility/id.js';
+
+const slots = defineSlots<{
+ default(props: { item: T; index: number; dragStart: (ev: DragEvent) => void }): any;
+ header(): any;
+ footer(): any;
+}>();
+
+const props = withDefaults(defineProps<{
+ modelValue: T[];
+ direction: 'horizontal' | 'vertical';
+ group?: string | null;
+ manualDragStart?: boolean;
+ withGaps?: boolean;
+ canNest?: boolean;
+}>(), {
+ group: null,
+ manualDragStart: false,
+ withGaps: false,
+ canNest: false,
+});
+
+const emit = defineEmits<{
+ (ev: 'update:modelValue', value: T[]): void;
+}>();
+
+const dropReadyArea = ref<[T['id'] | null, 'forward' | 'backward' | null]>([null, null]);
+const instanceId = genId();
+const group = props.group ?? instanceId;
+
+function onDragstart(ev: DragEvent, item: T) {
+ if (ev.dataTransfer == null) return;
+ ev.dataTransfer.effectAllowed = 'move';
+ setDragData(ev, 'MkDraggable', { item, instanceId, group });
+
+ const target = ev.target as HTMLElement;
+ target.addEventListener('dragend', (ev) => {
+ dragging.value = false;
+ dropReadyArea.value = [null, null];
+ }, { once: true });
+
+ dropCallback = (targetInstanceId) => {
+ if (targetInstanceId === instanceId) return;
+ const newValue = props.modelValue.filter(x => x.id !== item.id);
+ emit('update:modelValue', newValue);
+ };
+
+ // Chromeのバグで、Dragstartハンドラ内ですぐにDOMを変更する(=リアクティブなプロパティを変更する)とDragが終了してしまう
+ // SEE: https://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately
+ // SEE: https://issues.chromium.org/issues/41150279
+ window.setTimeout(() => {
+ dragging.value = true;
+ }, 10);
+}
+
+function onDragover(ev: DragEvent, item: T, backward: boolean) {
+ nextTick(() => {
+ dropReadyArea.value = [item.id, backward ? 'backward' : 'forward'];
+ });
+}
+
+function onDragleave(ev: DragEvent, item: T) {
+ dropReadyArea.value = [null, null];
+}
+
+function onDrop(ev: DragEvent, item: T, backward: boolean) {
+ const dragged = getDragData(ev, 'MkDraggable');
+ dropReadyArea.value = [null, null];
+ if (dragged == null || dragged.group !== group || dragged.item.id === item.id) return;
+ dropCallback?.(instanceId);
+
+ const fromIndex = props.modelValue.findIndex(x => x.id === dragged.item.id);
+ let toIndex = props.modelValue.findIndex(x => x.id === item.id);
+
+ const newValue = [...props.modelValue];
+ if (fromIndex > -1) newValue.splice(fromIndex, 1);
+ toIndex = newValue.findIndex(x => x.id === item.id);
+ if (backward) toIndex += 1;
+ newValue.splice(toIndex, 0, dragged.item as T);
+
+ emit('update:modelValue', newValue);
+}
+
+function onEmptyDrop(ev: DragEvent) {
+ const dragged = getDragData(ev, 'MkDraggable');
+ if (dragged == null) return;
+ dropCallback?.(instanceId);
+
+ emit('update:modelValue', [dragged.item as T]);
+}
+</script>
+
+<style lang="scss" module>
+.transition_items_move,
+.transition_items_enterActive,
+.transition_items_leaveActive {
+ transition: all 0.15s ease;
+}
+.transition_items_enterFrom,
+.transition_items_leaveTo {
+ opacity: 0;
+}
+.transition_items_leaveActive {
+ position: absolute;
+}
+
+.items {
+ display: flex;
+ align-items: center;
+ justify-content: left;
+ flex-wrap: wrap;
+}
+
+.items.horizontal {
+ flex-direction: row;
+}
+.items.vertical {
+ flex-direction: column;
+}
+
+.item {
+ position: relative;
+}
+
+.items.vertical .item {
+ width: 100%;
+}
+
+.items.horizontal.withGaps {
+ row-gap: var(--MI-margin);
+}
+
+.items.horizontal.withGaps .item {
+ padding-left: calc(var(--MI-margin) / 2);
+ padding-right: calc(var(--MI-margin) / 2);
+}
+
+.items.vertical.withGaps .item {
+ padding-top: calc(var(--MI-margin) / 2);
+ padding-bottom: calc(var(--MI-margin) / 2);
+}
+
+.forwardArea, .backwardArea {
+ position: absolute;
+ z-index: 1;
+ pointer-events: none;
+}
+
+.items.dragging {
+ .forwardArea, .backwardArea {
+ pointer-events: auto;
+ }
+}
+
+.items.horizontal {
+ .forwardArea {
+ top: 0;
+ left: 0;
+ width: 50%;
+ height: 100%;
+ }
+
+ .backwardArea {
+ top: 0;
+ right: 0;
+ width: 50%;
+ height: 100%;
+ }
+}
+
+.items.vertical {
+ .forwardArea {
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 50%;
+ }
+
+ .backwardArea {
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 50%;
+ }
+}
+
+.items.canNest.horizontal {
+ .forwardArea, .backwardArea {
+ width: 30px;
+ }
+}
+
+.items.canNest.vertical {
+ .forwardArea, .backwardArea {
+ height: 30px;
+ }
+}
+
+.dropReady::before {
+ content: '';
+ position: absolute;
+ z-index: 99999;
+ background: var(--MI_THEME-accent);
+ border-radius: 999px;
+ pointer-events: none;
+}
+
+.items.horizontal {
+ .forwardArea.dropReady::before {
+ top: 0;
+ left: -1px;
+ width: 2px;
+ height: 100%;
+ }
+
+ .backwardArea.dropReady::before {
+ top: 0;
+ right: -1px;
+ width: 2px;
+ height: 100%;
+ }
+}
+
+.items.vertical {
+ .forwardArea.dropReady::before {
+ top: -1px;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ }
+
+ .backwardArea.dropReady::before {
+ bottom: -1px;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ }
+}
+
+.items.horizontal .emptyDropArea {
+ width: 40px;
+ height: 40px;
+}
+
+.items.vertical .emptyDropArea {
+ width: 100%;
+ height: 50px;
+}
+</style>
diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue
index 0eca85b3a6..e2858084c0 100644
--- a/packages/frontend/src/components/MkDrive.file.vue
+++ b/packages/frontend/src/components/MkDrive.file.vue
@@ -64,7 +64,7 @@ const isDragging = ref(false);
const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`);
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
os.contextMenu(getDriveFileMenu(props.file, props.folder), ev);
}
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index d7dd12408c..6d93dfc0d4 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -57,7 +57,7 @@ const props = withDefaults(defineProps<{
const emit = defineEmits<{
(ev: 'chosen', v: Misskey.entities.DriveFolder): void;
(ev: 'unchose', v: Misskey.entities.DriveFolder): void;
- (ev: 'upload', files: File[], folder: Misskey.entities.DriveFolder);
+ (ev: 'upload', files: File[], folder: Misskey.entities.DriveFolder): void;
(ev: 'dragstart'): void;
(ev: 'dragend'): void;
}>();
@@ -231,17 +231,17 @@ function rename() {
}
function move() {
- selectDriveFolder(null).then(folder => {
- if (folder[0] && folder[0].id === props.folder.id) return;
+ selectDriveFolder(null).then(({ canceled, folders }) => {
+ if (canceled || (folders[0] && folders[0].id === props.folder.id)) return;
misskeyApi('drive/folders/update', {
folderId: props.folder.id,
- parentId: folder[0] ? folder[0].id : null,
+ parentId: folders[0] ? folders[0].id : null,
}).then(() => {
globalEvents.emit('driveFoldersUpdated', [{
...props.folder,
- parentId: folder[0] ? folder[0].id : null,
- parent: folder[0] ?? null,
+ parentId: folders[0] ? folders[0].id : null,
+ parent: folders[0] ?? null,
}]);
});
});
@@ -277,7 +277,7 @@ function setAsUploadFolder() {
prefer.commit('uploadFolder', props.folder.id);
}
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
let menu: MenuItem[];
menu = [{
text: i18n.ts.openInWindow,
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index d8c949d8eb..2961bc5032 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -69,7 +69,6 @@ SPDX-License-Identifier: AGPL-3.0-only
v-for="(f, i) in foldersPaginator.items.value"
:key="f.id"
v-anim="i"
- :class="$style.folder"
:folder="f"
:selectMode="select === 'folder'"
:isSelected="selectedFolders.some(x => x.id === f.id)"
@@ -102,7 +101,6 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<XFile
v-for="file in item.items" :key="file.id"
- :class="$style.file"
:file="file"
:folder="folder"
:isSelected="selectedFiles.some(x => x.id === file.id)"
@@ -125,7 +123,6 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<XFile
v-for="file in filesPaginator.items.value" :key="file.id"
- :class="$style.file"
:file="file"
:folder="folder"
:isSelected="selectedFiles.some(x => x.id === file.id)"
@@ -135,7 +132,16 @@ SPDX-License-Identifier: AGPL-3.0-only
/>
</TransitionGroup>
- <MkButton v-show="filesPaginator.canFetchOlder.value" :class="$style.loadMore" primary rounded @click="filesPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
+ <MkButton
+ v-show="canFetchFiles"
+ v-appear="shouldEnableInfiniteScroll ? fetchMoreFiles : null"
+ :class="$style.loadMore"
+ primary
+ rounded
+ @click="fetchMoreFiles"
+ >
+ {{ i18n.ts.loadMore }}
+ </MkButton>
<div v-if="filesPaginator.items.value.length == 0 && foldersPaginator.items.value.length == 0 && !fetching" :class="$style.empty">
<div v-if="draghover">{{ i18n.ts.dropHereToUpload }}</div>
@@ -182,10 +188,12 @@ const props = withDefaults(defineProps<{
type?: string;
multiple?: boolean;
select?: 'file' | 'folder' | null;
+ forceDisableInfiniteScroll?: boolean;
}>(), {
initialFolder: null,
multiple: false,
select: null,
+ forceDisableInfiniteScroll: false,
});
const emit = defineEmits<{
@@ -194,6 +202,10 @@ const emit = defineEmits<{
(ev: 'cd', v: Misskey.entities.DriveFolder | null): void;
}>();
+const shouldEnableInfiniteScroll = computed(() => {
+ return prefer.r.enableInfiniteScroll.value && !props.forceDisableInfiniteScroll;
+});
+
const folder = ref<Misskey.entities.DriveFolder | null>(null);
const hierarchyFolders = ref<Misskey.entities.DriveFolder[]>([]);
@@ -228,10 +240,9 @@ const filesPaginator = markRaw(new Paginator('drive/files', {
params: () => ({ // 自動でリロードしたくないためcomputedParamsは使わない
folderId: folder.value ? folder.value.id : null,
type: props.type,
- sort: sortModeSelect.value,
+ sort: ['-createdAt', '+createdAt'].includes(sortModeSelect.value) ? null : sortModeSelect.value,
}),
}));
-
const foldersPaginator = markRaw(new Paginator('drive/folders', {
limit: 30,
canFetchDetection: 'limit',
@@ -240,6 +251,16 @@ const foldersPaginator = markRaw(new Paginator('drive/folders', {
}),
}));
+const canFetchFiles = computed(() => !fetching.value && (filesPaginator.order.value === 'oldest' ? filesPaginator.canFetchNewer.value : filesPaginator.canFetchOlder.value));
+
+async function fetchMoreFiles() {
+ if (filesPaginator.order.value === 'oldest') {
+ filesPaginator.fetchNewer();
+ } else {
+ filesPaginator.fetchOlder();
+ }
+}
+
const filesTimeline = makeDateGroupedTimelineComputedRef(filesPaginator.items, 'month');
const shouldBeGroupedByDate = computed(() => ['+createdAt', '-createdAt'].includes(sortModeSelect.value));
@@ -250,10 +271,10 @@ watch(sortModeSelect, () => {
async function initialize() {
fetching.value = true;
- await Promise.all([
- foldersPaginator.init(),
- filesPaginator.init(),
- ]);
+ await foldersPaginator.reload();
+ filesPaginator.initialDirection = sortModeSelect.value === '-createdAt' ? 'newer' : 'older';
+ filesPaginator.order.value = sortModeSelect.value === '-createdAt' ? 'oldest' : 'newest';
+ await filesPaginator.reload();
fetching.value = false;
}
@@ -472,7 +493,7 @@ function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) {
});
}
-function onFileClick(ev: MouseEvent, file: Misskey.entities.DriveFile) {
+function onFileClick(ev: PointerEvent, file: Misskey.entities.DriveFile) {
if (ev.shiftKey) {
isEditMode.value = true;
}
@@ -544,7 +565,7 @@ function cd(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder
folder.value = folderToMove;
hierarchyFolders.value = [];
- const dive = folderToDive => {
+ const dive = (folderToDive: Misskey.entities.DriveFolder) => {
hierarchyFolders.value.unshift(folderToDive);
if (folderToDive.parent) dive(folderToDive.parent);
};
@@ -558,17 +579,19 @@ function cd(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder
async function moveFilesBulk() {
if (selectedFiles.value.length === 0) return;
- const toFolder = await selectDriveFolder(folder.value ? folder.value.id : null);
+ const { canceled, folders } = await selectDriveFolder(folder.value ? folder.value.id : null);
+
+ if (canceled) return;
await os.apiWithDialog('drive/files/move-bulk', {
fileIds: selectedFiles.value.map(f => f.id),
- folderId: toFolder[0] ? toFolder[0].id : null,
+ folderId: folders[0] ? folders[0].id : null,
});
globalEvents.emit('driveFilesUpdated', selectedFiles.value.map(x => ({
...x,
- folderId: toFolder[0] ? toFolder[0].id : null,
- folder: toFolder[0] ?? null,
+ folderId: folders[0] ? folders[0].id : null,
+ folder: folders[0] ?? null,
})));
}
@@ -668,11 +691,11 @@ function getMenu() {
return menu;
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
}
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
os.contextMenu(getMenu(), ev);
}
diff --git a/packages/frontend/src/components/MkEmbedCodeGenDialog.vue b/packages/frontend/src/components/MkEmbedCodeGenDialog.vue
index 4f16149caa..9002669378 100644
--- a/packages/frontend/src/components/MkEmbedCodeGenDialog.vue
+++ b/packages/frontend/src/components/MkEmbedCodeGenDialog.vue
@@ -23,9 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
:enterFromClass="$style.transition_x_enterFrom"
:leaveToClass="$style.transition_x_leaveTo"
>
- <div v-if="phase === 'input'" key="input" :class="$style.embedCodeGenInputRoot">
- <div :class="[$style.embedCodeGenPreviewRoot, prefer.s.animation ? $style.animatedBg : null]">
- <MkLoading v-if="iframeLoading" :class="$style.embedCodeGenPreviewSpinner"/>
+ <MkPreviewWithControls v-if="phase === 'input'" key="input" :previewLoading="iframeLoading">
+ <template #preview>
<div :class="$style.embedCodeGenPreviewWrapper">
<div class="_acrylic" :class="$style.embedCodeGenPreviewTitle">{{ i18n.ts.preview }}</div>
<div ref="resizerRootEl" :class="$style.embedCodeGenPreviewResizerRoot" inert>
@@ -43,27 +42,29 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- </div>
- <div :class="$style.embedCodeGenSettings" class="_gaps">
- <MkInput v-if="isEmbedWithScrollbar" v-model="maxHeight" type="number" :min="0">
- <template #label>{{ i18n.ts._embedCodeGen.maxHeight }}</template>
- <template #suffix>px</template>
- <template #caption>{{ i18n.ts._embedCodeGen.maxHeightDescription }}</template>
- </MkInput>
- <MkSelect v-model="colorMode" :items="colorModeDef">
- <template #label>{{ i18n.ts.theme }}</template>
- </MkSelect>
- <MkSwitch v-if="isEmbedWithScrollbar" v-model="header">{{ i18n.ts._embedCodeGen.header }}</MkSwitch>
- <MkSwitch v-model="rounded">{{ i18n.ts._embedCodeGen.rounded }}</MkSwitch>
- <MkSwitch v-model="border">{{ i18n.ts._embedCodeGen.border }}</MkSwitch>
- <MkInfo v-if="isEmbedWithScrollbar && (!maxHeight || maxHeight <= 0)" warn>{{ i18n.ts._embedCodeGen.maxHeightWarn }}</MkInfo>
- <MkInfo v-if="typeof maxHeight === 'number' && (maxHeight <= 0 || maxHeight > 700)">{{ i18n.ts._embedCodeGen.previewIsNotActual }}</MkInfo>
- <div class="_buttons">
- <MkButton :disabled="iframeLoading" @click="applyToPreview">{{ i18n.ts._embedCodeGen.applyToPreview }}</MkButton>
- <MkButton :disabled="iframeLoading" primary @click="generate">{{ i18n.ts._embedCodeGen.generateCode }} <i class="ti ti-arrow-right"></i></MkButton>
+ </template>
+ <template #controls>
+ <div class="_spacer _gaps">
+ <MkInput v-if="isEmbedWithScrollbar" v-model="maxHeight" type="number" :min="0">
+ <template #label>{{ i18n.ts._embedCodeGen.maxHeight }}</template>
+ <template #suffix>px</template>
+ <template #caption>{{ i18n.ts._embedCodeGen.maxHeightDescription }}</template>
+ </MkInput>
+ <MkSelect v-model="colorMode" :items="colorModeDef">
+ <template #label>{{ i18n.ts.theme }}</template>
+ </MkSelect>
+ <MkSwitch v-if="isEmbedWithScrollbar" v-model="header">{{ i18n.ts._embedCodeGen.header }}</MkSwitch>
+ <MkSwitch v-model="rounded">{{ i18n.ts._embedCodeGen.rounded }}</MkSwitch>
+ <MkSwitch v-model="border">{{ i18n.ts._embedCodeGen.border }}</MkSwitch>
+ <MkInfo v-if="isEmbedWithScrollbar && (!maxHeight || maxHeight <= 0)" warn>{{ i18n.ts._embedCodeGen.maxHeightWarn }}</MkInfo>
+ <MkInfo v-if="typeof maxHeight === 'number' && (maxHeight <= 0 || maxHeight > 700)">{{ i18n.ts._embedCodeGen.previewIsNotActual }}</MkInfo>
+ <div class="_buttons">
+ <MkButton :disabled="iframeLoading" @click="applyToPreview">{{ i18n.ts._embedCodeGen.applyToPreview }}</MkButton>
+ <MkButton :disabled="iframeLoading" primary @click="generate">{{ i18n.ts._embedCodeGen.generateCode }} <i class="ti ti-arrow-right"></i></MkButton>
+ </div>
</div>
- </div>
- </div>
+ </template>
+ </MkPreviewWithControls>
<div v-else-if="phase === 'result'" key="result" :class="$style.embedCodeGenResultRoot">
<div :class="$style.embedCodeGenResultWrapper" class="_gaps">
<div class="_gaps_s">
@@ -89,18 +90,17 @@ import { url } from '@@/js/config.js';
import { embedRouteWithScrollbar } from '@@/js/embed-page.js';
import type { EmbeddableEntity, EmbedParams } from '@@/js/embed-page.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkPreviewWithControls from '@/components/MkPreviewWithControls.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkButton from '@/components/MkButton.vue';
import MkCode from '@/components/MkCode.vue';
import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { useMkSelect } from '@/composables/use-mkselect.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { normalizeEmbedParams, getEmbedCode } from '@/utility/get-embed-code.js';
-import { prefer } from '@/preferences.js';
const emit = defineEmits<{
(ev: 'ok'): void;
@@ -302,29 +302,6 @@ onUnmounted(() => {
height: 100%;
}
-.embedCodeGenInputRoot {
- height: 100%;
- display: grid;
- grid-template-columns: 1fr 400px;
-}
-
-.embedCodeGenPreviewRoot {
- position: relative;
- cursor: not-allowed;
- background-color: var(--MI_THEME-bg);
- background-image: linear-gradient(135deg, transparent 30%, var(--MI_THEME-panel) 30%, var(--MI_THEME-panel) 50%, transparent 50%, transparent 80%, var(--MI_THEME-panel) 80%, var(--MI_THEME-panel) 100%);
- background-size: 20px 20px;
-}
-
-.animatedBg {
- animation: bg 1.2s linear infinite;
-}
-
-@keyframes bg {
- 0% { background-position: 0 0; }
- 100% { background-position: -20px -20px; }
-}
-
.embedCodeGenPreviewWrapper {
display: flex;
flex-direction: column;
@@ -372,11 +349,6 @@ onUnmounted(() => {
color-scheme: light dark;
}
-.embedCodeGenSettings {
- padding: 24px;
- overflow-y: scroll;
-}
-
.embedCodeGenResultRoot {
box-sizing: border-box;
padding: 24px;
@@ -417,11 +389,4 @@ onUnmounted(() => {
.embedCodeGenResultButtons {
margin: 0 auto;
}
-
-@container (max-width: 800px) {
- .embedCodeGenInputRoot {
- grid-template-columns: 1fr;
- grid-template-rows: 1fr 1fr;
- }
-}
</style>
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index ef515e471f..3ee32710e5 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -62,8 +62,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed } from 'vue';
-import type { Ref } from 'vue';
import { getEmojiName } from '@@/js/emojilist.js';
+import type { Ref } from 'vue';
import type { CustomEmojiFolderTree } from '@@/js/emojilist.js';
import { i18n } from '@/i18n.js';
import { customEmojis } from '@/custom-emojis.js';
@@ -78,7 +78,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (ev: 'chosen', v: string, event: MouseEvent): void;
+ (ev: 'chosen', v: string, event: PointerEvent): void;
}>();
const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props.emojis.value);
@@ -86,13 +86,13 @@ const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props
const shown = ref(!!props.initialShown);
/** @see MkEmojiPicker.vue */
-function computeButtonTitle(ev: MouseEvent): void {
+function computeButtonTitle(ev: PointerEvent): void {
const elm = ev.target as HTMLElement;
const emoji = elm.dataset.emoji as string;
elm.title = getEmojiName(emoji);
}
-function nestedChosen(emoji: string, ev: MouseEvent) {
+function nestedChosen(emoji: string, ev: PointerEvent) {
emit('chosen', emoji, ev);
}
</script>
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 33e9137c2f..bf0f9d0130 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -412,13 +412,13 @@ function getDef(emoji: string): string | Misskey.entities.EmojiSimple | UnicodeE
}
/** @see MkEmojiPicker.section.vue */
-function computeButtonTitle(ev: MouseEvent): void {
+function computeButtonTitle(ev: PointerEvent): void {
const elm = ev.target as HTMLElement;
const emoji = elm.dataset.emoji as string;
elm.title = getEmojiName(emoji);
}
-function chosen(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef, ev?: MouseEvent) {
+function chosen(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef, ev?: PointerEvent) {
const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined;
if (el && prefer.s.animation) {
const rect = el.getBoundingClientRect();
diff --git a/packages/frontend/src/components/MkExtensionInstaller.vue b/packages/frontend/src/components/MkExtensionInstaller.vue
index c9d18ee731..3f7eb9bccd 100644
--- a/packages/frontend/src/components/MkExtensionInstaller.vue
+++ b/packages/frontend/src/components/MkExtensionInstaller.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #key>{{ i18n.ts.permission }}</template>
<template #value>
<ul v-if="extension.meta.permissions && extension.meta.permissions.length > 0" :class="$style.extInstallerKVList">
- <li v-for="permission in extension.meta.permissions" :key="permission">{{ i18n.ts._permissions[permission] }}</li>
+ <li v-for="permission in extension.meta.permissions" :key="permission">{{ i18n.ts._permissions[permission] ?? permission }}</li>
</ul>
<template v-else>{{ i18n.ts.none }}</template>
</template>
@@ -91,7 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder>
</div>
- <slot name="additionalInfo"/>
+ <slot name="additionalInfo"></slot>
<div class="_buttonsCenter">
<MkButton danger rounded large @click="emits('cancel')"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
@@ -101,6 +101,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts">
+import * as Misskey from 'misskey-js';
+
export type Extension = {
type: 'plugin';
raw: string;
@@ -109,7 +111,7 @@ export type Extension = {
version: string;
author: string;
description?: string;
- permissions?: string[];
+ permissions?: (typeof Misskey.permissions)[number][];
config?: Record<string, unknown>;
};
} | {
@@ -125,7 +127,6 @@ export type Extension = {
<script lang="ts" setup>
import { computed } from 'vue';
import MkButton from '@/components/MkButton.vue';
-import FormSection from '@/components/form/section.vue';
import FormSplit from '@/components/form/split.vue';
import MkCode from '@/components/MkCode.vue';
import MkInfo from '@/components/MkInfo.vue';
diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue
index a998c810f0..59dac46162 100644
--- a/packages/frontend/src/components/MkFileListForAdmin.vue
+++ b/packages/frontend/src/components/MkFileListForAdmin.vue
@@ -6,7 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
<MkPagination v-slot="{ items }" :paginator="paginator">
- <div :class="[$style.fileList, { [$style.grid]: viewMode === 'grid', [$style.list]: viewMode === 'list', '_gaps_s': viewMode === 'list' }]">
+ <div
+ :class="{
+ [$style.grid]: viewMode === 'grid',
+ [$style.list]: viewMode === 'list',
+ '_gaps_s': viewMode === 'list',
+ }"
+ >
<MkA
v-for="file in items"
:key="file.id"
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index 94fdf6da36..864f53d09c 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -169,7 +169,7 @@ function afterLeave(el: Element) {
let pageId = pageFolderTeleportCount.value;
pageFolderTeleportCount.value += 1000;
-async function toggle(ev: MouseEvent) {
+async function toggle(ev: PointerEvent) {
if (asPage && !opened.value) {
pageId++;
const { dispose } = await popup(MkFolderPage, {
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index ba21fe82e4..72a24411c1 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -81,7 +81,13 @@ function onFollowChange(user: Misskey.entities.UserDetailed) {
}
async function onClick() {
- pleaseLogin({ openOnRemote: { type: 'web', path: `/@${props.user.username}@${props.user.host ?? host}` } });
+ const isLoggedIn = await pleaseLogin({
+ openOnRemote: {
+ type: 'web',
+ path: `/@${props.user.username}@${props.user.host ?? host}`,
+ },
+ });
+ if (!isLoggedIn) return;
wait.value = true;
diff --git a/packages/frontend/src/components/MkFormDialog.file.vue b/packages/frontend/src/components/MkForm.file.vue
index 182ff3ccf5..d233467e8b 100644
--- a/packages/frontend/src/components/MkFormDialog.file.vue
+++ b/packages/frontend/src/components/MkForm.file.vue
@@ -50,7 +50,7 @@ if (props.fileId) {
});
}
-function selectButton(ev: MouseEvent) {
+function selectButton(ev: PointerEvent) {
selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
diff --git a/packages/frontend/src/components/MkForm.vue b/packages/frontend/src/components/MkForm.vue
new file mode 100644
index 0000000000..f2360e8cdd
--- /dev/null
+++ b/packages/frontend/src/components/MkForm.vue
@@ -0,0 +1,125 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div v-if="Object.values(form).filter(item => typeof item.hidden !== 'boolean' || item.hidden === true).length > 0" class="_gaps_m">
+ <template v-for="v, k in form">
+ <template v-if="typeof v.hidden == 'function' ? v.hidden(values) : v.hidden"></template>
+ <MkInput v-else-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1" :manualSave="v.manualSave" @savingStateChange="(changed, invalid) => onSavingStateChange(k, changed, invalid)">
+ <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+ <template v-if="v.description" #caption>{{ v.description }}</template>
+ </MkInput>
+ <MkInput v-else-if="v.type === 'string' && !v.multiline" v-model="values[k]" type="text" :mfmAutocomplete="v.treatAsMfm" :manualSave="v.manualSave" @savingStateChange="(changed, invalid) => onSavingStateChange(k, changed, invalid)">
+ <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+ <template v-if="v.description" #caption>{{ v.description }}</template>
+ </MkInput>
+ <MkTextarea v-else-if="v.type === 'string' && v.multiline" v-model="values[k]" :mfmAutocomplete="v.treatAsMfm" :mfmPreview="v.treatAsMfm" :manualSave="v.manualSave" @savingStateChange="(changed, invalid) => onSavingStateChange(k, changed, invalid)">
+ <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+ <template v-if="v.description" #caption>{{ v.description }}</template>
+ </MkTextarea>
+ <MkSwitch v-else-if="v.type === 'boolean'" v-model="values[k]">
+ <span v-text="v.label || k"></span>
+ <template v-if="v.description" #caption>{{ v.description }}</template>
+ </MkSwitch>
+ <MkSelect v-else-if="v.type === 'enum'" v-model="values[k]" :items="getMkSelectDef(v)">
+ <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+ </MkSelect>
+ <MkRadios v-else-if="v.type === 'radio'" v-model="values[k]" :options="getRadioOptionsDef(v)">
+ <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+ </MkRadios>
+ <MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter">
+ <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
+ <template v-if="v.description" #caption>{{ v.description }}</template>
+ </MkRange>
+ <MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)">
+ <span v-text="v.content || k"></span>
+ </MkButton>
+ <XFile
+ v-else-if="v.type === 'drive-file'"
+ :fileId="v.defaultFileId"
+ :validate="async f => !v.validate || await v.validate(f)"
+ @update="f => values[k] = f"
+ />
+ </template>
+</div>
+<MkResult v-else type="empty" :text="i18n.ts.nothingToConfigure"/>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, watch } from 'vue';
+import XFile from '@/components/MkForm.file.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import MkSelect from '@/components/MkSelect.vue';
+import MkRange from '@/components/MkRange.vue';
+import MkButton from '@/components/MkButton.vue';
+import MkRadios from '@/components/MkRadios.vue';
+import { i18n } from '@/i18n.js';
+import type { MkSelectItem } from '@/components/MkSelect.vue';
+import type { MkRadiosOption } from '@/components/MkRadios.vue';
+import type { Form, EnumFormItem, RadioFormItem } from '@/utility/form.js';
+
+const props = defineProps<{
+ form: Form;
+}>();
+
+const emit = defineEmits<{
+ (ev: 'canSaveStateChange', canSave: boolean): void;
+}>();
+
+// TODO: ジェネリックにしたい
+const values = defineModel<Record<string, any>>({ required: true });
+
+// 保存可能状態の管理
+const inputSavingStates = ref<Record<string, { changed: boolean; invalid: boolean }>>({});
+
+function onSavingStateChange(key: string, changed: boolean, invalid: boolean) {
+ inputSavingStates.value[key] = { changed, invalid };
+}
+
+const canSave = computed(() => {
+ for (const key in inputSavingStates.value) {
+ const state = inputSavingStates.value[key];
+ if (
+ ('manualSave' in props.form[key] && props.form[key].manualSave && state.changed) ||
+ state.invalid
+ ) {
+ return false;
+ }
+ if ('required' in props.form[key] && props.form[key].required) {
+ const val = values.value[key];
+ if (val === null || val === undefined || val === '') {
+ return false;
+ }
+ }
+ }
+ return true;
+});
+
+watch(canSave, (newCanSave) => {
+ emit('canSaveStateChange', newCanSave);
+}, { immediate: true });
+
+function getMkSelectDef(def: EnumFormItem): MkSelectItem[] {
+ return def.enum.map((v) => {
+ if (typeof v === 'string') {
+ return { value: v, label: v };
+ } else {
+ return { value: v.value, label: v.label };
+ }
+ });
+}
+
+function getRadioOptionsDef(def: RadioFormItem): MkRadiosOption[] {
+ return def.options.map<MkRadiosOption>((v) => {
+ if (typeof v === 'string') {
+ return { value: v, label: v };
+ } else {
+ return { value: v.value, label: v.label };
+ }
+ });
+}
+</script>
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index 142ccb12a3..091721b40b 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:width="450"
:canClose="false"
:withOkButton="true"
- :okButtonDisabled="false"
+ :okButtonDisabled="!canSave"
@click="cancel()"
@ok="ok()"
@close="cancel()"
@@ -20,66 +20,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div class="_spacer" style="--MI_SPACER-min: 20px; --MI_SPACER-max: 32px;">
- <div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
- <template v-for="(v, k) in Object.fromEntries(Object.entries(form))">
- <template v-if="typeof v.hidden == 'function' ? v.hidden(values) : v.hidden"></template>
- <MkInput v-else-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1">
- <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
- <template v-if="v.description" #caption>{{ v.description }}</template>
- </MkInput>
- <MkInput v-else-if="v.type === 'string' && !v.multiline" v-model="values[k]" type="text" :mfmAutocomplete="v.treatAsMfm">
- <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
- <template v-if="v.description" #caption>{{ v.description }}</template>
- </MkInput>
- <MkTextarea v-else-if="v.type === 'string' && v.multiline" v-model="values[k]" :mfmAutocomplete="v.treatAsMfm" :mfmPreview="v.treatAsMfm">
- <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
- <template v-if="v.description" #caption>{{ v.description }}</template>
- </MkTextarea>
- <MkSwitch v-else-if="v.type === 'boolean'" v-model="values[k]">
- <span v-text="v.label || k"></span>
- <template v-if="v.description" #caption>{{ v.description }}</template>
- </MkSwitch>
- <MkSelect v-else-if="v.type === 'enum'" v-model="values[k]" :items="getMkSelectDef(v)">
- <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
- </MkSelect>
- <MkRadios v-else-if="v.type === 'radio'" v-model="values[k]">
- <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
- <option v-for="option in v.options" :key="getRadioKey(option)" :value="option.value">{{ option.label }}</option>
- </MkRadios>
- <MkRange v-else-if="v.type === 'range'" v-model="values[k]" :min="v.min" :max="v.max" :step="v.step" :textConverter="v.textConverter">
- <template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
- <template v-if="v.description" #caption>{{ v.description }}</template>
- </MkRange>
- <MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)">
- <span v-text="v.content || k"></span>
- </MkButton>
- <XFile
- v-else-if="v.type === 'drive-file'"
- :fileId="v.defaultFileId"
- :validate="async f => !v.validate || await v.validate(f)"
- @update="f => values[k] = f"
- />
- </template>
- </div>
- <MkResult v-else type="empty"/>
+ <MkForm v-model="values" :form="form" @canSaveStateChange="onCanSaveStateChanged"/>
</div>
</MkModalWindow>
</template>
<script lang="ts" setup>
-import { reactive, useTemplateRef } from 'vue';
-import MkInput from './MkInput.vue';
-import MkTextarea from './MkTextarea.vue';
-import MkSwitch from './MkSwitch.vue';
-import MkSelect from './MkSelect.vue';
-import MkRange from './MkRange.vue';
-import MkButton from './MkButton.vue';
-import MkRadios from './MkRadios.vue';
-import XFile from './MkFormDialog.file.vue';
-import type { MkSelectItem } from '@/components/MkSelect.vue';
-import type { Form, EnumFormItem, RadioFormItem } from '@/utility/form.js';
+import { ref, useTemplateRef } from 'vue';
+import type { Form } from '@/utility/form.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
-import { i18n } from '@/i18n.js';
+import MkForm from '@/components/MkForm.vue';
const props = defineProps<{
title: string;
@@ -96,19 +46,30 @@ const emit = defineEmits<{
}>();
const dialog = useTemplateRef('dialog');
-const values = reactive({});
-for (const item in props.form) {
- if ('default' in props.form[item]) {
- values[item] = props.form[item].default ?? null;
- } else {
- values[item] = null;
+const values = ref((() => {
+ const obj: Record<string, any> = {};
+ for (const item in props.form) {
+ if ('default' in props.form[item]) {
+ obj[item] = props.form[item].default ?? null;
+ } else {
+ obj[item] = null;
+ }
}
+ return obj;
+})());
+
+const canSave = ref(true);
+
+function onCanSaveStateChanged(newCanSave: boolean) {
+ canSave.value = newCanSave;
}
function ok() {
+ if (!canSave.value) return;
+
emit('done', {
- result: values,
+ result: values.value,
});
dialog.value?.close();
}
@@ -119,18 +80,4 @@ function cancel() {
});
dialog.value?.close();
}
-
-function getMkSelectDef(def: EnumFormItem): MkSelectItem[] {
- return def.enum.map((v) => {
- if (typeof v === 'string') {
- return { value: v, label: v };
- } else {
- return { value: v.value, label: v.label };
- }
- });
-}
-
-function getRadioKey(e: RadioFormItem['options'][number]) {
- return typeof e.value === 'string' ? e.value : JSON.stringify(e.value);
-}
</script>
diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue
index abbf86004b..03780bf3ba 100644
--- a/packages/frontend/src/components/MkHeatmap.vue
+++ b/packages/frontend/src/components/MkHeatmap.vue
@@ -125,8 +125,7 @@ async function renderChart() {
data: format(values) as any,
borderWidth: 0,
borderRadius: 3,
- backgroundColor(c) {
- // @ts-expect-error TS(2339)
+ backgroundColor(c: any) {
const value = c.dataset.data[c.dataIndex].v as number;
let a = (value - min) / max;
if (value !== 0) { // 0でない限りは完全に不可視にはしない
@@ -195,7 +194,7 @@ async function renderChart() {
font: {
size: 9,
},
- callback: (value, index, values) => ['', 'Mon', '', 'Wed', '', 'Fri', ''][value],
+ callback: (value, index, values) => ['', 'Mon', '', 'Wed', '', 'Fri', ''][value as any],
},
},
},
diff --git a/packages/frontend/src/components/MkImageEffectorDialog.vue b/packages/frontend/src/components/MkImageEffectorDialog.vue
index 3d7801f925..85e86e3a77 100644
--- a/packages/frontend/src/components/MkImageEffectorDialog.vue
+++ b/packages/frontend/src/components/MkImageEffectorDialog.vue
@@ -16,37 +16,36 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header><i class="ti ti-sparkles"></i> {{ i18n.ts._imageEffector.title }}</template>
- <div :class="$style.root">
- <div :class="$style.container">
- <div :class="[$style.preview, prefer.s.animation ? $style.animatedBg : null]">
- <canvas ref="canvasEl" :class="$style.previewCanvas" @pointerdown.prevent.stop="onImagePointerdown"></canvas>
- <div :class="$style.previewContainer">
- <div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
- <div class="_acrylic" :class="$style.editControls">
- <button class="_button" :class="[$style.previewControlsButton, penMode != null ? $style.active : null]" @click="showPenMenu"><i class="ti ti-pencil"></i></button>
- </div>
- <div class="_acrylic" :class="$style.previewControls">
- <button class="_button" :class="[$style.previewControlsButton, !enabled ? $style.active : null]" @click="enabled = false">Before</button>
- <button class="_button" :class="[$style.previewControlsButton, enabled ? $style.active : null]" @click="enabled = true">After</button>
- </div>
+ <MkPreviewWithControls>
+ <template #preview>
+ <canvas ref="canvasEl" :class="$style.previewCanvas" @pointerdown.prevent.stop="onImagePointerdown"></canvas>
+ <div :class="$style.previewContainer">
+ <div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
+ <div class="_acrylic" :class="$style.editControls">
+ <button class="_button" :class="[$style.previewControlsButton, penMode != null ? $style.active : null]" @click="showPenMenu"><i class="ti ti-pencil"></i></button>
+ </div>
+ <div class="_acrylic" :class="$style.previewControls">
+ <button class="_button" :class="[$style.previewControlsButton, !enabled ? $style.active : null]" @click="enabled = false">Before</button>
+ <button class="_button" :class="[$style.previewControlsButton, enabled ? $style.active : null]" @click="enabled = true">After</button>
</div>
</div>
- <div :class="$style.controls">
- <div class="_spacer _gaps">
- <XLayer
- v-for="(layer, i) in layers"
- :key="layer.id"
- v-model:layer="layers[i]"
- @del="onLayerDelete(layer)"
- @swapUp="onLayerSwapUp(layer)"
- @swapDown="onLayerSwapDown(layer)"
- ></XLayer>
+ </template>
- <MkButton rounded primary style="margin: 0 auto;" @click="addEffect"><i class="ti ti-plus"></i> {{ i18n.ts._imageEffector.addEffect }}</MkButton>
- </div>
+ <template #controls>
+ <div class="_spacer _gaps">
+ <XLayer
+ v-for="(layer, i) in layers"
+ :key="layer.id"
+ v-model:layer="layers[i]"
+ @del="onLayerDelete(layer)"
+ @swapUp="onLayerSwapUp(layer)"
+ @swapDown="onLayerSwapDown(layer)"
+ ></XLayer>
+
+ <MkButton rounded primary style="margin: 0 auto;" @click="addEffect"><i class="ti ti-plus"></i> {{ i18n.ts._imageEffector.addEffect }}</MkButton>
</div>
- </div>
- </div>
+ </template>
+ </MkPreviewWithControls>
</MkModalWindow>
</template>
@@ -56,15 +55,12 @@ import type { ImageEffectorLayer } from '@/utility/image-effector/ImageEffector.
import { i18n } from '@/i18n.js';
import { ImageEffector } from '@/utility/image-effector/ImageEffector.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
-import MkSelect from '@/components/MkSelect.vue';
+import MkPreviewWithControls from '@/components/MkPreviewWithControls.vue';
import MkButton from '@/components/MkButton.vue';
-import MkInput from '@/components/MkInput.vue';
import XLayer from '@/components/MkImageEffectorDialog.Layer.vue';
import * as os from '@/os.js';
-import { deepClone } from '@/utility/clone.js';
import { FXS } from '@/utility/image-effector/fxs.js';
import { genId } from '@/utility/id.js';
-import { prefer } from '@/preferences.js';
const props = defineProps<{
image: File;
@@ -99,7 +95,7 @@ watch(layers, async () => {
}
}, { deep: true });
-function addEffect(ev: MouseEvent) {
+function addEffect(ev: PointerEvent) {
os.popupMenu(Object.entries(FXS).map(([id, fx]) => ({
text: fx.uiDefinition.name,
action: () => {
@@ -223,7 +219,7 @@ watch(enabled, () => {
const penMode = ref<'fill' | 'blur' | 'pixelate' | null>(null);
-function showPenMenu(ev: MouseEvent) {
+function showPenMenu(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts._imageEffector._fxs.fill,
action: () => {
@@ -299,7 +295,7 @@ function onImagePointerdown(ev: PointerEvent) {
scaleX: 0.1,
scaleY: 0.1,
angle: 0,
- radius: 3,
+ radius: 10,
ellipse: false,
},
});
@@ -367,33 +363,6 @@ function onImagePointerdown(ev: PointerEvent) {
</script>
<style module>
-.root {
- container-type: inline-size;
- height: 100%;
-}
-
-.container {
- height: 100%;
- display: grid;
- grid-template-columns: 1fr 400px;
-}
-
-.preview {
- position: relative;
- background-color: var(--MI_THEME-bg);
- background-image: linear-gradient(135deg, transparent 30%, var(--MI_THEME-panel) 30%, var(--MI_THEME-panel) 50%, transparent 50%, transparent 80%, var(--MI_THEME-panel) 80%, var(--MI_THEME-panel) 100%);
- background-size: 20px 20px;
-}
-
-.animatedBg {
- animation: bg 1.2s linear infinite;
-}
-
-@keyframes bg {
- 0% { background-position: 0 0; }
- 100% { background-position: -20px -20px; }
-}
-
.previewContainer {
display: flex;
flex-direction: column;
@@ -442,16 +411,6 @@ function onImagePointerdown(ev: PointerEvent) {
}
}
-.previewSpinner {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- pointer-events: none;
- user-select: none;
- -webkit-user-drag: none;
-}
-
.previewCanvas {
position: absolute;
top: 0;
@@ -467,15 +426,4 @@ function onImagePointerdown(ev: PointerEvent) {
object-fit: contain;
touch-action: none;
}
-
-.controls {
- overflow-y: scroll;
-}
-
-@container (max-width: 800px) {
- .container {
- grid-template-columns: 1fr;
- grid-template-rows: 1fr 1fr;
- }
-}
</style>
diff --git a/packages/frontend/src/components/MkImageEffectorFxForm.vue b/packages/frontend/src/components/MkImageEffectorFxForm.vue
index e581b1f743..6bbec6c868 100644
--- a/packages/frontend/src/components/MkImageEffectorFxForm.vue
+++ b/packages/frontend/src/components/MkImageEffectorFxForm.vue
@@ -28,13 +28,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ v.label ?? k }}</template>
<template v-if="v.caption != null" #caption>{{ v.caption }}</template>
</MkRange>
- <MkRadios v-else-if="v.type === 'number:enum'" v-model="params[k]">
+ <MkRadios v-else-if="v.type === 'number:enum'" v-model="params[k]" :options="v.enum">
<template #label>{{ v.label ?? k }}</template>
<template v-if="v.caption != null" #caption>{{ v.caption }}</template>
- <option v-for="item in v.enum" :value="item.value">
- <i v-if="item.icon" :class="item.icon"></i>
- <template v-else>{{ item.label }}</template>
- </option>
</MkRadios>
<div v-else-if="v.type === 'seed'">
<MkRange v-model="params[k]" continuousUpdate type="number" :min="0" :max="10000" :step="1">
@@ -48,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkInput>
</div>
<div v-if="Object.keys(paramDefs).length === 0" :class="$style.nothingToConfigure">
- {{ i18n.ts._imageEffector.nothingToConfigure }}
+ {{ i18n.ts.nothingToConfigure }}
</div>
</div>
</template>
@@ -68,7 +64,7 @@ defineProps<{
const params = defineModel<Record<string, any>>({ required: true });
function getHex(c: ImageEffectorRGB) {
- return `#${c.map(x => (x * 255).toString(16).padStart(2, '0')).join('')}`;
+ return `#${c.map(x => Math.round(x * 255).toString(16).padStart(2, '0')).join('')}`;
}
function getRgb(hex: string | number): ImageEffectorRGB | null {
diff --git a/packages/frontend/src/components/MkImageFrameEditorDialog.vue b/packages/frontend/src/components/MkImageFrameEditorDialog.vue
index 2a91c85952..1a37a32a96 100644
--- a/packages/frontend/src/components/MkImageFrameEditorDialog.vue
+++ b/packages/frontend/src/components/MkImageFrameEditorDialog.vue
@@ -16,140 +16,139 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header><i class="ti ti-device-ipad-horizontal"></i> {{ i18n.ts._imageFrameEditor.title }}</template>
- <div :class="$style.root">
- <div :class="$style.container">
- <div :class="[$style.preview, prefer.s.animation ? $style.animatedBg : null]">
- <canvas ref="canvasEl" :class="$style.previewCanvas"></canvas>
- <div :class="$style.previewContainer">
- <div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
- <div v-if="props.image == null" class="_acrylic" :class="$style.previewControls">
- <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '3_2' ? $style.active : null]" @click="sampleImageType = '3_2'"><i class="ti ti-crop-landscape"></i></button>
- <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '2_3' ? $style.active : null]" @click="sampleImageType = '2_3'"><i class="ti ti-crop-portrait"></i></button>
- <button class="_button" :class="[$style.previewControlsButton]" @click="choiceImage"><i class="ti ti-upload"></i></button>
- </div>
+ <MkPreviewWithControls>
+ <template #preview>
+ <canvas ref="canvasEl" :class="$style.previewCanvas"></canvas>
+ <div :class="$style.previewContainer">
+ <div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
+ <div v-if="props.image == null" class="_acrylic" :class="$style.previewControls">
+ <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '3_2' ? $style.active : null]" @click="sampleImageType = '3_2'"><i class="ti ti-crop-landscape"></i></button>
+ <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '2_3' ? $style.active : null]" @click="sampleImageType = '2_3'"><i class="ti ti-crop-portrait"></i></button>
+ <button class="_button" :class="[$style.previewControlsButton]" @click="choiceImage"><i class="ti ti-upload"></i></button>
</div>
</div>
- <div :class="$style.controls">
- <div class="_spacer _gaps">
- <MkRange v-model="params.borderThickness" :min="0" :max="0.2" :step="0.01" :continuousUpdate="true">
- <template #label>{{ i18n.ts._imageFrameEditor.borderThickness }}</template>
- </MkRange>
+ </template>
- <MkInput :modelValue="getHex(params.bgColor)" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) params.bgColor = c; }">
- <template #label>{{ i18n.ts._imageFrameEditor.backgroundColor }}</template>
- </MkInput>
+ <template #controls>
+ <div class="_spacer _gaps">
+ <MkRange v-model="params.borderThickness" :min="0" :max="0.2" :step="0.01" :continuousUpdate="true">
+ <template #label>{{ i18n.ts._imageFrameEditor.borderThickness }}</template>
+ </MkRange>
- <MkInput :modelValue="getHex(params.fgColor)" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) params.fgColor = c; }">
- <template #label>{{ i18n.ts._imageFrameEditor.textColor }}</template>
- </MkInput>
+ <MkInput :modelValue="getHex(params.bgColor)" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) params.bgColor = c; }">
+ <template #label>{{ i18n.ts._imageFrameEditor.backgroundColor }}</template>
+ </MkInput>
- <MkSelect
- v-model="params.font" :items="[
- { label: i18n.ts._imageFrameEditor.fontSansSerif, value: 'sans-serif' },
- { label: i18n.ts._imageFrameEditor.fontSerif, value: 'serif' },
- ]"
- >
- <template #label>{{ i18n.ts._imageFrameEditor.font }}</template>
- </MkSelect>
+ <MkInput :modelValue="getHex(params.fgColor)" type="color" @update:modelValue="v => { const c = getRgb(v); if (c != null) params.fgColor = c; }">
+ <template #label>{{ i18n.ts._imageFrameEditor.textColor }}</template>
+ </MkInput>
- <MkFolder :defaultOpen="params.labelTop.enabled">
- <template #label>{{ i18n.ts._imageFrameEditor.header }}</template>
+ <MkSelect
+ v-model="params.font" :items="[
+ { label: i18n.ts._imageFrameEditor.fontSansSerif, value: 'sans-serif' },
+ { label: i18n.ts._imageFrameEditor.fontSerif, value: 'serif' },
+ ]"
+ >
+ <template #label>{{ i18n.ts._imageFrameEditor.font }}</template>
+ </MkSelect>
- <div class="_gaps">
- <MkSwitch v-model="params.labelTop.enabled">
- <template #label>{{ i18n.ts.show }}</template>
- </MkSwitch>
+ <MkFolder :defaultOpen="params.labelTop.enabled">
+ <template #label>{{ i18n.ts._imageFrameEditor.header }}</template>
- <MkRange v-model="params.labelTop.padding" :min="0.01" :max="0.5" :step="0.01" :continuousUpdate="true">
- <template #label>{{ i18n.ts._imageFrameEditor.labelThickness }}</template>
- </MkRange>
+ <div class="_gaps">
+ <MkSwitch v-model="params.labelTop.enabled">
+ <template #label>{{ i18n.ts.show }}</template>
+ </MkSwitch>
- <MkRange v-model="params.labelTop.scale" :min="0.5" :max="2.0" :step="0.01" :continuousUpdate="true">
- <template #label>{{ i18n.ts._imageFrameEditor.labelScale }}</template>
- </MkRange>
+ <MkRange v-model="params.labelTop.padding" :min="0.01" :max="0.5" :step="0.01" :continuousUpdate="true">
+ <template #label>{{ i18n.ts._imageFrameEditor.labelThickness }}</template>
+ </MkRange>
- <MkSwitch v-model="params.labelTop.centered">
- <template #label>{{ i18n.ts._imageFrameEditor.centered }}</template>
- </MkSwitch>
+ <MkRange v-model="params.labelTop.scale" :min="0.5" :max="2.0" :step="0.01" :continuousUpdate="true">
+ <template #label>{{ i18n.ts._imageFrameEditor.labelScale }}</template>
+ </MkRange>
- <MkInput v-model="params.labelTop.textBig">
- <template #label>{{ i18n.ts._imageFrameEditor.captionMain }}</template>
- </MkInput>
+ <MkSwitch v-model="params.labelTop.centered">
+ <template #label>{{ i18n.ts._imageFrameEditor.centered }}</template>
+ </MkSwitch>
- <MkTextarea v-model="params.labelTop.textSmall">
- <template #label>{{ i18n.ts._imageFrameEditor.captionSub }}</template>
- </MkTextarea>
+ <MkInput v-model="params.labelTop.textBig">
+ <template #label>{{ i18n.ts._imageFrameEditor.captionMain }}</template>
+ </MkInput>
- <MkSwitch v-model="params.labelTop.withQrCode">
- <template #label>{{ i18n.ts._imageFrameEditor.withQrCode }}</template>
- </MkSwitch>
- </div>
- </MkFolder>
+ <MkTextarea v-model="params.labelTop.textSmall">
+ <template #label>{{ i18n.ts._imageFrameEditor.captionSub }}</template>
+ </MkTextarea>
- <MkFolder :defaultOpen="params.labelBottom.enabled">
- <template #label>{{ i18n.ts._imageFrameEditor.footer }}</template>
+ <MkSwitch v-model="params.labelTop.withQrCode">
+ <template #label>{{ i18n.ts._imageFrameEditor.withQrCode }}</template>
+ </MkSwitch>
+ </div>
+ </MkFolder>
- <div class="_gaps">
- <MkSwitch v-model="params.labelBottom.enabled">
- <template #label>{{ i18n.ts.show }}</template>
- </MkSwitch>
+ <MkFolder :defaultOpen="params.labelBottom.enabled">
+ <template #label>{{ i18n.ts._imageFrameEditor.footer }}</template>
- <MkRange v-model="params.labelBottom.padding" :min="0.01" :max="0.5" :step="0.01" :continuousUpdate="true">
- <template #label>{{ i18n.ts._imageFrameEditor.labelThickness }}</template>
- </MkRange>
+ <div class="_gaps">
+ <MkSwitch v-model="params.labelBottom.enabled">
+ <template #label>{{ i18n.ts.show }}</template>
+ </MkSwitch>
- <MkRange v-model="params.labelBottom.scale" :min="0.5" :max="2.0" :step="0.01" :continuousUpdate="true">
- <template #label>{{ i18n.ts._imageFrameEditor.labelScale }}</template>
- </MkRange>
+ <MkRange v-model="params.labelBottom.padding" :min="0.01" :max="0.5" :step="0.01" :continuousUpdate="true">
+ <template #label>{{ i18n.ts._imageFrameEditor.labelThickness }}</template>
+ </MkRange>
- <MkSwitch v-model="params.labelBottom.centered">
- <template #label>{{ i18n.ts._imageFrameEditor.centered }}</template>
- </MkSwitch>
+ <MkRange v-model="params.labelBottom.scale" :min="0.5" :max="2.0" :step="0.01" :continuousUpdate="true">
+ <template #label>{{ i18n.ts._imageFrameEditor.labelScale }}</template>
+ </MkRange>
- <MkInput v-model="params.labelBottom.textBig">
- <template #label>{{ i18n.ts._imageFrameEditor.captionMain }}</template>
- </MkInput>
+ <MkSwitch v-model="params.labelBottom.centered">
+ <template #label>{{ i18n.ts._imageFrameEditor.centered }}</template>
+ </MkSwitch>
- <MkTextarea v-model="params.labelBottom.textSmall">
- <template #label>{{ i18n.ts._imageFrameEditor.captionSub }}</template>
- </MkTextarea>
+ <MkInput v-model="params.labelBottom.textBig">
+ <template #label>{{ i18n.ts._imageFrameEditor.captionMain }}</template>
+ </MkInput>
- <MkSwitch v-model="params.labelBottom.withQrCode">
- <template #label>{{ i18n.ts._imageFrameEditor.withQrCode }}</template>
- </MkSwitch>
- </div>
- </MkFolder>
+ <MkTextarea v-model="params.labelBottom.textSmall">
+ <template #label>{{ i18n.ts._imageFrameEditor.captionSub }}</template>
+ </MkTextarea>
- <MkInfo>
- <div>{{ i18n.ts._imageFrameEditor.availableVariables }}:</div>
- <div><code class="_selectableAtomic">{filename}</code> - {{ i18n.ts._imageEditing._vars.filename }}</div>
- <div><code class="_selectableAtomic">{filename_without_ext}</code> - {{ i18n.ts._imageEditing._vars.filename_without_ext }}</div>
- <div><code class="_selectableAtomic">{caption}</code> - {{ i18n.ts._imageEditing._vars.caption }}</div>
- <div><code class="_selectableAtomic">{year}</code> - {{ i18n.ts._imageEditing._vars.year }}</div>
- <div><code class="_selectableAtomic">{month}</code> - {{ i18n.ts._imageEditing._vars.month }}</div>
- <div><code class="_selectableAtomic">{day}</code> - {{ i18n.ts._imageEditing._vars.day }}</div>
- <div><code class="_selectableAtomic">{hour}</code> - {{ i18n.ts._imageEditing._vars.hour }}</div>
- <div><code class="_selectableAtomic">{minute}</code> - {{ i18n.ts._imageEditing._vars.minute }}</div>
- <div><code class="_selectableAtomic">{second}</code> - {{ i18n.ts._imageEditing._vars.second }}</div>
- <div><code class="_selectableAtomic">{0month}</code> - {{ i18n.ts._imageEditing._vars.month }} ({{ i18n.ts.zeroPadding }})</div>
- <div><code class="_selectableAtomic">{0day}</code> - {{ i18n.ts._imageEditing._vars.day }} ({{ i18n.ts.zeroPadding }})</div>
- <div><code class="_selectableAtomic">{0hour}</code> - {{ i18n.ts._imageEditing._vars.hour }} ({{ i18n.ts.zeroPadding }})</div>
- <div><code class="_selectableAtomic">{0minute}</code> - {{ i18n.ts._imageEditing._vars.minute }} ({{ i18n.ts.zeroPadding }})</div>
- <div><code class="_selectableAtomic">{0second}</code> - {{ i18n.ts._imageEditing._vars.second }} ({{ i18n.ts.zeroPadding }})</div>
- <div><code class="_selectableAtomic">{camera_model}</code> - {{ i18n.ts._imageEditing._vars.camera_model }}</div>
- <div><code class="_selectableAtomic">{camera_lens_model}</code> - {{ i18n.ts._imageEditing._vars.camera_lens_model }}</div>
- <div><code class="_selectableAtomic">{camera_mm}</code> - {{ i18n.ts._imageEditing._vars.camera_mm }}</div>
- <div><code class="_selectableAtomic">{camera_mm_35}</code> - {{ i18n.ts._imageEditing._vars.camera_mm_35 }}</div>
- <div><code class="_selectableAtomic">{camera_f}</code> - {{ i18n.ts._imageEditing._vars.camera_f }}</div>
- <div><code class="_selectableAtomic">{camera_s}</code> - {{ i18n.ts._imageEditing._vars.camera_s }}</div>
- <div><code class="_selectableAtomic">{camera_iso}</code> - {{ i18n.ts._imageEditing._vars.camera_iso }}</div>
- <div><code class="_selectableAtomic">{gps_lat}</code> - {{ i18n.ts._imageEditing._vars.gps_lat }}</div>
- <div><code class="_selectableAtomic">{gps_long}</code> - {{ i18n.ts._imageEditing._vars.gps_long }}</div>
- </MkInfo>
- </div>
+ <MkSwitch v-model="params.labelBottom.withQrCode">
+ <template #label>{{ i18n.ts._imageFrameEditor.withQrCode }}</template>
+ </MkSwitch>
+ </div>
+ </MkFolder>
+
+ <MkInfo>
+ <div>{{ i18n.ts._imageFrameEditor.availableVariables }}:</div>
+ <div><code class="_selectableAtomic">{filename}</code> - {{ i18n.ts._imageEditing._vars.filename }}</div>
+ <div><code class="_selectableAtomic">{filename_without_ext}</code> - {{ i18n.ts._imageEditing._vars.filename_without_ext }}</div>
+ <div><code class="_selectableAtomic">{caption}</code> - {{ i18n.ts._imageEditing._vars.caption }}</div>
+ <div><code class="_selectableAtomic">{year}</code> - {{ i18n.ts._imageEditing._vars.year }}</div>
+ <div><code class="_selectableAtomic">{month}</code> - {{ i18n.ts._imageEditing._vars.month }}</div>
+ <div><code class="_selectableAtomic">{day}</code> - {{ i18n.ts._imageEditing._vars.day }}</div>
+ <div><code class="_selectableAtomic">{hour}</code> - {{ i18n.ts._imageEditing._vars.hour }}</div>
+ <div><code class="_selectableAtomic">{minute}</code> - {{ i18n.ts._imageEditing._vars.minute }}</div>
+ <div><code class="_selectableAtomic">{second}</code> - {{ i18n.ts._imageEditing._vars.second }}</div>
+ <div><code class="_selectableAtomic">{0month}</code> - {{ i18n.ts._imageEditing._vars.month }} ({{ i18n.ts.zeroPadding }})</div>
+ <div><code class="_selectableAtomic">{0day}</code> - {{ i18n.ts._imageEditing._vars.day }} ({{ i18n.ts.zeroPadding }})</div>
+ <div><code class="_selectableAtomic">{0hour}</code> - {{ i18n.ts._imageEditing._vars.hour }} ({{ i18n.ts.zeroPadding }})</div>
+ <div><code class="_selectableAtomic">{0minute}</code> - {{ i18n.ts._imageEditing._vars.minute }} ({{ i18n.ts.zeroPadding }})</div>
+ <div><code class="_selectableAtomic">{0second}</code> - {{ i18n.ts._imageEditing._vars.second }} ({{ i18n.ts.zeroPadding }})</div>
+ <div><code class="_selectableAtomic">{camera_model}</code> - {{ i18n.ts._imageEditing._vars.camera_model }}</div>
+ <div><code class="_selectableAtomic">{camera_lens_model}</code> - {{ i18n.ts._imageEditing._vars.camera_lens_model }}</div>
+ <div><code class="_selectableAtomic">{camera_mm}</code> - {{ i18n.ts._imageEditing._vars.camera_mm }}</div>
+ <div><code class="_selectableAtomic">{camera_mm_35}</code> - {{ i18n.ts._imageEditing._vars.camera_mm_35 }}</div>
+ <div><code class="_selectableAtomic">{camera_f}</code> - {{ i18n.ts._imageEditing._vars.camera_f }}</div>
+ <div><code class="_selectableAtomic">{camera_s}</code> - {{ i18n.ts._imageEditing._vars.camera_s }}</div>
+ <div><code class="_selectableAtomic">{camera_iso}</code> - {{ i18n.ts._imageEditing._vars.camera_iso }}</div>
+ <div><code class="_selectableAtomic">{gps_lat}</code> - {{ i18n.ts._imageEditing._vars.gps_lat }}</div>
+ <div><code class="_selectableAtomic">{gps_long}</code> - {{ i18n.ts._imageEditing._vars.gps_long }}</div>
+ </MkInfo>
</div>
- </div>
- </div>
+ </template>
+ </MkPreviewWithControls>
</MkModalWindow>
</template>
@@ -157,12 +156,12 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, useTemplateRef, watch, onMounted, onUnmounted, reactive, nextTick } from 'vue';
import ExifReader from 'exifreader';
import { throttle } from 'throttle-debounce';
+import MkPreviewWithControls from './MkPreviewWithControls.vue';
import type { ImageFrameParams, ImageFramePreset } from '@/utility/image-frame-renderer/ImageFrameRenderer.js';
import { ImageFrameRenderer } from '@/utility/image-frame-renderer/ImageFrameRenderer.js';
import { i18n } from '@/i18n.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
import MkSelect from '@/components/MkSelect.vue';
-import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkRange from '@/components/MkRange.vue';
@@ -173,8 +172,6 @@ import * as os from '@/os.js';
import { deepClone } from '@/utility/clone.js';
import { ensureSignin } from '@/i.js';
import { genId } from '@/utility/id.js';
-import { useMkSelect } from '@/composables/use-mkselect.js';
-import { prefer } from '@/preferences.js';
const $i = ensureSignin();
@@ -393,7 +390,7 @@ async function save() {
}
function getHex(c: [number, number, number]) {
- return `#${c.map(x => (x * 255).toString(16).padStart(2, '0')).join('')}`;
+ return `#${c.map(x => Math.round(x * 255).toString(16).padStart(2, '0')).join('')}`;
}
function getRgb(hex: string | number): [number, number, number] | null {
@@ -412,33 +409,6 @@ function getRgb(hex: string | number): [number, number, number] | null {
</script>
<style module>
-.root {
- container-type: inline-size;
- height: 100%;
-}
-
-.container {
- height: 100%;
- display: grid;
- grid-template-columns: 1fr 400px;
-}
-
-.preview {
- position: relative;
- background-color: var(--MI_THEME-bg);
- background-image: linear-gradient(135deg, transparent 30%, var(--MI_THEME-panel) 30%, var(--MI_THEME-panel) 50%, transparent 50%, transparent 80%, var(--MI_THEME-panel) 80%, var(--MI_THEME-panel) 100%);
- background-size: 20px 20px;
-}
-
-.animatedBg {
- animation: bg 1.2s linear infinite;
-}
-
-@keyframes bg {
- 0% { background-position: 0 0; }
- 100% { background-position: -20px -20px; }
-}
-
.previewContainer {
display: flex;
flex-direction: column;
@@ -495,15 +465,4 @@ function getRgb(hex: string | number): [number, number, number] | null {
box-sizing: border-box;
object-fit: contain;
}
-
-.controls {
- overflow-y: scroll;
-}
-
-@container (max-width: 800px) {
- .container {
- grid-template-columns: 1fr;
- grid-template-rows: 1fr 1fr;
- }
-}
</style>
diff --git a/packages/frontend/src/components/MkImgPreviewDialog.vue b/packages/frontend/src/components/MkImgPreviewDialog.vue
index e17a1651cf..530b6c45db 100644
--- a/packages/frontend/src/components/MkImgPreviewDialog.vue
+++ b/packages/frontend/src/components/MkImgPreviewDialog.vue
@@ -11,10 +11,11 @@ SPDX-License-Identifier: AGPL-3.0-only
@close="close"
@esc="close"
@click="close"
+ @closed="emit('closed')"
>
<template #header>{{ file.name }}</template>
<div :class="$style.container">
- <img :src="file.url" :alt="file.comment ?? file.name" :class="$style.img"/>
+ <img :src="file.url" :alt="file.comment || file.name" :class="$style.img"/>
</div>
</MkModalWindow>
</template>
@@ -27,6 +28,10 @@ defineProps<{
file: Misskey.entities.DriveFile;
}>();
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
+
const modal = ref<typeof MkModalWindow | null>(null);
function close() {
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index 983a0932c3..a61836e101 100644
--- a/packages/frontend/src/components/MkImgWithBlurhash.vue
+++ b/packages/frontend/src/components/MkImgWithBlurhash.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
draggable="false"
tabindex="-1"
style="-webkit-user-drag: none;"
- />
+ ></canvas>
<img
v-show="!hide"
key="img"
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index 7f052dff94..aebeefe165 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@input="onInput"
>
<datalist v-if="datalist" :id="id">
- <option v-for="data in datalist" :key="data" :value="data"/>
+ <option v-for="data in datalist" :key="data" :value="data"></option>
</datalist>
<div ref="suffixEl" :class="$style.suffix"><slot name="suffix"></slot></div>
</div>
@@ -88,10 +88,11 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (ev: 'change', _ev: KeyboardEvent): void;
+ (ev: 'change', _ev: InputEvent): void;
(ev: 'keydown', _ev: KeyboardEvent): void;
(ev: 'enter', _ev: KeyboardEvent): void;
(ev: 'update:modelValue', value: ModelValueType<T>): void;
+ (ev: 'savingStateChange', saved: boolean, invalid: boolean): void;
}>();
const { modelValue } = toRefs(props);
@@ -111,10 +112,9 @@ const height =
let autocompleteWorker: Autocomplete | null = null;
const focus = () => inputEl.value?.focus();
-const onInput = (event: Event) => {
- const ev = event as KeyboardEvent;
+const onInput = (event: InputEvent) => {
changed.value = true;
- emit('change', ev);
+ emit('change', event);
};
const onKeydown = (ev: KeyboardEvent) => {
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return;
@@ -153,6 +153,10 @@ watch(v, () => {
invalid.value = inputEl.value?.validity.badInput ?? true;
});
+watch([changed, invalid], ([newChanged, newInvalid]) => {
+ emit('savingStateChange', newChanged, newInvalid);
+}, { immediate: true });
+
// このコンポーネントが作成された時、非表示状態である場合がある
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
useInterval(() => {
diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue
index 7902151921..130a0e9986 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.vue
+++ b/packages/frontend/src/components/MkInstanceCardMini.vue
@@ -33,7 +33,7 @@ misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, spa
chartValues.value = res.requests.received;
});
-function getInstanceIcon(instance): string {
+function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png';
}
</script>
diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue
index 13048a2e1b..368fa5be27 100644
--- a/packages/frontend/src/components/MkInstanceStats.vue
+++ b/packages/frontend/src/components/MkInstanceStats.vue
@@ -57,10 +57,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, computed, useTemplateRef } from 'vue';
import { Chart } from 'chart.js';
-import MkSelect from '@/components/MkSelect.vue';
import type { MkSelectItem, ItemOption } from '@/components/MkSelect.vue';
-import MkChart from '@/components/MkChart.vue';
import type { ChartSrc } from '@/components/MkChart.vue';
+import MkSelect from '@/components/MkSelect.vue';
+import MkChart from '@/components/MkChart.vue';
import { useChartTooltip } from '@/composables/use-chart-tooltip.js';
import { $i } from '@/i.js';
import * as os from '@/os.js';
@@ -172,7 +172,14 @@ const { handler: externalTooltipHandler2 } = useChartTooltip({
position: 'middle',
});
-function createDoughnut(chartEl, tooltip, data) {
+type ChartData = {
+ name: string,
+ color: string,
+ value: number,
+ onClick?: () => void,
+}[];
+
+function createDoughnut(chartEl: HTMLCanvasElement, tooltip: ReturnType<typeof useChartTooltip>['handler'], data: ChartData) {
const chartInstance = new Chart(chartEl, {
type: 'doughnut',
data: {
@@ -198,8 +205,8 @@ function createDoughnut(chartEl, tooltip, data) {
onClick: (ev) => {
if (ev.native == null) return;
const hit = chartInstance.getElementsAtEventForMode(ev.native, 'nearest', { intersect: true }, false)[0];
- if (hit && data[hit.index].onClick) {
- data[hit.index].onClick();
+ if (hit != null) {
+ data[hit.index].onClick?.();
}
},
plugins: {
@@ -223,16 +230,9 @@ function createDoughnut(chartEl, tooltip, data) {
onMounted(() => {
misskeyApiGet('federation/stats', { limit: 30 }).then(fedStats => {
- type ChartData = {
- name: string,
- color: string | null,
- value: number,
- onClick?: () => void,
- }[];
-
const subs: ChartData = fedStats.topSubInstances.map(x => ({
name: x.host,
- color: x.themeColor,
+ color: x.themeColor ?? '#888888',
value: x.followersCount,
onClick: () => {
os.pageWindow(`/instance-info/${x.host}`);
@@ -245,11 +245,11 @@ onMounted(() => {
value: fedStats.otherFollowersCount,
});
- createDoughnut(subDoughnutEl.value, externalTooltipHandler1, subs);
+ if (subDoughnutEl.value != null) createDoughnut(subDoughnutEl.value, externalTooltipHandler1, subs);
const pubs: ChartData = fedStats.topPubInstances.map(x => ({
name: x.host,
- color: x.themeColor,
+ color: x.themeColor ?? '#888888',
value: x.followingCount,
onClick: () => {
os.pageWindow(`/instance-info/${x.host}`);
@@ -262,7 +262,7 @@ onMounted(() => {
value: fedStats.otherFollowingCount,
});
- createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, pubs);
+ if (pubDoughnutEl.value != null) createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, pubs);
});
});
</script>
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index d8725ade0b..0c73df4e2d 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -8,13 +8,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }">
<div class="main">
<template v-for="item in items" :key="item.text">
- <button v-if="item.action" v-click-anime class="_button item" @click="$event => { item.action($event); close(); }">
+ <button v-if="item.action != null" v-click-anime class="_button item" @click="$event => { item.action!($event); close(); }">
<i class="icon" :class="item.icon"></i>
<div class="text">{{ item.text }}</div>
<span v-if="item.indicate && item.indicateValue" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span>
<span v-else-if="item.indicate" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</button>
- <MkA v-else v-click-anime :to="item.to" class="item" @click.passive="close()">
+ <MkA v-else-if="item.to != null" v-click-anime :to="item.to" class="item" @click.passive="close()">
<i class="icon" :class="item.icon"></i>
<div class="text">{{ item.text }}</div>
<span v-if="item.indicate && item.indicateValue" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span>
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index e3bb39549f..efcbf26a29 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -100,6 +100,7 @@ import { hms } from '@/filters/hms.js';
import MkMediaRange from '@/components/MkMediaRange.vue';
import { $i, iAmModerator } from '@/i.js';
import { prefer } from '@/preferences.js';
+import { canRevealFile, shouldHideFileByDefault } from '@/utility/sensitive-file.js';
const props = defineProps<{
audio: Misskey.entities.DriveFile;
@@ -154,16 +155,11 @@ function hasFocus() {
const playerEl = useTemplateRef('playerEl');
const audioEl = useTemplateRef('audioEl');
-// eslint-disable-next-line vue/no-setup-props-reactivity-loss
-const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.audio.isSensitive && prefer.s.nsfw !== 'ignore'));
+const hide = ref(shouldHideFileByDefault(props.audio));
async function reveal() {
- if (props.audio.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
- const { canceled } = await os.confirm({
- type: 'question',
- text: i18n.ts.sensitiveMediaRevealConfirm,
- });
- if (canceled) return;
+ if (!(await canRevealFile(props.audio))) {
+ return;
}
hide.value = false;
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index 7730e01a9f..fd86b61b87 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
- <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="reveal">
+ <div v-else-if="hide" :class="$style.sensitive" @click="reveal">
<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
<b>{{ i18n.ts.sensitive }}</b>
<span>{{ i18n.ts.clickToShow }}</span>
@@ -27,23 +27,18 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref } from 'vue';
import * as Misskey from 'misskey-js';
import { i18n } from '@/i18n.js';
-import * as os from '@/os.js';
import MkMediaAudio from '@/components/MkMediaAudio.vue';
-import { prefer } from '@/preferences.js';
+import { shouldHideFileByDefault, canRevealFile } from '@/utility/sensitive-file.js';
const props = defineProps<{
media: Misskey.entities.DriveFile;
}>();
-const hide = ref(true);
+const hide = ref(shouldHideFileByDefault(props.media));
async function reveal() {
- if (props.media.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
- const { canceled } = await os.confirm({
- type: 'question',
- text: i18n.ts.sensitiveMediaRevealConfirm,
- });
- if (canceled) return;
+ if (!(await canRevealFile(props.media))) {
+ return;
}
hide.value = false;
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index f59d15d9a2..4236bd943a 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="reveal">
+<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="reveal" @contextmenu.stop="onContextmenu">
<component
:is="disableImageLink ? 'div' : 'a'"
v-bind="disableImageLink ? {
@@ -77,6 +77,7 @@ import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { $i, iAmModerator } from '@/i.js';
import { prefer } from '@/preferences.js';
+import { shouldHideFileByDefault, canRevealFile } from '@/utility/sensitive-file.js';
const props = withDefaults(defineProps<{
image: Misskey.entities.DriveFile;
@@ -99,19 +100,15 @@ const url = computed(() => (props.raw || prefer.s.loadRawImages)
: props.image.thumbnailUrl!,
);
-async function reveal(ev: MouseEvent) {
+async function reveal(ev: PointerEvent) {
if (!props.controls) {
return;
}
if (hide.value) {
ev.stopPropagation();
- if (props.image.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
- const { canceled } = await os.confirm({
- type: 'question',
- text: i18n.ts.sensitiveMediaRevealConfirm,
- });
- if (canceled) return;
+ if (!(await canRevealFile(props.image))) {
+ return;
}
hide.value = false;
@@ -119,14 +116,14 @@ async function reveal(ev: MouseEvent) {
}
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
-watch(() => props.image, () => {
- hide.value = (prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.image.isSensitive && prefer.s.nsfw !== 'ignore');
+watch(() => props.image, (newImage) => {
+ hide.value = shouldHideFileByDefault(newImage);
}, {
deep: true,
immediate: true,
});
-function showMenu(ev: MouseEvent) {
+function getMenu() {
const menuItems: MenuItem[] = [];
menuItems.push({
@@ -191,9 +188,16 @@ function showMenu(ev: MouseEvent) {
});
}
- os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
+ return menuItems;
}
+function showMenu(ev: PointerEvent) {
+ os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
+}
+
+function onContextmenu(ev: PointerEvent) {
+ os.contextMenu(getMenu(), ev);
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index bfc8179e13..9090e74bb6 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -4,13 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div>
+<div :class="$style.root">
<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/>
<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container">
<div
ref="gallery"
:class="[
$style.medias,
+ ...(prefer.s.showMediaListByGridInWideArea ? [$style.gridInWideArea] : []),
count === 1 ? [$style.n1, {
[$style.n116_9]: prefer.s.mediaListWithOneImageAppearance === '16_9',
[$style.n11_1]: prefer.s.mediaListWithOneImageAppearance === '1_1',
@@ -107,8 +108,10 @@ onMounted(() => {
src: media.url,
w: media.properties.width,
h: media.properties.height,
- alt: media.comment ?? media.name,
- comment: media.comment ?? media.name,
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ alt: media.comment || media.name,
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ comment: media.comment || media.name,
};
if (media.properties.orientation != null && media.properties.orientation >= 5) {
[item.w, item.h] = [item.h, item.w];
@@ -155,8 +158,10 @@ onMounted(() => {
[itemData.w, itemData.h] = [itemData.h, itemData.w];
}
itemData.msrc = file.thumbnailUrl ?? undefined;
- itemData.alt = file.comment ?? file.name;
- itemData.comment = file.comment ?? file.name;
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ itemData.alt = file.comment || file.name;
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ itemData.comment = file.comment || file.name;
itemData.thumbCropped = true;
return itemData;
@@ -226,6 +231,10 @@ defineExpose({
</script>
<style lang="scss" module>
+.root {
+ container-type: inline-size;
+}
+
.container {
position: relative;
width: 100%;
@@ -309,6 +318,20 @@ defineExpose({
border-radius: 8px;
}
+@container (min-width: 500px) {
+ .medias.gridInWideArea {
+ display: grid;
+ aspect-ratio: auto;
+ grid-template-columns: repeat(4, 1fr);
+ grid-template-rows: auto;
+ grid-gap: 8px;
+
+ > .media {
+ aspect-ratio: 1 / 1;
+ }
+ }
+}
+
:global(.pswp) {
--pswp-root-z-index: var(--mk-pswp-root-z-index, 2000700) !important;
--pswp-bg: var(--MI_THEME-modalBg) !important;
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index b0f7a909d3..4d06e42c05 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -124,6 +124,7 @@ import hasAudio from '@/utility/media-has-audio.js';
import MkMediaRange from '@/components/MkMediaRange.vue';
import { $i, iAmModerator } from '@/i.js';
import { prefer } from '@/preferences.js';
+import { shouldHideFileByDefault, canRevealFile } from '@/utility/sensitive-file.js';
const props = defineProps<{
video: Misskey.entities.DriveFile;
@@ -176,15 +177,11 @@ function hasFocus() {
}
// eslint-disable-next-line vue/no-setup-props-reactivity-loss
-const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.video.isSensitive && prefer.s.nsfw !== 'ignore'));
+const hide = ref(shouldHideFileByDefault(props.video));
async function reveal() {
- if (props.video.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
- const { canceled } = await os.confirm({
- type: 'question',
- text: i18n.ts.sensitiveMediaRevealConfirm,
- });
- if (canceled) return;
+ if (!(await canRevealFile(props.video))) {
+ return;
}
hide.value = false;
@@ -193,7 +190,7 @@ async function reveal() {
// Menu
const menuShowing = ref(false);
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
const menu: MenuItem[] = [
// TODO: 再生キューに追加
{
@@ -708,7 +705,7 @@ onDeactivated(() => {
.controlButton {
padding: 6px;
border-radius: calc(var(--MI-radius) / 2);
- transition: background-color .2s ease-in-out;
+ transition: background-color .15s ease;
font-size: 1.05rem;
&:hover {
@@ -763,4 +760,21 @@ onDeactivated(() => {
}
}
}
+
+@container (max-width: 300px) {
+ .videoControls {
+ grid-template-areas:
+ "left . right"
+ "seekbar seekbar seekbar";
+ grid-template-columns: auto 1fr auto;
+ }
+
+ .controlsTime {
+ display: none;
+ }
+
+ .controlsVolume {
+ display: none;
+ }
+}
</style>
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 6c8fac934c..b618dab6b2 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span><MkEllipsis/></span>
</span>
- <div v-else-if="item.type === 'component'" role="menuitem" tabindex="-1" :class="[$style.componentItem]">
+ <div v-else-if="item.type === 'component'" role="menuitem" tabindex="-1">
<component :is="item.component" v-bind="item.props"/>
</div>
@@ -316,16 +316,27 @@ function onItemMouseLeave() {
if (childCloseTimer) window.clearTimeout(childCloseTimer);
}
-async function showRadioOptions(item: MenuRadio, ev: Event) {
+async function showRadioOptions(item: MenuRadio, ev: MouseEvent | PointerEvent | KeyboardEvent) {
const children: MenuItem[] = Object.keys(item.options).map<MenuRadioOption>(key => {
const value = item.options[key];
return {
type: 'radioOption',
text: key,
action: () => {
- item.ref = value;
+ if ('value' in item.ref) {
+ item.ref.value = value;
+ } else {
+ // @ts-expect-error リアクティビティは保たれる
+ item.ref = value;
+ }
},
- active: computed(() => item.ref === value),
+ active: computed(() => {
+ if ('value' in item.ref) {
+ return item.ref.value === value;
+ } else {
+ return item.ref === value;
+ }
+ }),
};
});
@@ -341,7 +352,7 @@ async function showRadioOptions(item: MenuRadio, ev: Event) {
}
}
-async function showChildren(item: MenuParent, ev: Event) {
+async function showChildren(item: MenuParent, ev: MouseEvent | PointerEvent | KeyboardEvent) {
ev.stopPropagation();
const children: MenuItem[] = await (async () => {
@@ -371,7 +382,7 @@ async function showChildren(item: MenuParent, ev: Event) {
}
}
-function clicked(fn: MenuAction, ev: MouseEvent, doClose = true) {
+function clicked(fn: MenuAction, ev: PointerEvent, doClose = true) {
fn(ev);
if (!doClose) return;
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 660d5a26be..92174d8ef7 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -184,8 +184,8 @@ const align = () => {
const width = content.value!.offsetWidth;
const height = content.value!.offsetHeight;
- let left;
- let top;
+ let left = 0;
+ let top = 0;
const x = anchorRect.left + (fixed.value ? 0 : window.scrollX);
const y = anchorRect.top + (fixed.value ? 0 : window.scrollY);
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index a7299d2961..c78cc44425 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -468,8 +468,12 @@ if (!props.mock) {
}
}
-function renote() {
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+async function renote() {
+ if (props.mock) return;
+
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
+
showMovedDialog();
const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock });
@@ -478,11 +482,12 @@ function renote() {
subscribeManuallyToNoteCapture();
}
-function reply(): void {
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
- if (props.mock) {
- return;
- }
+async function reply() {
+ if (props.mock) return;
+
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
+
os.post({
reply: appearNote,
channel: appearNote.channel,
@@ -491,8 +496,10 @@ function reply(): void {
});
}
-function react(): void {
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+async function react() {
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
+
showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') {
sound.playMisskeySfx('reaction');
@@ -587,7 +594,7 @@ function toggleReact() {
}
}
-function onContextmenu(ev: MouseEvent): void {
+function onContextmenu(ev: PointerEvent): void {
if (props.mock) {
return;
}
@@ -621,10 +628,12 @@ async function clip(): Promise<void> {
os.popupMenu(await getNoteClipMenu({ note: note, currentClip: currentClip?.value }), clipButton.value).then(focus);
}
-function showRenoteMenu(): void {
+async function showRenoteMenu() {
if (props.mock) {
return;
}
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
function getUnrenote(): MenuItem {
return {
@@ -649,7 +658,6 @@ function showRenoteMenu(): void {
};
if (isMyRenote) {
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
os.popupMenu([
renoteDetailsMenu,
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 47bf365877..083e3e5da0 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note"/>
</div>
- <MkNoteSub v-if="appearNote.replyId" :note="appearNote.reply" :class="$style.replyTo"/>
+ <MkNoteSub v-if="appearNote.replyId" :note="appearNote?.reply ?? null" :class="$style.replyTo"/>
<div v-if="isRenote" :class="$style.renote">
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
<i class="ti ti-repeat" style="margin-right: 4px;"></i>
@@ -143,8 +143,6 @@ SPDX-License-Identifier: AGPL-3.0-only
:reactionEmojis="$appearNote.reactionEmojis"
:myReaction="$appearNote.myReaction"
:noteId="appearNote.id"
- :maxNumber="16"
- @mockUpdateMyReaction="emitUpdReaction"
/>
<button class="_button" :class="$style.noteFooterButton" @click="reply()">
<i class="ti ti-arrow-back-up"></i>
@@ -233,7 +231,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, inject, markRaw, onMounted, provide, ref, useTemplateRef } from 'vue';
+import { computed, inject, markRaw, provide, ref, useTemplateRef } from 'vue';
import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js';
import { isLink } from '@@/js/is-link.js';
@@ -358,7 +356,9 @@ const keymap = {
if (!prefer.s.showClipButtonInNoteFooter) return;
clip();
},
- 'o': () => galleryEl.value?.openGallery(),
+ 'o': () => {
+ galleryEl.value?.openGallery();
+ },
'v|enter': () => {
if (appearNote.cw != null) {
showContent.value = !showContent.value;
@@ -448,8 +448,10 @@ if (appearNote.reactionAcceptance === 'likeOnly') {
});
}
-function renote() {
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+async function renote() {
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
+
showMovedDialog();
const { menu } = getRenoteMenu({ note: note, renoteButton });
@@ -459,8 +461,10 @@ function renote() {
subscribeManuallyToNoteCapture();
}
-function reply(): void {
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+async function reply() {
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
+
showMovedDialog();
os.post({
reply: appearNote,
@@ -470,8 +474,10 @@ function reply(): void {
});
}
-function react(): void {
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+async function react() {
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
+
showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') {
sound.playMisskeySfx('reaction');
@@ -547,7 +553,7 @@ function toggleReact() {
}
}
-function onContextmenu(ev: MouseEvent): void {
+function onContextmenu(ev: PointerEvent): void {
if (ev.target && isLink(ev.target as HTMLElement)) return;
if (window.getSelection()?.toString() !== '') return;
@@ -569,9 +575,12 @@ async function clip(): Promise<void> {
os.popupMenu(await getNoteClipMenu({ note: note }), clipButton.value).then(focus);
}
-function showRenoteMenu(): void {
+async function showRenoteMenu() {
if (!isMyRenote) return;
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
+
os.popupMenu([{
text: i18n.ts.unrenote,
icon: 'ti ti-trash',
diff --git a/packages/frontend/src/components/MkNoteDraftsDialog.vue b/packages/frontend/src/components/MkNoteDraftsDialog.vue
index 3f0a5a5247..371240ae4f 100644
--- a/packages/frontend/src/components/MkNoteDraftsDialog.vue
+++ b/packages/frontend/src/components/MkNoteDraftsDialog.vue
@@ -118,7 +118,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.draftActions" class="_buttons">
<template v-if="draft.scheduledAt != null && draft.isActuallyScheduled">
<MkButton
- :class="$style.itemButton"
small
@click="cancelSchedule(draft)"
>
@@ -126,7 +125,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkButton>
<!-- TODO
<MkButton
- :class="$style.itemButton"
small
@click="reSchedule(draft)"
>
@@ -136,7 +134,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<MkButton
v-else
- :class="$style.itemButton"
small
@click="restoreDraft(draft)"
>
@@ -147,7 +144,6 @@ SPDX-License-Identifier: AGPL-3.0-only
danger
small
:iconOnly="true"
- :class="$style.itemButton"
style="margin-left: auto;"
@click="deleteDraft(draft)"
>
diff --git a/packages/frontend/src/components/MkNoteMediaGrid.vue b/packages/frontend/src/components/MkNoteMediaGrid.vue
index 7e900b28fa..e46456d614 100644
--- a/packages/frontend/src/components/MkNoteMediaGrid.vue
+++ b/packages/frontend/src/components/MkNoteMediaGrid.vue
@@ -6,14 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<template v-for="file in note.files">
<div
- v-if="(((
- (prefer.s.nsfw === 'force' || file.isSensitive) &&
- prefer.s.nsfw !== 'ignore'
- ) || (prefer.s.dataSaver.media && file.type.startsWith('image/'))) &&
- !showingFiles.has(file.id)
- )"
+ v-if="isHiding(file)"
:class="[$style.filePreview, { [$style.square]: square }]"
- @click="showingFiles.add(file.id)"
+ @click="reveal(file)"
>
<MkDriveFileThumbnail
:file="file"
@@ -49,6 +44,7 @@ import * as Misskey from 'misskey-js';
import { notePage } from '@/filters/note.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
+import { shouldHideFileByDefault, canRevealFile } from '@/utility/sensitive-file.js';
import bytes from '@/filters/bytes.js';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
@@ -59,6 +55,24 @@ defineProps<{
}>();
const showingFiles = ref<Set<string>>(new Set());
+
+function isHiding(file: Misskey.entities.DriveFile) {
+ if (shouldHideFileByDefault(file) && !showingFiles.value.has(file.id)) {
+ if (!file.isSensitive && !file.type.startsWith('image/')) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+async function reveal(file: Misskey.entities.DriveFile) {
+ if (!(await canRevealFile(file))) {
+ return;
+ }
+
+ showingFiles.value.add(file.id);
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 45a74e3f02..6c70358c2c 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -121,7 +121,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ notification.invitation.room.name }}
</div>
<MkA v-else-if="notification.type === 'achievementEarned'" :class="$style.text" to="/my/achievements">
- {{ i18n.ts._achievements._types['_' + notification.achievement].title }}
+ {{ i18n.ts._achievements._types[`_${notification.achievement}`].title }}
</MkA>
<MkA v-else-if="notification.type === 'exportCompleted'" :class="$style.text" :to="`/my/drive/file/${notification.fileId}`">
{{ i18n.ts.showFile }}
@@ -136,15 +136,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</div>
<div v-if="notification.message" :class="$style.text" style="opacity: 0.6; font-style: oblique;">
<i class="ti ti-quote" :class="$style.quote"></i>
- <span>{{ notification.message }}</span>
+ <Mfm :text="notification.message" :author="notification.user" :plain="true" :nowrap="true"/>
<i class="ti ti-quote" :class="$style.quote"></i>
</div>
</template>
<template v-else-if="notification.type === 'receiveFollowRequest'">
<span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.receiveFollowRequest }}</span>
<div v-if="full && !followRequestDone" :class="$style.followRequestCommands">
- <MkButton :class="$style.followRequestCommandButton" rounded primary @click="acceptFollowRequest()"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton>
- <MkButton :class="$style.followRequestCommandButton" rounded danger @click="rejectFollowRequest()"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
+ <MkButton :class="$style.followRequestCommandButton" rounded primary @click="acceptFollowRequest()"><i class="ti ti-check"></i> {{ i18n.ts.accept }}</MkButton>
+ <MkButton :class="$style.followRequestCommandButton" rounded danger @click="rejectFollowRequest()"><i class="ti ti-x"></i> {{ i18n.ts.reject }}</MkButton>
</div>
</template>
<span v-else-if="notification.type === 'test'" :class="$style.text">{{ i18n.ts._notification.notificationWillBeDisplayedLikeThis }}</span>
diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue
index 7205e516d2..5300abd0cf 100644
--- a/packages/frontend/src/components/MkNotificationSelectWindow.vue
+++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue
@@ -42,7 +42,7 @@ import { i18n } from '@/i18n.js';
type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>;
const emit = defineEmits<{
- (ev: 'done', v: { excludeTypes: string[] }): void,
+ (ev: 'done', v: { excludeTypes: typeof notificationTypes[number][] }): void,
(ev: 'closed'): void,
}>();
diff --git a/packages/frontend/src/components/MkObjectView.value.vue b/packages/frontend/src/components/MkObjectView.value.vue
index 7fa8c23c6c..abc4407d2a 100644
--- a/packages/frontend/src/components/MkObjectView.value.vue
+++ b/packages/frontend/src/components/MkObjectView.value.vue
@@ -42,7 +42,7 @@ const props = defineProps<{
value: unknown;
}>();
-const collapsed = reactive({});
+const collapsed = reactive<Record<string, boolean>>({});
if (isObject(props.value)) {
for (const key in props.value) {
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index a4c8ca0095..ad8fcf283c 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -88,7 +88,7 @@ const shouldEnableInfiniteScroll = computed(() => {
return prefer.r.enableInfiniteScroll.value && !props.forceDisableInfiniteScroll;
});
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
if (ev.target && isLink(ev.target as HTMLElement)) return;
if (window.getSelection()?.toString() !== '') return;
diff --git a/packages/frontend/src/components/MkPagingButtons.vue b/packages/frontend/src/components/MkPagingButtons.vue
index fe59efd83a..10f432e50d 100644
--- a/packages/frontend/src/components/MkPagingButtons.vue
+++ b/packages/frontend/src/components/MkPagingButtons.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.buttons">
<div v-if="prevDotVisible" :class="$style.headTailButtons">
<MkButton @click="onToHeadButtonClicked">{{ min }}</MkButton>
- <span class="ti ti-dots"/>
+ <span class="ti ti-dots"></span>
</div>
<MkButton
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkButton>
<div v-if="nextDotVisible" :class="$style.headTailButtons">
- <span class="ti ti-dots"/>
+ <span class="ti ti-dots"></span>
<MkButton @click="onToTailButtonClicked">{{ max }}</MkButton>
</div>
</div>
diff --git a/packages/frontend/src/components/MkPolkadots.vue b/packages/frontend/src/components/MkPolkadots.vue
index 4f1346b685..ca7bfd3a93 100644
--- a/packages/frontend/src/components/MkPolkadots.vue
+++ b/packages/frontend/src/components/MkPolkadots.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="[$style.root, accented ? $style.accented : null, revered ? $style.revered : null]"/>
+<div :class="[$style.root, accented ? $style.accented : null, revered ? $style.revered : null]"></div>
</template>
<script lang="ts" setup>
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index 305e9b5c4f..31567d2b84 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -90,7 +90,8 @@ const pleaseLoginContext = computed<OpenOnRemoteOptions>(() => ({
const vote = async (id: number) => {
if (props.readOnly || closed.value || isVoted.value) return;
- pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ const isLoggedIn = await pleaseLogin({ openOnRemote: pleaseLoginContext.value });
+ if (!isLoggedIn) return;
const { canceled } = await os.confirm({
type: 'question',
diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue
index b7c3d1f42d..bd36a0b97a 100644
--- a/packages/frontend/src/components/MkPollEditor.vue
+++ b/packages/frontend/src/components/MkPollEditor.vue
@@ -110,7 +110,7 @@ if (props.modelValue.expiresAt) {
expiration.value = 'infinite';
}
-function onInput(i, value) {
+function onInput(i: number, value: string) {
choices.value[i] = value;
}
@@ -122,7 +122,7 @@ function add() {
// });
}
-function remove(i) {
+function remove(i: number) {
choices.value = choices.value.filter((_, _i) => _i !== i);
}
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index b3bcfcc137..d709286041 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<header :class="$style.header">
<div :class="$style.headerLeft">
<button v-if="!fixed" :class="$style.cancel" class="_button" @click="cancel"><i class="ti ti-x"></i></button>
- <button ref="accountMenuEl" v-click-anime v-tooltip="i18n.ts.account" :class="$style.account" class="_button" @click="openAccountMenu">
+ <button ref="accountMenuEl" v-click-anime v-tooltip="i18n.ts.account" class="_button" @click="openAccountMenu">
<img :class="$style.avatar" :src="(postAccount ?? $i).avatarUrl" style="border-radius: 100%;"/>
</button>
</div>
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span :class="$style.headerRightButtonText">{{ targetChannel.name }}</span>
</button>
</template>
- <button v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="targetChannel != null || visibility === 'specified'" @click="toggleLocalOnly">
+ <button v-if="visibility !== 'specified'" v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="targetChannel != null" @click="toggleLocalOnly">
<span v-if="!localOnly"><i class="ti ti-rocket"></i></span>
<span v-else><i class="ti ti-rocket-off"></i></span>
</button>
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.visibleUsers">
<span v-for="u in visibleUsers" :key="u.id" :class="$style.visibleUser">
<MkAcct :user="u"/>
- <button class="_button" style="padding: 4px 8px;" @click="removeVisibleUser(u)"><i class="ti ti-x"></i></button>
+ <button class="_button" style="padding: 4px 8px;" @click="removeVisibleUser(u.id)"><i class="ti ti-x"></i></button>
</span>
<button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button>
</div>
@@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
<div v-if="targetChannel" :class="$style.colorBar" :style="{ background: targetChannel.color }"></div>
- <textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @keyup="onKeyup" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
+ <textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @keyup="onKeyup" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"></textarea>
<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
</div>
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
@@ -108,13 +108,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</footer>
<datalist id="hashtags">
- <option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/>
+ <option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"></option>
</datalist>
</div>
</template>
<script lang="ts" setup>
-import { watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, useTemplateRef, onUnmounted } from 'vue';
+import { watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, useTemplateRef, onUnmounted, onBeforeUnmount } from 'vue';
import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor';
@@ -227,6 +227,10 @@ const targetChannel = shallowRef(props.channel);
const serverDraftId = ref<string | null>(null);
const postFormActions = getPluginHandlers('post_form_action');
+let textAutocomplete: Autocomplete | null = null;
+let cwAutocomplete: Autocomplete | null = null;
+let hashtagAutocomplete: Autocomplete | null = null;
+
const uploader = useUploader({
multiple: true,
});
@@ -329,8 +333,8 @@ const canSaveAsServerDraft = computed((): boolean => {
return canPost.value && (textLength.value > 0 || files.value.length > 0 || poll.value != null);
});
-const withHashtags = computed(store.makeGetterSetter('postFormWithHashtags'));
-const hashtags = computed(store.makeGetterSetter('postFormHashtags'));
+const withHashtags = store.model('postFormWithHashtags');
+const hashtags = store.model('postFormHashtags');
watch(text, () => {
checkMissingMention();
@@ -476,6 +480,7 @@ function togglePoll() {
}
function addTag(tag: string) {
+ if (textareaEl.value == null) return;
insertTextAtCursor(textareaEl.value, ` #${tag} `);
}
@@ -486,7 +491,7 @@ function focus() {
}
}
-function chooseFileFromPc(ev: MouseEvent) {
+function chooseFileFromPc(ev: PointerEvent) {
if (props.mock) return;
os.chooseFileFromPc({ multiple: true }).then(files => {
@@ -495,7 +500,7 @@ function chooseFileFromPc(ev: MouseEvent) {
});
}
-function chooseFileFromDrive(ev: MouseEvent) {
+function chooseFileFromDrive(ev: PointerEvent) {
if (props.mock) return;
chooseDriveFile({ multiple: true }).then(driveFiles => {
@@ -503,18 +508,18 @@ function chooseFileFromDrive(ev: MouseEvent) {
});
}
-function detachFile(id) {
+function detachFile(id: Misskey.entities.DriveFile['id']) {
files.value = files.value.filter(x => x.id !== id);
}
-function updateFileSensitive(file, sensitive) {
+function updateFileSensitive(file: Misskey.entities.DriveFile, isSensitive: boolean) {
if (props.mock) {
- emit('fileChangeSensitive', file.id, sensitive);
+ emit('fileChangeSensitive', file.id, isSensitive);
}
- files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive;
+ files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = isSensitive;
}
-function updateFileName(file, name) {
+function updateFileName(file: Misskey.entities.DriveFile, name: Misskey.entities.DriveFile['name']) {
files.value[files.value.findIndex(x => x.id === file.id)].name = name;
}
@@ -528,7 +533,6 @@ function setVisibility() {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
currentVisibility: visibility.value,
isSilenced: $i.isSilenced,
- localOnly: localOnly.value,
anchorElement: visibilityButton.value,
...(replyTargetNote.value ? { isReplyVisibilitySpecified: replyTargetNote.value.visibility === 'specified' } : {}),
}, {
@@ -704,8 +708,8 @@ function addVisibleUser() {
});
}
-function removeVisibleUser(user) {
- visibleUsers.value = erase(user, visibleUsers.value);
+function removeVisibleUser(id: string) {
+ visibleUsers.value = visibleUsers.value.filter(u => u.id !== id);
}
function clear() {
@@ -742,7 +746,8 @@ const pastedFileName = 'yyyy-MM-dd HH-mm-ss [{{number}}]';
async function onPaste(ev: ClipboardEvent) {
if (props.mock) return;
- if (!ev.clipboardData) return;
+ if (ev.clipboardData == null) return;
+ if (textareaEl.value == null) return;
let pastedFiles: File[] = [];
for (const { item, i } of Array.from(ev.clipboardData.items, (data, x) => ({ item: data, i: x }))) {
@@ -767,39 +772,42 @@ async function onPaste(ev: ClipboardEvent) {
if (!renoteTargetNote.value && !quoteId.value && paste.startsWith(url + '/notes/')) {
ev.preventDefault();
- os.confirm({
+ const { canceled } = await os.confirm({
type: 'info',
text: i18n.ts.quoteQuestion,
- }).then(({ canceled }) => {
- if (canceled) {
- insertTextAtCursor(textareaEl.value, paste);
- return;
- }
-
- quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null;
});
+
+ if (canceled) {
+ insertTextAtCursor(textareaEl.value, paste);
+ return;
+ }
+
+ quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null;
}
if (paste.length > 1000) {
ev.preventDefault();
- os.confirm({
+
+ const { canceled } = await os.confirm({
type: 'info',
text: i18n.ts.attachAsFileQuestion,
- }).then(({ canceled }) => {
- if (canceled) {
- insertTextAtCursor(textareaEl.value, paste);
- return;
- }
-
- const fileName = formatTimeString(new Date(), pastedFileName).replace(/{{number}}/g, '0');
- const file = new File([paste], `${fileName}.txt`, { type: 'text/plain' });
- uploader.addFiles([file]);
});
+
+ if (canceled) {
+ insertTextAtCursor(textareaEl.value, paste);
+ return;
+ }
+
+ const fileName = formatTimeString(new Date(), pastedFileName).replace(/{{number}}/g, '0');
+ const file = new File([paste], `${fileName}.txt`, { type: 'text/plain' });
+ uploader.addFiles([file]);
}
}
-function onDragover(ev) {
- if (!ev.dataTransfer.items[0]) return;
+function onDragover(ev: DragEvent) {
+ if (ev.dataTransfer == null) return;
+ if (ev.dataTransfer.items[0] == null) return;
+
const isFile = ev.dataTransfer.items[0].kind === 'file';
if (isFile || checkDragDataType(ev, ['driveFiles'])) {
ev.preventDefault();
@@ -852,13 +860,32 @@ function onDrop(ev: DragEvent): void {
//#endregion
}
+type StoredDrafts = {
+ [key: string]: {
+ updatedAt: string;
+ data: {
+ text: string;
+ useCw: boolean;
+ cw: string | null;
+ visibility: 'public' | 'home' | 'followers' | 'specified';
+ localOnly: boolean;
+ files: Misskey.entities.DriveFile[];
+ poll: PollEditorModelValue | null;
+ visibleUserIds?: string[];
+ quoteId: string | null;
+ reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null;
+ scheduledAt: number | null;
+ };
+ };
+};
+
function saveDraft() {
if (props.instant || props.mock) return;
- const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
+ const draftsData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}') as StoredDrafts;
- draftData[draftKey.value] = {
- updatedAt: new Date(),
+ draftsData[draftKey.value] = {
+ updatedAt: new Date().toISOString(),
data: {
text: text.value,
useCw: useCw.value,
@@ -874,15 +901,15 @@ function saveDraft() {
},
};
- miLocalStorage.setItem('drafts', JSON.stringify(draftData));
+ miLocalStorage.setItem('drafts', JSON.stringify(draftsData));
}
function deleteDraft() {
- const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
+ const draftsData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}') as StoredDrafts;
- delete draftData[draftKey.value];
+ delete draftsData[draftKey.value];
- miLocalStorage.setItem('drafts', JSON.stringify(draftData));
+ miLocalStorage.setItem('drafts', JSON.stringify(draftsData));
}
async function saveServerDraft(options: {
@@ -924,8 +951,8 @@ async function uploadFiles() {
}
}
-async function post(ev?: MouseEvent) {
- if (ev) {
+async function post(ev?: PointerEvent) {
+ if (ev != null) {
const el = (ev.currentTarget ?? ev.target) as HTMLElement | null;
if (el && prefer.s.animation) {
@@ -999,7 +1026,7 @@ async function post(ev?: MouseEvent) {
channelId: targetChannel.value ? targetChannel.value.id : undefined,
poll: poll.value,
cw: useCw.value ? cw.value ?? '' : null,
- localOnly: localOnly.value,
+ localOnly: visibility.value === 'specified' ? false : localOnly.value,
visibility: visibility.value,
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
reactionAcceptance: reactionAcceptance.value,
@@ -1138,11 +1165,12 @@ function cancel() {
function insertMention() {
os.selectUser({ localOnly: localOnly.value, includeSelf: true }).then(user => {
+ if (textareaEl.value == null) return;
insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' ');
});
}
-async function insertEmoji(ev: MouseEvent) {
+async function insertEmoji(ev: PointerEvent) {
textAreaReadOnly.value = true;
const target = ev.currentTarget ?? ev.target;
if (target == null) return;
@@ -1166,21 +1194,45 @@ async function insertEmoji(ev: MouseEvent) {
},
() => {
textAreaReadOnly.value = false;
- nextTick(() => focus());
+ nextTick(() => {
+ if (textareaEl.value) {
+ textareaEl.value.focus();
+ textareaEl.value.setSelectionRange(pos, posEnd);
+ }
+ });
},
);
}
-async function insertMfmFunction(ev: MouseEvent) {
+async function insertMfmFunction(ev: PointerEvent) {
if (textareaEl.value == null) return;
+ let pos = textareaEl.value.selectionStart ?? 0;
+ let posEnd = textareaEl.value.selectionEnd ?? text.value.length;
mfmFunctionPicker(
ev.currentTarget ?? ev.target,
- textareaEl.value,
- text,
+ (tag) => {
+ if (pos === posEnd) {
+ text.value = `${text.value.substring(0, pos)}$[${tag} ]${text.value.substring(pos)}`;
+ pos += tag.length + 3;
+ posEnd = pos;
+ } else {
+ text.value = `${text.value.substring(0, pos)}$[${tag} ${text.value.substring(pos, posEnd)}]${text.value.substring(posEnd)}`;
+ pos += tag.length + 3;
+ posEnd = pos;
+ }
+ },
+ () => {
+ nextTick(() => {
+ if (textareaEl.value) {
+ textareaEl.value.focus();
+ textareaEl.value.setSelectionRange(pos, posEnd);
+ }
+ });
+ },
);
}
-function showActions(ev: MouseEvent) {
+function showActions(ev: PointerEvent) {
os.popupMenu(postFormActions.map(action => ({
text: action.title,
action: () => {
@@ -1198,7 +1250,7 @@ function showActions(ev: MouseEvent) {
const postAccount = ref<Misskey.entities.UserDetailed | null>(null);
-async function openAccountMenu(ev: MouseEvent) {
+async function openAccountMenu(ev: PointerEvent) {
if (props.mock) return;
function showDraftsDialog(scheduled: boolean) {
@@ -1288,12 +1340,12 @@ async function openAccountMenu(ev: MouseEvent) {
}, { type: 'divider' }, ...items], (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
}
-function showPerUploadItemMenu(item: UploaderItem, ev: MouseEvent) {
+function showPerUploadItemMenu(item: UploaderItem, ev: PointerEvent) {
const menu = uploader.getMenu(item);
os.popupMenu(menu, ev.currentTarget ?? ev.target);
}
-function showPerUploadItemMenuViaContextmenu(item: UploaderItem, ev: MouseEvent) {
+function showPerUploadItemMenuViaContextmenu(item: UploaderItem, ev: PointerEvent) {
const menu = uploader.getMenu(item);
os.contextMenu(menu, ev);
}
@@ -1360,16 +1412,15 @@ onMounted(() => {
});
}
- // TODO: detach when unmount
- if (textareaEl.value) new Autocomplete(textareaEl.value, text);
- if (cwInputEl.value) new Autocomplete(cwInputEl.value, cw);
- if (hashtagsInputEl.value) new Autocomplete(hashtagsInputEl.value, hashtags);
+ if (textareaEl.value) textAutocomplete = new Autocomplete(textareaEl.value, text);
+ if (cwInputEl.value) cwAutocomplete = new Autocomplete(cwInputEl.value, cw);
+ if (hashtagsInputEl.value) hashtagAutocomplete = new Autocomplete(hashtagsInputEl.value, hashtags);
nextTick(() => {
// 書きかけの投稿を復元
if (!props.instant && !props.mention && !props.specified && !props.mock) {
- const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value];
- if (draft) {
+ const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value] as StoredDrafts[string] | undefined;
+ if (draft != null) {
text.value = draft.data.text;
useCw.value = draft.data.useCw;
cw.value = draft.data.cw;
@@ -1420,6 +1471,19 @@ onMounted(() => {
});
});
+onBeforeUnmount(() => {
+ uploader.abortAll();
+ if (textAutocomplete) {
+ textAutocomplete.detach();
+ }
+ if (cwAutocomplete) {
+ cwAutocomplete.detach();
+ }
+ if (hashtagAutocomplete) {
+ hashtagAutocomplete.detach();
+ }
+});
+
async function canClose() {
if (!uploader.allItemsUploaded.value) {
const { canceled } = await os.confirm({
@@ -1469,9 +1533,6 @@ defineExpose({
padding: 8px;
}
-.account {
-}
-
.avatar {
display: block;
width: 28px;
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index f429db94df..58adb16954 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -5,23 +5,30 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div v-show="props.modelValue.length != 0" :class="$style.root">
- <Sortable :modelValue="props.modelValue" :class="$style.files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)">
- <template #item="{ element }">
+ <MkDraggable
+ :modelValue="props.modelValue"
+ :class="$style.files"
+ direction="horizontal"
+ withGaps
+ @update:modelValue="v => emit('update:modelValue', v)"
+ >
+ <template #default="{ item }">
<div
:class="$style.file"
role="button"
tabindex="0"
- @click="showFileMenu(element, $event)"
- @keydown.space.enter="showFileMenu(element, $event)"
- @contextmenu.prevent="showFileMenu(element, $event)"
+ @click="showFileMenu(item, $event)"
+ @keydown.space.enter="showFileMenu(item, $event)"
+ @contextmenu.prevent.stop="showFileMenu(item, $event)"
>
- <MkDriveFileThumbnail :data-id="element.id" :class="$style.thumbnail" :file="element" fit="cover"/>
- <div v-if="element.isSensitive" :class="$style.sensitive">
+ <!-- pointer-eventsをnoneにしておかないとiOSなどでドラッグしたときに画像の方に判定が持ってかれる -->
+ <MkDriveFileThumbnail style="pointer-events: none;" :data-id="item.id" :class="$style.thumbnail" :file="item" fit="cover"/>
+ <div v-if="item.isSensitive" :class="$style.sensitive" style="pointer-events: none;">
<i class="ti ti-eye-exclamation" style="margin: auto;"></i>
</div>
</div>
</template>
- </Sortable>
+ </MkDraggable>
<p
:class="[$style.remain, {
[$style.exceeded]: props.modelValue.length > 16,
@@ -33,11 +40,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, inject } from 'vue';
+import { inject } from 'vue';
import * as Misskey from 'misskey-js';
import type { MenuItem } from '@/types/menu';
import { copyToClipboard } from '@/utility/copy-to-clipboard';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
@@ -45,8 +53,6 @@ import { prefer } from '@/preferences.js';
import { DI } from '@/di.js';
import { globalEvents } from '@/events.js';
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-
const props = defineProps<{
modelValue: Misskey.entities.DriveFile[];
detachMediaFn?: (id: string) => void;
@@ -91,7 +97,7 @@ async function detachAndDeleteMedia(file: Misskey.entities.DriveFile) {
globalEvents.emit('driveFilesDeleted', [file]);
}
-function toggleSensitive(file) {
+function toggleSensitive(file: Misskey.entities.DriveFile) {
if (mock) {
emit('changeSensitive', file, !file.isSensitive);
return;
@@ -105,7 +111,7 @@ function toggleSensitive(file) {
});
}
-async function rename(file) {
+async function rename(file: Misskey.entities.DriveFile) {
if (mock) return;
const { canceled, result } = await os.inputText({
@@ -143,7 +149,7 @@ async function describe(file: Misskey.entities.DriveFile) {
});
}
-function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | KeyboardEvent): void {
+function showFileMenu(file: Misskey.entities.DriveFile, ev: PointerEvent | KeyboardEvent): void {
if (menuShowing) return;
const isImage = file.type.startsWith('image/');
@@ -221,7 +227,6 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
position: relative;
width: 64px;
height: 64px;
- margin-right: 4px;
border-radius: 4px;
overflow: hidden;
cursor: move;
diff --git a/packages/frontend/src/components/MkPreferenceContainer.vue b/packages/frontend/src/components/MkPreferenceContainer.vue
index 70b111513c..1ce608dda9 100644
--- a/packages/frontend/src/components/MkPreferenceContainer.vue
+++ b/packages/frontend/src/components/MkPreferenceContainer.vue
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
const isAccountOverrided = ref(prefer.isAccountOverrided(props.k));
const isSyncEnabled = ref(prefer.isSyncEnabled(props.k));
-function showMenu(ev: MouseEvent, contextmenu?: boolean) {
+function showMenu(ev: PointerEvent, contextmenu?: boolean) {
const i = window.setInterval(() => {
isAccountOverrided.value = prefer.isAccountOverrided(props.k);
isSyncEnabled.value = prefer.isSyncEnabled(props.k);
diff --git a/packages/frontend/src/components/MkPreview.vue b/packages/frontend/src/components/MkPreview.vue
index 6c7bf6be6b..c589cd9685 100644
--- a/packages/frontend/src/components/MkPreview.vue
+++ b/packages/frontend/src/components/MkPreview.vue
@@ -12,11 +12,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="flag" :class="$style.preview__content1__switch_button">
<span>Switch is now {{ flag ? 'on' : 'off' }}</span>
</MkSwitch>
- <div :class="$style.preview__content1__input">
- <MkRadio v-model="radio" value="misskey">Misskey</MkRadio>
- <MkRadio v-model="radio" value="mastodon">Mastodon</MkRadio>
- <MkRadio v-model="radio" value="pleroma">Pleroma</MkRadio>
- </div>
<div :class="$style.preview__content1__button">
<MkButton inline>This is</MkButton>
<MkButton inline primary>the button</MkButton>
@@ -40,15 +35,12 @@ import * as config from '@@/js/config.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
-import MkRadio from '@/components/MkRadio.vue';
import * as os from '@/os.js';
import { $i } from '@/i.js';
import { chooseDriveFile } from '@/utility/drive.js';
const text = ref('');
const flag = ref(true);
-const radio = ref('misskey');
const mfm = ref(`Hello world! This is an @example mention. BTW you are @${$i ? $i.username : 'guest'}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.`);
const openDialog = async () => {
@@ -89,7 +81,7 @@ const selectUser = async () => {
await os.selectUser();
};
-const openMenu = async (ev: Event) => {
+const openMenu = async (ev: PointerEvent) => {
os.popupMenu([{
type: 'label',
text: 'Fruits',
diff --git a/packages/frontend/src/components/MkPreviewWithControls.vue b/packages/frontend/src/components/MkPreviewWithControls.vue
new file mode 100644
index 0000000000..ad5fd2a01d
--- /dev/null
+++ b/packages/frontend/src/components/MkPreviewWithControls.vue
@@ -0,0 +1,93 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.root">
+ <div :class="$style.container">
+ <div :class="[$style.preview, prefer.s.animation ? $style.animatedBg : null]">
+ <div :class="$style.previewContent">
+ <slot name="preview"></slot>
+ </div>
+ <div v-if="previewLoading" :class="$style.previewLoading">
+ <MkLoading/>
+ </div>
+ </div>
+ <div :class="$style.controls">
+ <slot name="controls"></slot>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { prefer } from '@/preferences.js';
+
+const props = withDefaults(defineProps<{
+ previewLoading?: boolean;
+}>(), {
+ previewLoading: false,
+});
+
+defineSlots<{
+ preview: () => any;
+ controls: () => any;
+}>();
+</script>
+
+<style lang="scss" module>
+.root {
+ container-type: inline-size;
+ height: 100%;
+}
+
+.container {
+ height: 100%;
+ display: grid;
+ grid-template-columns: 1fr 400px;
+}
+
+.preview {
+ position: relative;
+ background-color: var(--MI_THEME-bg);
+ background-image: linear-gradient(135deg, transparent 30%, var(--MI_THEME-panel) 30%, var(--MI_THEME-panel) 50%, transparent 50%, transparent 80%, var(--MI_THEME-panel) 80%, var(--MI_THEME-panel) 100%);
+ background-size: 20px 20px;
+}
+
+.previewContent {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ overflow: clip;
+}
+
+.previewLoading {
+ position: absolute;
+ inset: 0;
+ background-color: color(from var(--MI_THEME-panel) srgb r g b / 0.7);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.animatedBg {
+ animation: bg 1.2s linear infinite;
+}
+
+@keyframes bg {
+ 0% { background-position: 0 0; }
+ 100% { background-position: -20px -20px; }
+}
+
+.controls {
+ overflow-y: scroll;
+}
+
+@container (max-width: 800px) {
+ .container {
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr 1fr;
+ }
+}
+</style>
diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue
index 89aca5d29b..4d936758c5 100644
--- a/packages/frontend/src/components/MkPullToRefresh.vue
+++ b/packages/frontend/src/components/MkPullToRefresh.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
- <slot/>
+ <slot></slot>
</div>
</template>
diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
index 38441b0ea6..cba9b47c56 100644
--- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -144,7 +144,7 @@ async function unsubscribe() {
}
function encode(buffer: ArrayBuffer | null) {
- return btoa(String.fromCharCode.apply(null, buffer ? new Uint8Array(buffer) as any : []));
+ return btoa(String.fromCharCode(...(buffer != null ? new Uint8Array(buffer) : [])));
}
/**
diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue
deleted file mode 100644
index a7d77dd118..0000000000
--- a/packages/frontend/src/components/MkRadio.vue
+++ /dev/null
@@ -1,136 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and misskey-project
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<div
- v-adaptive-border
- :class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked }]"
- :aria-checked="checked"
- :aria-disabled="disabled"
- role="checkbox"
- @click="toggle"
->
- <input
- type="radio"
- :disabled="disabled"
- :class="$style.input"
- >
- <span :class="$style.button">
- <span></span>
- </span>
- <span :class="$style.label"><slot></slot></span>
-</div>
-</template>
-
-<script lang="ts" setup generic="T extends unknown">
-import { computed } from 'vue';
-
-const props = defineProps<{
- modelValue: T;
- value: T;
- disabled?: boolean;
-}>();
-
-const emit = defineEmits<{
- (ev: 'update:modelValue', value: T): void;
-}>();
-
-const checked = computed(() => props.modelValue === props.value);
-
-function toggle(): void {
- if (props.disabled) return;
- emit('update:modelValue', props.value);
-}
-</script>
-
-<style lang="scss" module>
-.root {
- position: relative;
- display: inline-flex;
- align-items: center;
- text-align: left;
- cursor: pointer;
- padding: 7px 10px;
- min-width: 60px;
- background-color: var(--MI_THEME-panel);
- background-clip: padding-box !important;
- border: solid 1px var(--MI_THEME-panel);
- border-radius: 6px;
- font-size: 90%;
- transition: all 0.2s;
- user-select: none;
-
- &.disabled {
- opacity: 0.6;
- cursor: not-allowed !important;
- }
-
- &:hover {
- border-color: var(--MI_THEME-inputBorderHover) !important;
- }
-
- &:focus-within {
- outline: none;
- box-shadow: 0 0 0 2px var(--MI_THEME-focus);
- }
-
- &.checked {
- background-color: var(--MI_THEME-accentedBg) !important;
- border-color: var(--MI_THEME-accentedBg) !important;
- color: var(--MI_THEME-accent);
- cursor: default !important;
-
- > .button {
- border-color: var(--MI_THEME-accent);
-
- &::after {
- background-color: var(--MI_THEME-accent);
- transform: scale(1);
- opacity: 1;
- }
- }
- }
-}
-
-.input {
- position: absolute;
- width: 0;
- height: 0;
- opacity: 0;
- margin: 0;
-}
-
-.button {
- position: relative;
- display: inline-block;
- width: 14px;
- height: 14px;
- background: none;
- border: solid 2px var(--MI_THEME-inputBorder);
- border-radius: 100%;
- transition: inherit;
-
- &::after {
- content: '';
- display: block;
- position: absolute;
- top: 3px;
- right: 3px;
- bottom: 3px;
- left: 3px;
- border-radius: 100%;
- opacity: 0;
- transform: scale(0);
- transition: 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
- }
-}
-
-.label {
- margin-left: 8px;
- display: block;
- line-height: 20px;
- cursor: pointer;
-}
-</style>
diff --git a/packages/frontend/src/components/MkRadios.vue b/packages/frontend/src/components/MkRadios.vue
index 426a1d2c2b..e2210e858e 100644
--- a/packages/frontend/src/components/MkRadios.vue
+++ b/packages/frontend/src/components/MkRadios.vue
@@ -3,99 +3,225 @@ SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
+<template>
+<div :class="{ [$style.vertical]: vertical }">
+ <div :class="$style.label">
+ <slot name="label"></slot>
+ </div>
+
+ <div :class="$style.body">
+ <div
+ v-for="option in options"
+ :key="getKey(option.value)"
+ v-adaptive-border
+ :class="[$style.optionRoot, { [$style.disabled]: option.disabled, [$style.checked]: model === option.value }]"
+ :aria-checked="model === option.value"
+ :aria-disabled="option.disabled"
+ role="checkbox"
+ @click="toggle(option)"
+ >
+ <input
+ type="radio"
+ :disabled="option.disabled"
+ :class="$style.optionInput"
+ >
+ <span :class="$style.optionButton">
+ <span></span>
+ </span>
+ <div :class="$style.optionContent">
+ <i v-if="option.icon" :class="[$style.optionIcon, option.icon]" :style="option.iconStyle"></i>
+ <div>
+ <slot v-if="option.slotId != null" :name="`option-${option.slotId as SlotNames}`"></slot>
+ <template v-else>
+ <div :style="option.labelStyle">{{ option.label ?? option.value }}</div>
+ <div v-if="option.caption" :class="$style.optionCaption">{{ option.caption }}</div>
+ </template>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div :class="$style.caption">
+ <slot name="caption"></slot>
+ </div>
+</div>
+</template>
+
<script lang="ts">
-import { Comment, defineComponent, h, ref, watch } from 'vue';
-import MkRadio from './MkRadio.vue';
-import type { VNode } from 'vue';
+import type { StyleValue } from 'vue';
+import type { OptionValue } from '@/types/option-value.js';
+
+export type MkRadiosOption<T = OptionValue, S = string> = {
+ value: T;
+ slotId?: S;
+ label?: string;
+ labelStyle?: StyleValue;
+ icon?: string;
+ iconStyle?: StyleValue;
+ caption?: string;
+ disabled?: boolean;
+};
+</script>
-export default defineComponent({
- props: {
- modelValue: {
- required: false,
- },
- vertical: {
- type: Boolean,
- default: false,
- },
- },
- setup(props, context) {
- const value = ref(props.modelValue);
- watch(value, () => {
- context.emit('update:modelValue', value.value);
- });
- watch(() => props.modelValue, v => {
- value.value = v;
- });
- if (!context.slots.default) return null;
- let options = context.slots.default();
- const label = context.slots.label && context.slots.label();
- const caption = context.slots.caption && context.slots.caption();
+<script setup lang="ts" generic="const T extends MkRadiosOption">
+defineProps<{
+ options: T[];
+ vertical?: boolean;
+}>();
- // なぜかFragmentになることがあるため
- if (options.length === 1 && options[0].props == null) options = options[0].children as VNode[];
+type SlotNames = NonNullable<T extends MkRadiosOption<any, infer U> ? U : never>;
- // vnodeのうちv-if=falseなものを除外する(trueになるものはoptionなど他typeになる)
- options = options.filter(vnode => vnode.type !== Comment);
+defineSlots<{
+ label?: () => void;
+ caption?: () => void;
+} & {
+ [K in `option-${SlotNames}`]: () => void;
+}>();
- return () => h('div', {
- class: [
- 'novjtcto',
- ...(props.vertical ? ['vertical'] : []),
- ],
- }, [
- ...(label ? [h('div', {
- class: 'label',
- }, label)] : []),
- h('div', {
- class: 'body',
- }, options.map(option => h(MkRadio, {
- key: option.key as string,
- value: option.props?.value,
- disabled: option.props?.disabled,
- modelValue: value.value,
- 'onUpdate:modelValue': _v => value.value = _v,
- }, () => option.children)),
- ),
- ...(caption ? [h('div', {
- class: 'caption',
- }, caption)] : []),
- ]);
- },
-});
+const model = defineModel<T['value']>({ required: true });
+
+function getKey(value: OptionValue): PropertyKey {
+ if (value === null) return '___null___';
+ return value;
+}
+
+function toggle(o: MkRadiosOption): void {
+ if (o.disabled) return;
+ model.value = o.value;
+}
</script>
-<style lang="scss">
-.novjtcto {
- > .label {
- font-size: 0.85em;
- padding: 0 0 8px 0;
- user-select: none;
+<style lang="scss" module>
+.label {
+ font-size: 0.85em;
+ padding: 0 0 8px 0;
+ user-select: none;
- &:empty {
- display: none;
- }
+ &:empty {
+ display: none;
}
+}
+
+.body {
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+}
- > .body {
- display: flex;
- gap: 10px;
- flex-wrap: wrap;
+.caption {
+ font-size: 0.85em;
+ padding: 8px 0 0 0;
+ color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
+
+ &:empty {
+ display: none;
}
+}
- > .caption {
- font-size: 0.85em;
- padding: 8px 0 0 0;
- color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
+.vertical > .body {
+ flex-direction: column;
+}
- &:empty {
- display: none;
- }
+.optionRoot {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ text-align: left;
+ cursor: pointer;
+ padding: 8px 10px;
+ min-width: 60px;
+ background-color: var(--MI_THEME-panel);
+ background-clip: padding-box !important;
+ border: solid 1px var(--MI_THEME-panel);
+ border-radius: 6px;
+ font-size: 90%;
+ transition: all 0.2s;
+ user-select: none;
+
+ &.disabled {
+ opacity: 0.6;
+ cursor: not-allowed !important;
+ }
+
+ &:hover {
+ border-color: var(--MI_THEME-inputBorderHover) !important;
+ }
+
+ &:focus-within {
+ outline: none;
+ box-shadow: 0 0 0 2px var(--MI_THEME-focus);
}
- &.vertical {
- > .body {
- flex-direction: column;
+ &.checked {
+ background-color: var(--MI_THEME-accentedBg) !important;
+ border-color: var(--MI_THEME-accentedBg) !important;
+ color: var(--MI_THEME-accent);
+ cursor: default !important;
+
+ .optionButton {
+ border-color: var(--MI_THEME-accent);
+
+ &::after {
+ background-color: var(--MI_THEME-accent);
+ transform: scale(1);
+ opacity: 1;
+ }
+ }
+
+ .optionCaption {
+ color: color(from var(--MI_THEME-accent) srgb r g b / 0.75);
}
}
}
+
+.optionInput {
+ position: absolute;
+ width: 0;
+ height: 0;
+ opacity: 0;
+ margin: 0;
+}
+
+.optionButton {
+ position: relative;
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ background: none;
+ border: solid 2px var(--MI_THEME-inputBorder);
+ border-radius: 100%;
+ transition: inherit;
+
+ &::after {
+ content: '';
+ display: block;
+ position: absolute;
+ top: 3px;
+ right: 3px;
+ bottom: 3px;
+ left: 3px;
+ border-radius: 100%;
+ opacity: 0;
+ transform: scale(0);
+ transition: 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
+ }
+}
+
+.optionContent {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin-left: 8px;
+}
+
+.optionCaption {
+ font-size: 0.85em;
+ padding: 2px 0 0 0;
+ color: color(from var(--MI_THEME-fg) srgb r g b / 0.75);
+ transition: all 0.2s;
+}
+
+.optionIcon {
+ flex-shrink: 0;
+}
</style>
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 7c60288883..a89f947fa7 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -153,7 +153,7 @@ async function toggleReaction() {
}
}
-async function menu(ev) {
+async function menu(ev: PointerEvent) {
let menuItems: MenuItem[] = [];
if (canGetInfo.value) {
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index bd9ef50157..67fd570b41 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:myReaction="props.myReaction"
@reactionToggled="onMockToggleReaction"
/>
- <slot v-if="hasMoreReactions" name="more"/>
+ <slot v-if="hasMoreReactions" name="more"></slot>
</component>
</template>
@@ -32,11 +32,11 @@ SPDX-License-Identifier: AGPL-3.0-only
import * as Misskey from 'misskey-js';
import { inject, watch, ref } from 'vue';
import { TransitionGroup } from 'vue';
+import { isSupportedEmoji } from '@@/js/emojilist.js';
import XReaction from '@/components/MkReactionsViewer.reaction.vue';
import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
import { customEmojisMap } from '@/custom-emojis.js';
-import { isSupportedEmoji } from '@@/js/emojilist.js';
import { DI } from '@/di.js';
const props = withDefaults(defineProps<{
@@ -60,8 +60,8 @@ const initialReactions = new Set(Object.keys(props.reactions));
const _reactions = ref<[string, number][]>([]);
const hasMoreReactions = ref(false);
-if (props.myReaction && !Object.keys(_reactions.value).includes(props.myReaction)) {
- _reactions.value[props.myReaction] = props.reactions[props.myReaction];
+if (props.myReaction != null && !(props.myReaction in props.reactions)) {
+ _reactions.value.push([props.myReaction, props.reactions[props.myReaction]]);
}
function onMockToggleReaction(emoji: string, count: number) {
diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue
index a204bc3bf1..6ab7a01ce7 100644
--- a/packages/frontend/src/components/MkRetentionHeatmap.vue
+++ b/packages/frontend/src/components/MkRetentionHeatmap.vue
@@ -98,7 +98,7 @@ async function renderChart() {
data: data as any,
borderWidth: 0,
borderRadius: 3,
- backgroundColor(c) {
+ backgroundColor(c: any) {
const v = c.dataset.data[c.dataIndex] as unknown as typeof data[0];
const value = v.v;
const m = max(v.y);
@@ -179,7 +179,7 @@ async function renderChart() {
enabled: false,
callbacks: {
title(context) {
- const v = context[0].dataset.data[context[0].dataIndex];
+ const v = context[0].dataset.data[context[0].dataIndex] as unknown as typeof data[0];
return getYYYYMMDD(new Date(new Date(v.y).getTime() + (v.x * 86400000)));
},
label(context) {
diff --git a/packages/frontend/src/components/MkRetentionLineChart.vue b/packages/frontend/src/components/MkRetentionLineChart.vue
index 21c20f944b..5b18bab8c9 100644
--- a/packages/frontend/src/components/MkRetentionLineChart.vue
+++ b/packages/frontend/src/components/MkRetentionLineChart.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, useTemplateRef } from 'vue';
import { Chart } from 'chart.js';
+import type { ScatterDataPoint } from 'chart.js';
import tinycolor from 'tinycolor2';
import { store } from '@/store.js';
import { useChartTooltip } from '@/composables/use-chart-tooltip.js';
@@ -18,6 +19,12 @@ import { alpha } from '@/utility/color.js';
import { initChart } from '@/utility/init-chart.js';
import { misskeyApi } from '@/utility/misskey-api.js';
+interface RetentionPoint extends ScatterDataPoint {
+ x: number;
+ y: number;
+ d: string;
+}
+
initChart();
const chartEl = useTemplateRef('chartEl');
@@ -62,14 +69,14 @@ onMounted(async () => {
fill: false,
tension: 0.4,
data: [{
- x: '0',
+ x: 0,
y: 100,
d: getYYYYMMDD(new Date(record.createdAt)),
}, ...Object.entries(record.data).sort((a, b) => getDate(a[0]) > getDate(b[0]) ? 1 : -1).map(([k, v], i) => ({
- x: (i + 1).toString(),
+ x: i + 1,
y: (v / record.users) * 100,
d: getYYYYMMDD(new Date(record.createdAt)),
- }))] as any,
+ }))],
})),
},
options: {
@@ -111,11 +118,11 @@ onMounted(async () => {
enabled: false,
callbacks: {
title(context) {
- const v = context[0].dataset.data[context[0].dataIndex] as unknown as { x: string, y: number, d: string };
+ const v = context[0].dataset.data[context[0].dataIndex] as RetentionPoint;
return `${v.x} days later`;
},
label(context) {
- const v = context.dataset.data[context.dataIndex] as unknown as { x: string, y: number, d: string };
+ const v = context.dataset.data[context.dataIndex] as RetentionPoint;
const p = Math.round(v.y) + '%';
return `${v.d} ${p}`;
},
diff --git a/packages/frontend/src/components/MkRoleSelectDialog.vue b/packages/frontend/src/components/MkRoleSelectDialog.vue
index 937804703d..651165136a 100644
--- a/packages/frontend/src/components/MkRoleSelectDialog.vue
+++ b/packages/frontend/src/components/MkRoleSelectDialog.vue
@@ -55,9 +55,9 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
import MkLoading from '@/components/global/MkLoading.vue';
const emit = defineEmits<{
- (ev: 'done', value: Misskey.entities.Role[]),
- (ev: 'close'),
- (ev: 'closed'),
+ (ev: 'done', value: Misskey.entities.Role[]): void;
+ (ev: 'close'): void;
+ (ev: 'closed'): void;
}>();
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index f130145e36..6f6957d504 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts">
-export type OptionValue = string | number | null;
+import type { OptionValue } from '@/types/option-value.js';
export type ItemOption<T extends OptionValue = OptionValue> = {
type?: 'option';
diff --git a/packages/frontend/src/components/MkServerSetupWizard.vue b/packages/frontend/src/components/MkServerSetupWizard.vue
index 8e3b41e754..462ded6de3 100644
--- a/packages/frontend/src/components/MkServerSetupWizard.vue
+++ b/packages/frontend/src/components/MkServerSetupWizard.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root" class="_gaps_m">
+<div class="_gaps_m">
<MkInput v-model="q_name" data-cy-server-name>
<template #label>{{ i18n.ts.instanceName }}</template>
</MkInput>
@@ -14,19 +14,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-settings-question"></i></template>
<div class="_gaps_s">
- <MkRadios v-model="q_use" :vertical="true">
- <option value="single">
- <div><i class="ti ti-user"></i> <b>{{ i18n.ts._serverSetupWizard._use.single }}</b></div>
- <div>{{ i18n.ts._serverSetupWizard._use.single_description }}</div>
- </option>
- <option value="group">
- <div><i class="ti ti-lock"></i> <b>{{ i18n.ts._serverSetupWizard._use.group }}</b></div>
- <div>{{ i18n.ts._serverSetupWizard._use.group_description }}</div>
- </option>
- <option value="open">
- <div><i class="ti ti-world"></i> <b>{{ i18n.ts._serverSetupWizard._use.open }}</b></div>
- <div>{{ i18n.ts._serverSetupWizard._use.open_description }}</div>
- </option>
+ <MkRadios
+ v-model="q_use"
+ :options="[
+ { value: 'single', label: i18n.ts._serverSetupWizard._use.single, icon: 'ti ti-user', caption: i18n.ts._serverSetupWizard._use.single_description },
+ { value: 'group', label: i18n.ts._serverSetupWizard._use.group, icon: 'ti ti-lock', caption: i18n.ts._serverSetupWizard._use.group_description },
+ { value: 'open', label: i18n.ts._serverSetupWizard._use.open, icon: 'ti ti-world', caption: i18n.ts._serverSetupWizard._use.open_description },
+ ]"
+ vertical
+ >
</MkRadios>
<MkInfo v-if="q_use === 'single'">{{ i18n.ts._serverSetupWizard._use.single_youCanCreateMultipleAccounts }}</MkInfo>
@@ -40,10 +36,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #icon><i class="ti ti-users"></i></template>
<div class="_gaps_s">
- <MkRadios v-model="q_scale" :vertical="true">
- <option value="small"><i class="ti ti-user"></i> {{ i18n.ts._serverSetupWizard._scale.small }}</option>
- <option value="medium"><i class="ti ti-users"></i> {{ i18n.ts._serverSetupWizard._scale.medium }}</option>
- <option value="large"><i class="ti ti-users-group"></i> {{ i18n.ts._serverSetupWizard._scale.large }}</option>
+ <MkRadios
+ v-model="q_scale"
+ :options="[
+ { value: 'small', label: i18n.ts._serverSetupWizard._scale.small, icon: 'ti ti-user' },
+ { value: 'medium', label: i18n.ts._serverSetupWizard._scale.medium, icon: 'ti ti-users' },
+ { value: 'large', label: i18n.ts._serverSetupWizard._scale.large, icon: 'ti ti-users-group' },
+ ]"
+ vertical
+ >
</MkRadios>
<MkInfo v-if="q_scale === 'large'"><b>{{ i18n.ts.advice }}:</b> {{ i18n.ts._serverSetupWizard.largeScaleServerAdvice }}</MkInfo>
@@ -57,9 +58,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_s">
<div>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description1 }}<br>{{ i18n.ts._serverSetupWizard.doYouConnectToFediverse_description2 }}<br><MkLink target="_blank" url="https://wikipedia.org/wiki/Fediverse">{{ i18n.ts.learnMore }}</MkLink></div>
- <MkRadios v-model="q_federation" :vertical="true">
- <option value="yes">{{ i18n.ts.yes }}</option>
- <option value="no">{{ i18n.ts.no }}</option>
+ <MkRadios
+ v-model="q_federation"
+ :options="[
+ { value: 'yes', label: i18n.ts.yes },
+ { value: 'no', label: i18n.ts.no },
+ ]"
+ vertical
+ >
</MkRadios>
<MkInfo v-if="q_federation === 'yes'">{{ i18n.ts._serverSetupWizard.youCanConfigureMoreFederationSettingsLater }}</MkInfo>
@@ -212,9 +218,9 @@ const props = withDefaults(defineProps<{
});
const q_name = ref('');
-const q_use = ref('single');
-const q_scale = ref('small');
-const q_federation = ref('yes');
+const q_use = ref<'single' | 'group' | 'open'>('single');
+const q_scale = ref<'small' | 'medium' | 'large'>('small');
+const q_federation = ref<'yes' | 'no'>('no');
const q_remoteContentsCleaning = ref(true);
const q_adminName = ref('');
const q_adminEmail = ref('');
@@ -239,7 +245,7 @@ const serverSettings = computed<Misskey.entities.AdminUpdateMetaRequest>(() => {
enableReactionsBuffering,
clientOptions: {
entrancePageStyle: q_use.value === 'open' ? 'classic' : 'simple',
- } as any,
+ },
};
});
@@ -370,8 +376,3 @@ function applySettings() {
});
}
</script>
-
-<style lang="scss" module>
-.root {
-}
-</style>
diff --git a/packages/frontend/src/components/MkServerSetupWizardDialog.vue b/packages/frontend/src/components/MkServerSetupWizardDialog.vue
index ea2c5dd47f..1d03438f83 100644
--- a/packages/frontend/src/components/MkServerSetupWizardDialog.vue
+++ b/packages/frontend/src/components/MkServerSetupWizardDialog.vue
@@ -33,7 +33,7 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
import MkServerSetupWizard from '@/components/MkServerSetupWizard.vue';
const emit = defineEmits<{
- (ev: 'closed'),
+ (ev: 'closed'): void;
}>();
const windowEl = useTemplateRef('windowEl');
diff --git a/packages/frontend/src/components/MkSignin.input.vue b/packages/frontend/src/components/MkSignin.input.vue
index 4c73eab3f5..89ec6373cf 100644
--- a/packages/frontend/src/components/MkSignin.input.vue
+++ b/packages/frontend/src/components/MkSignin.input.vue
@@ -78,7 +78,7 @@ const props = withDefaults(defineProps<{
const emit = defineEmits<{
(ev: 'usernameSubmitted', v: string): void;
- (ev: 'passkeyClick', v: MouseEvent): void;
+ (ev: 'passkeyClick', v: PointerEvent): void;
}>();
const host = toUnicode(configHost);
diff --git a/packages/frontend/src/components/MkSortOrderEditor.vue b/packages/frontend/src/components/MkSortOrderEditor.vue
index 27ffc724ae..3ac809cdbf 100644
--- a/packages/frontend/src/components/MkSortOrderEditor.vue
+++ b/packages/frontend/src/components/MkSortOrderEditor.vue
@@ -25,11 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts" generic="T extends string">
import { toRefs } from 'vue';
+import type { MenuItem } from '@/types/menu.js';
+import type { SortOrder } from '@/components/MkSortOrderEditor.define.js';
import MkTagItem from '@/components/MkTagItem.vue';
import MkButton from '@/components/MkButton.vue';
-import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
-import type { SortOrder } from '@/components/MkSortOrderEditor.define.js';
const emit = defineEmits<{
(ev: 'update', sortOrders: SortOrder<T>[]): void;
@@ -55,7 +55,7 @@ function onToggleSortOrderButtonClicked(order: SortOrder<T>) {
emitOrder(currentOrders.value);
}
-function onAddSortOrderButtonClicked(ev: MouseEvent) {
+function onAddSortOrderButtonClicked(ev: PointerEvent) {
const menuItems: MenuItem[] = props.baseOrderKeyNames
.filter(baseKey => !currentOrders.value.map(it => it.key).includes(baseKey))
.map(it => {
diff --git a/packages/frontend/src/components/MkSpot.vue b/packages/frontend/src/components/MkSpot.vue
index 4a8ebb5f94..4bd11fe938 100644
--- a/packages/frontend/src/components/MkSpot.vue
+++ b/packages/frontend/src/components/MkSpot.vue
@@ -88,7 +88,7 @@ function setPosition() {
bodyEl.value.style.top = data.top + 'px';
}
-let loopHandler;
+let loopHandler: number | null = null;
onMounted(() => {
nextTick(() => {
@@ -104,7 +104,7 @@ onMounted(() => {
});
onUnmounted(() => {
- window.cancelAnimationFrame(loopHandler);
+ if (loopHandler != null) window.cancelAnimationFrame(loopHandler);
});
</script>
diff --git a/packages/frontend/src/components/MkStreamingNotesTimeline.vue b/packages/frontend/src/components/MkStreamingNotesTimeline.vue
index bc6ebf0918..9784d8e017 100644
--- a/packages/frontend/src/components/MkStreamingNotesTimeline.vue
+++ b/packages/frontend/src/components/MkStreamingNotesTimeline.vue
@@ -350,13 +350,12 @@ function connectChannel() {
connections.main = stream.useChannel('main');
connections.main.on('mention', prepend);
} else if (props.src === 'directs') {
- const onNote = note => {
+ connections.main = stream.useChannel('main');
+ connections.main.on('mention', note => {
if (note.visibility === 'specified') {
prepend(note);
}
- };
- connections.main = stream.useChannel('main');
- connections.main.on('mention', onNote);
+ });
} else if (props.src === 'list') {
if (props.list == null) return;
connections.userList = stream.useChannel('userList', {
diff --git a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
index 6ee2e347a5..91f071fe63 100644
--- a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
+++ b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
@@ -137,8 +137,8 @@ watch(visibility, () => {
}
});
-function onNotification(notification) {
- const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false;
+function onNotification(notification: Misskey.entities.Notification) {
+ const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type as typeof notificationTypes[number]) : false;
if (isMuted || window.document.visibilityState === 'visible') {
if (store.s.realtimeMode) {
useStream().send('readNotification');
diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue
index 236afa127c..585a628a96 100644
--- a/packages/frontend/src/components/MkSuperMenu.vue
+++ b/packages/frontend/src/components/MkSuperMenu.vue
@@ -82,7 +82,7 @@ export type SuperMenuDef = {
text: string;
danger?: boolean;
active?: boolean;
- action: (ev: MouseEvent) => Awaitable<void>;
+ action: (ev: PointerEvent) => Awaitable<void>;
} | {
type?: 'link';
to: string;
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
index 19e4eea733..f2ce55acc4 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.impl.ts
@@ -25,7 +25,7 @@ export type MkSystemWebhookResult = {
};
export async function showSystemWebhookEditorDialog(props: MkSystemWebhookEditorProps): Promise<MkSystemWebhookResult | null> {
- const { result } = await new Promise<{ result: MkSystemWebhookResult | null }>(async resolve => {
+ const { result } = await new Promise<{ result: MkSystemWebhookResult | null }>(resolve => {
const { dispose } = os.popup(
defineAsyncComponent(() => import('@/components/MkSystemWebhookEditor.vue')),
props,
diff --git a/packages/frontend/src/components/MkSystemWebhookEditor.vue b/packages/frontend/src/components/MkSystemWebhookEditor.vue
index cd72204fce..1536b14455 100644
--- a/packages/frontend/src/components/MkSystemWebhookEditor.vue
+++ b/packages/frontend/src/components/MkSystemWebhookEditor.vue
@@ -245,7 +245,7 @@ onMounted(async () => {
secret.value = res.secret;
isActive.value = res.isActive;
for (const ev of Object.keys(events.value)) {
- events.value[ev] = res.on.includes(ev as SystemWebhookEventType);
+ events.value[ev as SystemWebhookEventType] = res.on.includes(ev as SystemWebhookEventType);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (ex: any) {
diff --git a/packages/frontend/src/components/MkTabs.vue b/packages/frontend/src/components/MkTabs.vue
index 9798e2c3b3..a6342ec2e1 100644
--- a/packages/frontend/src/components/MkTabs.vue
+++ b/packages/frontend/src/components/MkTabs.vue
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts">
export type Tab<K = string> = {
key: K;
- onClick?: (ev: MouseEvent) => void;
+ onClick?: (ev: PointerEvent) => void;
iconOnly?: boolean;
title: string;
icon?: string;
@@ -74,7 +74,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'tabClick', key: string);
+ (ev: 'tabClick', key: string): void;
}>();
const tab = defineModel<T['key']>('tab');
@@ -100,7 +100,7 @@ function onTabMousedown(selectedTab: Tab, ev: MouseEvent): void {
}
}
-function onTabClick(t: Tab, ev: MouseEvent): void {
+function onTabClick(t: Tab, ev: PointerEvent): void {
emit('tabClick', t.key);
if (t.onClick) {
diff --git a/packages/frontend/src/components/MkTagItem.vue b/packages/frontend/src/components/MkTagItem.vue
index 8b7460f3a3..5cd2113e59 100644
--- a/packages/frontend/src/components/MkTagItem.vue
+++ b/packages/frontend/src/components/MkTagItem.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root" @click="(ev) => emit('click', ev)">
<span v-if="iconClass" :class="[$style.icon, iconClass]"></span>
- <span :class="$style.content">{{ content }}</span>
+ <span>{{ content }}</span>
<MkButton v-if="exButtonIconClass" :class="$style.exButton" @click="(ev) => emit('exButtonClick', ev)">
<span :class="[$style.exButtonIcon, exButtonIconClass]"></span>
</MkButton>
@@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
import MkButton from '@/components/MkButton.vue';
const emit = defineEmits<{
- (ev: 'click', payload: MouseEvent): void;
- (ev: 'exButtonClick', payload: MouseEvent): void;
+ (ev: 'click', payload: PointerEvent): void;
+ (ev: 'exButtonClick', payload: PointerEvent): void;
}>();
defineProps<{
diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
index 407ac33add..fe4f7b7aaf 100644
--- a/packages/frontend/src/components/MkTextarea.vue
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -63,10 +63,11 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (ev: 'change', _ev: KeyboardEvent): void;
+ (ev: 'change', _ev: InputEvent): void;
(ev: 'keydown', _ev: KeyboardEvent): void;
(ev: 'enter'): void;
(ev: 'update:modelValue', value: string): void;
+ (ev: 'savingStateChange', saved: boolean, invalid: boolean): void;
}>();
const { modelValue, autofocus } = toRefs(props);
@@ -79,12 +80,16 @@ const inputEl = useTemplateRef('inputEl');
const preview = ref(false);
let autocompleteWorker: Autocomplete | null = null;
-const focus = () => inputEl.value?.focus();
-const onInput = (ev) => {
+function focus() {
+ inputEl.value?.focus();
+}
+
+function onInput(ev: InputEvent) {
changed.value = true;
emit('change', ev);
-};
-const onKeydown = (ev: KeyboardEvent) => {
+}
+
+function onKeydown(ev: KeyboardEvent) {
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return;
emit('keydown', ev);
@@ -102,12 +107,12 @@ const onKeydown = (ev: KeyboardEvent) => {
});
ev.preventDefault();
}
-};
+}
-const updated = () => {
+function updated() {
changed.value = false;
emit('update:modelValue', v.value ?? '');
-};
+}
const debouncedUpdated = debounce(1000, updated);
@@ -127,6 +132,10 @@ watch(v, () => {
invalid.value = inputEl.value?.validity.badInput ?? true;
});
+watch([changed, invalid], ([newChanged, newInvalid]) => {
+ emit('savingStateChange', newChanged, newInvalid);
+}, { immediate: true });
+
onMounted(() => {
nextTick(() => {
if (autofocus.value) {
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index 42cb6f1e82..8d51e1fa87 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -33,12 +33,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
</div>
<div class="_gaps_s">
- <MkSwitch v-for="kind in Object.keys(permissionSwitches)" :key="kind" v-model="permissionSwitches[kind]">{{ i18n.ts._permissions[kind] }}</MkSwitch>
+ <MkSwitch v-for="kind in Object.keys(permissionSwitches)" :key="kind" v-model="permissionSwitches[kind as keyof typeof permissionSwitches]">{{ i18n.ts._permissions[kind as keyof typeof permissionSwitches] }}</MkSwitch>
</div>
<div v-if="iAmAdmin" :class="$style.adminPermissions">
<div :class="$style.adminPermissionsHeader"><b>{{ i18n.ts.adminPermission }}</b></div>
<div class="_gaps_s">
- <MkSwitch v-for="kind in Object.keys(permissionSwitchesForAdmin)" :key="kind" v-model="permissionSwitchesForAdmin[kind]">{{ i18n.ts._permissions[kind] }}</MkSwitch>
+ <MkSwitch v-for="kind in Object.keys(permissionSwitchesForAdmin)" :key="kind" v-model="permissionSwitchesForAdmin[kind as keyof typeof permissionSwitchesForAdmin]">{{ i18n.ts._permissions[kind as keyof typeof permissionSwitchesForAdmin] }}</MkSwitch>
</div>
</div>
</div>
@@ -102,8 +102,8 @@ function ok(): void {
emit('done', {
name: name.value,
permissions: [
- ...Object.keys(permissionSwitches.value).filter(p => permissionSwitches.value[p]),
- ...(iAmAdmin ? Object.keys(permissionSwitchesForAdmin.value).filter(p => permissionSwitchesForAdmin.value[p]) : []),
+ ...Object.keys(permissionSwitches.value).filter(p => permissionSwitches.value[p as (typeof Misskey.permissions)[number]]),
+ ...(iAmAdmin ? Object.keys(permissionSwitchesForAdmin.value).filter(p => permissionSwitchesForAdmin.value[p as (typeof Misskey.permissions)[number]]) : []),
],
});
dialog.value?.close();
@@ -111,22 +111,22 @@ function ok(): void {
function disableAll(): void {
for (const p in permissionSwitches.value) {
- permissionSwitches.value[p] = false;
+ permissionSwitches.value[p as (typeof Misskey.permissions)[number]] = false;
}
if (iAmAdmin) {
for (const p in permissionSwitchesForAdmin.value) {
- permissionSwitchesForAdmin.value[p] = false;
+ permissionSwitchesForAdmin.value[p as (typeof Misskey.permissions)[number]] = false;
}
}
}
function enableAll(): void {
for (const p in permissionSwitches.value) {
- permissionSwitches.value[p] = true;
+ permissionSwitches.value[p as (typeof Misskey.permissions)[number]] = true;
}
if (iAmAdmin) {
for (const p in permissionSwitchesForAdmin.value) {
- permissionSwitchesForAdmin.value[p] = true;
+ permissionSwitchesForAdmin.value[p as (typeof Misskey.permissions)[number]] = true;
}
}
}
diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue
index aa041c88e5..08a3f02f65 100644
--- a/packages/frontend/src/components/MkTooltip.vue
+++ b/packages/frontend/src/components/MkTooltip.vue
@@ -71,7 +71,7 @@ function setPosition() {
el.value.style.top = data.top + 'px';
}
-let loopHandler;
+let loopHandler: number | null = null;
onMounted(() => {
nextTick(() => {
@@ -87,7 +87,7 @@ onMounted(() => {
});
onUnmounted(() => {
- window.cancelAnimationFrame(loopHandler);
+ if (loopHandler != null) window.cancelAnimationFrame(loopHandler);
});
</script>
diff --git a/packages/frontend/src/components/MkTutorialDialog.Note.vue b/packages/frontend/src/components/MkTutorialDialog.Note.vue
index b77e67e9c6..3ab2c5f0d4 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Note.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Note.vue
@@ -74,7 +74,7 @@ const exampleNote = reactive<Misskey.entities.Note>({
});
const onceReacted = ref<boolean>(false);
-function addReaction(emoji) {
+function addReaction(emoji: string) {
onceReacted.value = true;
emit('reacted');
doNotification(emoji);
@@ -96,7 +96,7 @@ function doNotification(emoji: string): void {
globalEvents.emit('clientNotification', notification);
}
-function removeReaction(emoji) {
+function removeReaction(emoji: string) {
delete exampleNote.reactions[emoji];
exampleNote.myReaction = undefined;
}
diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index eba8e5472c..09cf595eab 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkModal ref="modal" preferType="dialog" :zPriority="'middle'" @click="modal?.close()" @closed="$emit('closed')">
+<MkModal ref="modal" preferType="dialog" :zPriority="'middle'" @click="modal?.close()" @closed="emit('closed')">
<div :class="$style.root">
<div :class="$style.title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div>
<div :class="$style.version">✨{{ version }}🚀</div>
@@ -26,6 +26,10 @@ import { confetti } from '@/utility/confetti.js';
const modal = useTemplateRef('modal');
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
+
const isBeta = version.includes('-beta') || version.includes('-alpha') || version.includes('-rc');
function whatIsNew() {
diff --git a/packages/frontend/src/components/MkUploaderDialog.vue b/packages/frontend/src/components/MkUploaderDialog.vue
index 8849fa447d..69de56d45c 100644
--- a/packages/frontend/src/components/MkUploaderDialog.vue
+++ b/packages/frontend/src/components/MkUploaderDialog.vue
@@ -166,17 +166,17 @@ async function done() {
dialog.value?.close();
}
-async function chooseFile(ev: MouseEvent) {
+async function chooseFile(ev: PointerEvent) {
const newFiles = await os.chooseFileFromPc({ multiple: true });
uploader.addFiles(newFiles);
}
-function showPerItemMenu(item: UploaderItem, ev: MouseEvent) {
+function showPerItemMenu(item: UploaderItem, ev: PointerEvent) {
const menu = uploader.getMenu(item);
os.popupMenu(menu, ev.currentTarget ?? ev.target);
}
-function showPerItemMenuViaContextmenu(item: UploaderItem, ev: MouseEvent) {
+function showPerItemMenuViaContextmenu(item: UploaderItem, ev: PointerEvent) {
const menu = uploader.getMenu(item);
os.contextMenu(menu, ev);
}
diff --git a/packages/frontend/src/components/MkUploaderItems.vue b/packages/frontend/src/components/MkUploaderItems.vue
index f31c717ad5..51f7ac2d09 100644
--- a/packages/frontend/src/components/MkUploaderItems.vue
+++ b/packages/frontend/src/components/MkUploaderItems.vue
@@ -57,18 +57,18 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (ev: 'showMenu', item: UploaderItem, event: MouseEvent): void;
- (ev: 'showMenuViaContextmenu', item: UploaderItem, event: MouseEvent): void;
+ (ev: 'showMenu', item: UploaderItem, event: PointerEvent): void;
+ (ev: 'showMenuViaContextmenu', item: UploaderItem, event: PointerEvent): void;
}>();
-function onContextmenu(item: UploaderItem, ev: MouseEvent) {
+function onContextmenu(item: UploaderItem, ev: PointerEvent) {
if (ev.target && isLink(ev.target as HTMLElement)) return;
if (window.getSelection()?.toString() !== '') return;
emit('showMenuViaContextmenu', item, ev);
}
-function onThumbnailClick(item: UploaderItem, ev: MouseEvent) {
+function onThumbnailClick(item: UploaderItem, ev: PointerEvent) {
// TODO: preview when item is image
}
</script>
diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
index 8ec48dcc3f..5e16460104 100644
--- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
+++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
@@ -22,18 +22,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkTextarea v-model="text">
<template #label>{{ i18n.ts.text }}</template>
</MkTextarea>
- <MkRadios v-model="icon">
+ <MkRadios
+ v-model="icon"
+ :options="[
+ { value: 'info', icon: 'ti ti-info-circle' },
+ { value: 'warning', icon: 'ti ti-alert-triangle', iconStyle: 'color: var(--MI_THEME-warn);' },
+ { value: 'error', icon: 'ti ti-circle-x', iconStyle: 'color: var(--MI_THEME-error);' },
+ { value: 'success', icon: 'ti ti-check', iconStyle: 'color: var(--MI_THEME-success);' },
+ ]"
+ >
<template #label>{{ i18n.ts.icon }}</template>
- <option value="info"><i class="ti ti-info-circle"></i></option>
- <option value="warning"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i></option>
- <option value="error"><i class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i></option>
- <option value="success"><i class="ti ti-check" style="color: var(--MI_THEME-success);"></i></option>
</MkRadios>
- <MkRadios v-model="display">
+ <MkRadios
+ v-model="display"
+ :options="[
+ { value: 'normal', label: i18n.ts.normal },
+ { value: 'banner', label: i18n.ts.banner },
+ { value: 'dialog', label: i18n.ts.dialog },
+ ]"
+ >
<template #label>{{ i18n.ts.display }}</template>
- <option value="normal">{{ i18n.ts.normal }}</option>
- <option value="banner">{{ i18n.ts.banner }}</option>
- <option value="dialog">{{ i18n.ts.dialog }}</option>
</MkRadios>
<MkSwitch v-model="needConfirmationToRead">
{{ i18n.ts._announcement.needConfirmationToRead }}
diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue
index dde2efd8ee..1fd43bd6e4 100644
--- a/packages/frontend/src/components/MkUserCardMini.vue
+++ b/packages/frontend/src/components/MkUserCardMini.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAvatar :class="$style.avatar" :user="user" indicator/>
<div :class="$style.body">
<span :class="$style.name"><MkUserName :user="user"/></span>
- <span :class="$style.sub"><span class="_monospace">@{{ acct(user) }}</span></span>
+ <span :class="$style.sub"><slot name="sub"><span class="_monospace">@{{ acct(user) }}</span></slot></span>
</div>
<MkMiniChart v-if="chartValues" :class="$style.chart" :src="chartValues"/>
</div>
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index f47d9b56dc..8ce929fff3 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -27,7 +27,7 @@ const props = withDefaults(defineProps<{
noGap?: boolean;
extractor?: ExtractorFunction<P, Misskey.entities.UserDetailed>;
}>(), {
- extractor: (item) => item,
+ extractor: (item: any) => item as Misskey.entities.UserDetailed,
});
</script>
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index f794899281..9f196ac2c1 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -90,7 +90,7 @@ const top = ref(0);
const left = ref(0);
const error = ref(false);
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
if (user.value == null) return;
const { menu, cleanup } = getUserMenu(user.value);
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
index 4e96eff82e..95449dd0eb 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
@@ -66,7 +66,7 @@ watch(description, () => {
});
});
-async function setAvatar(ev) {
+async function setAvatar(ev: PointerEvent) {
const files = await os.chooseFileFromPc({ multiple: false });
const file = files[0];
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index 88b934bb58..361fda0c24 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span :class="$style.itemDescription">{{ i18n.ts._visibility.followersDescription }}</span>
</div>
</button>
- <button key="specified" :disabled="localOnly" class="_button" :class="[$style.item, { [$style.active]: v === 'specified' }]" data-index="4" @click="choose('specified')">
+ <button key="specified" class="_button" :class="[$style.item, { [$style.active]: v === 'specified' }]" data-index="4" @click="choose('specified')">
<div :class="$style.icon"><i class="ti ti-mail"></i></div>
<div :class="$style.body">
<span :class="$style.itemTitle">{{ i18n.ts._visibility.specified }}</span>
@@ -52,7 +52,6 @@ const modal = useTemplateRef('modal');
const props = withDefaults(defineProps<{
currentVisibility: typeof Misskey.noteVisibilities[number];
isSilenced: boolean;
- localOnly: boolean;
anchorElement?: HTMLElement | null;
isReplyVisibilitySpecified?: boolean;
}>(), {
diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
index 6aaee76565..6513ca385d 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
@@ -46,7 +46,7 @@ async function renderChart() {
return new Date(y, m, d - ago);
};
- const format = (arr) => {
+ const format = (arr: number[]) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 8bef225de5..2ce1912b86 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -94,7 +94,7 @@ function signup() {
});
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
openInstanceMenu(ev);
}
</script>
diff --git a/packages/frontend/src/components/MkWaitingDialog.vue b/packages/frontend/src/components/MkWaitingDialog.vue
index 820cf05e1f..18f2b3e189 100644
--- a/packages/frontend/src/components/MkWaitingDialog.vue
+++ b/packages/frontend/src/components/MkWaitingDialog.vue
@@ -26,8 +26,8 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (ev: 'done');
- (ev: 'closed');
+ (ev: 'done'): void;
+ (ev: 'closed'): void;
}>();
function done() {
diff --git a/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue b/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
index 154b3ffc27..8e5bb6221d 100644
--- a/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
+++ b/packages/frontend/src/components/MkWatermarkEditorDialog.Layer.vue
@@ -387,7 +387,7 @@ onMounted(async () => {
}
});
-function chooseFile(ev: MouseEvent) {
+function chooseFile(ev: PointerEvent) {
selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
diff --git a/packages/frontend/src/components/MkWatermarkEditorDialog.vue b/packages/frontend/src/components/MkWatermarkEditorDialog.vue
index 6cd2111598..cadf9ba522 100644
--- a/packages/frontend/src/components/MkWatermarkEditorDialog.vue
+++ b/packages/frontend/src/components/MkWatermarkEditorDialog.vue
@@ -16,50 +16,49 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<template #header><i class="ti ti-copyright"></i> {{ i18n.ts._watermarkEditor.title }}</template>
- <div :class="$style.root">
- <div :class="$style.container">
- <div :class="[$style.preview, prefer.s.animation ? $style.animatedBg : null]">
- <canvas ref="canvasEl" :class="$style.previewCanvas"></canvas>
- <div :class="$style.previewContainer">
- <div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
- <div v-if="props.image == null" class="_acrylic" :class="$style.previewControls">
- <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '3_2' ? $style.active : null]" @click="sampleImageType = '3_2'"><i class="ti ti-crop-landscape"></i></button>
- <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '2_3' ? $style.active : null]" @click="sampleImageType = '2_3'"><i class="ti ti-crop-portrait"></i></button>
- <button class="_button" :class="[$style.previewControlsButton]" @click="choiceImage"><i class="ti ti-upload"></i></button>
- </div>
+ <MkPreviewWithControls>
+ <template #preview>
+ <canvas ref="canvasEl" :class="$style.previewCanvas"></canvas>
+ <div :class="$style.previewContainer">
+ <div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
+ <div v-if="props.image == null" class="_acrylic" :class="$style.previewControls">
+ <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '3_2' ? $style.active : null]" @click="sampleImageType = '3_2'"><i class="ti ti-crop-landscape"></i></button>
+ <button class="_button" :class="[$style.previewControlsButton, sampleImageType === '2_3' ? $style.active : null]" @click="sampleImageType = '2_3'"><i class="ti ti-crop-portrait"></i></button>
+ <button class="_button" :class="[$style.previewControlsButton]" @click="choiceImage"><i class="ti ti-upload"></i></button>
</div>
</div>
- <div :class="$style.controls">
- <div class="_spacer _gaps">
- <div class="_gaps_s">
- <MkFolder v-for="(layer, i) in layers" :key="layer.id" :defaultOpen="false" :canPage="false">
- <template #label>
- <div v-if="layer.type === 'text'">{{ i18n.ts._watermarkEditor.text }}</div>
- <div v-if="layer.type === 'image'">{{ i18n.ts._watermarkEditor.image }}</div>
- <div v-if="layer.type === 'qr'">{{ i18n.ts._watermarkEditor.qr }}</div>
- <div v-if="layer.type === 'stripe'">{{ i18n.ts._watermarkEditor.stripe }}</div>
- <div v-if="layer.type === 'polkadot'">{{ i18n.ts._watermarkEditor.polkadot }}</div>
- <div v-if="layer.type === 'checker'">{{ i18n.ts._watermarkEditor.checker }}</div>
- </template>
- <template #footer>
- <div class="_buttons">
- <MkButton iconOnly @click="removeLayer(layer)"><i class="ti ti-trash"></i></MkButton>
- <MkButton iconOnly @click="swapUpLayer(layer)"><i class="ti ti-arrow-up"></i></MkButton>
- <MkButton iconOnly @click="swapDownLayer(layer)"><i class="ti ti-arrow-down"></i></MkButton>
- </div>
- </template>
+ </template>
- <XLayer
- v-model:layer="layers[i]"
- ></XLayer>
- </MkFolder>
+ <template #controls>
+ <div class="_spacer _gaps">
+ <div class="_gaps_s">
+ <MkFolder v-for="(layer, i) in layers" :key="layer.id" :defaultOpen="false" :canPage="false">
+ <template #label>
+ <div v-if="layer.type === 'text'">{{ i18n.ts._watermarkEditor.text }}</div>
+ <div v-if="layer.type === 'image'">{{ i18n.ts._watermarkEditor.image }}</div>
+ <div v-if="layer.type === 'qr'">{{ i18n.ts._watermarkEditor.qr }}</div>
+ <div v-if="layer.type === 'stripe'">{{ i18n.ts._watermarkEditor.stripe }}</div>
+ <div v-if="layer.type === 'polkadot'">{{ i18n.ts._watermarkEditor.polkadot }}</div>
+ <div v-if="layer.type === 'checker'">{{ i18n.ts._watermarkEditor.checker }}</div>
+ </template>
+ <template #footer>
+ <div class="_buttons">
+ <MkButton iconOnly @click="removeLayer(layer)"><i class="ti ti-trash"></i></MkButton>
+ <MkButton iconOnly @click="swapUpLayer(layer)"><i class="ti ti-arrow-up"></i></MkButton>
+ <MkButton iconOnly @click="swapDownLayer(layer)"><i class="ti ti-arrow-down"></i></MkButton>
+ </div>
+ </template>
- <MkButton rounded primary style="margin: 0 auto;" @click="addLayer"><i class="ti ti-plus"></i></MkButton>
- </div>
+ <XLayer
+ v-model:layer="layers[i]"
+ ></XLayer>
+ </MkFolder>
+
+ <MkButton rounded primary style="margin: 0 auto;" @click="addLayer"><i class="ti ti-plus"></i></MkButton>
</div>
</div>
- </div>
- </div>
+ </template>
+ </MkPreviewWithControls>
</MkModalWindow>
</template>
@@ -69,6 +68,7 @@ import type { WatermarkLayers, WatermarkPreset } from '@/utility/watermark/Water
import { WatermarkRenderer } from '@/utility/watermark/WatermarkRenderer.js';
import { i18n } from '@/i18n.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkPreviewWithControls from '@/components/MkPreviewWithControls.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
@@ -350,7 +350,7 @@ async function save() {
}
}
-function addLayer(ev: MouseEvent) {
+function addLayer(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts._watermarkEditor.text,
action: () => {
@@ -411,33 +411,6 @@ function removeLayer(layer: WatermarkPreset['layers'][number]) {
</script>
<style module>
-.root {
- container-type: inline-size;
- height: 100%;
-}
-
-.container {
- height: 100%;
- display: grid;
- grid-template-columns: 1fr 400px;
-}
-
-.preview {
- position: relative;
- background-color: var(--MI_THEME-bg);
- background-image: linear-gradient(135deg, transparent 30%, var(--MI_THEME-panel) 30%, var(--MI_THEME-panel) 50%, transparent 50%, transparent 80%, var(--MI_THEME-panel) 80%, var(--MI_THEME-panel) 100%);
- background-size: 20px 20px;
-}
-
-.animatedBg {
- animation: bg 1.2s linear infinite;
-}
-
-@keyframes bg {
- 0% { background-position: 0 0; }
- 100% { background-position: -20px -20px; }
-}
-
.previewContainer {
display: flex;
flex-direction: column;
@@ -474,16 +447,6 @@ function removeLayer(layer: WatermarkPreset['layers'][number]) {
}
}
-.previewSpinner {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- pointer-events: none;
- user-select: none;
- -webkit-user-drag: none;
-}
-
.previewCanvas {
position: absolute;
top: 0;
@@ -494,15 +457,4 @@ function removeLayer(layer: WatermarkPreset['layers'][number]) {
box-sizing: border-box;
object-fit: contain;
}
-
-.controls {
- overflow-y: scroll;
-}
-
-@container (max-width: 800px) {
- .container {
- grid-template-columns: 1fr;
- grid-template-rows: 1fr 1fr;
- }
-}
</style>
diff --git a/packages/frontend/src/components/MkWidgetSettingsDialog.vue b/packages/frontend/src/components/MkWidgetSettingsDialog.vue
new file mode 100644
index 0000000000..292b4010ff
--- /dev/null
+++ b/packages/frontend/src/components/MkWidgetSettingsDialog.vue
@@ -0,0 +1,174 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+ ref="dialog"
+ :width="1000"
+ :height="600"
+ :scroll="false"
+ :withOkButton="true"
+ :okButtonDisabled="!canSave"
+ @close="cancel()"
+ @ok="save()"
+ @closed="emit('closed')"
+>
+ <template #header><i class="ti ti-icons"></i> {{ i18n.ts._widgets[widgetName] ?? widgetName }}</template>
+
+ <MkPreviewWithControls>
+ <template #preview>
+ <div :class="$style.previewWrapper">
+ <div class="_acrylic" :class="$style.previewTitle">{{ i18n.ts.preview }}</div>
+
+ <div ref="resizerRootEl" :class="$style.previewResizerRoot" inert>
+ <div
+ ref="resizerEl"
+ :class="$style.previewResizer"
+ :style="{ transform: widgetStyle }"
+ >
+ <component
+ :is="`widget-${widgetName}`"
+ :widget="{ name: widgetName, id: '__PREVIEW__', data: settings }"
+ ></component>
+ </div>
+ </div>
+ </div>
+ </template>
+
+ <template #controls>
+ <div class="_spacer">
+ <MkForm v-model="settings" :form="form" @canSaveStateChange="onCanSaveStateChanged"/>
+ </div>
+ </template>
+ </MkPreviewWithControls>
+</MkModalWindow>
+</template>
+
+<script setup lang="ts">
+import { useTemplateRef, ref, computed, onBeforeUnmount, onMounted } from 'vue';
+import MkPreviewWithControls from './MkPreviewWithControls.vue';
+import type { Form } from '@/utility/form.js';
+import type { WidgetName } from '@/widgets/index.js';
+import { deepClone } from '@/utility/clone.js';
+import { i18n } from '@/i18n.js';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import MkForm from '@/components/MkForm.vue';
+
+const props = defineProps<{
+ widgetName: WidgetName;
+ form: Form;
+ currentSettings: Record<string, any>;
+}>();
+
+const emit = defineEmits<{
+ (ev: 'saved', settings: Record<string, any>): void;
+ (ev: 'canceled'): void;
+ (ev: 'closed'): void;
+}>();
+
+const dialog = useTemplateRef('dialog');
+
+const settings = ref<Record<string, any>>(deepClone(props.currentSettings));
+
+const canSave = ref(true);
+
+function onCanSaveStateChanged(newCanSave: boolean) {
+ canSave.value = newCanSave;
+}
+
+function save() {
+ if (!canSave.value) return;
+ emit('saved', deepClone(settings.value));
+ dialog.value?.close();
+}
+
+function cancel() {
+ emit('canceled');
+ dialog.value?.close();
+}
+
+//#region プレビューのリサイズ
+const resizerRootEl = useTemplateRef('resizerRootEl');
+const resizerEl = useTemplateRef('resizerEl');
+const widgetHeight = ref(0);
+const widgetScale = ref(1);
+const widgetStyle = computed(() => {
+ return `translate(-50%, -50%) scale(${widgetScale.value})`;
+});
+const ro1 = new ResizeObserver(() => {
+ widgetHeight.value = resizerEl.value!.clientHeight;
+ calcScale();
+});
+const ro2 = new ResizeObserver(() => {
+ calcScale();
+});
+
+function calcScale() {
+ if (!resizerRootEl.value) return;
+ const previewWidth = resizerRootEl.value.clientWidth - 40; // 左右の余白 20pxずつ
+ const previewHeight = resizerRootEl.value.clientHeight - 40; // 上下の余白 20pxずつ
+ const widgetWidth = 280;
+ const scale = Math.min(previewWidth / widgetWidth, previewHeight / widgetHeight.value, 1); // 拡大はしないので1を上限に
+ widgetScale.value = scale;
+}
+
+onMounted(() => {
+ if (resizerEl.value) {
+ ro1.observe(resizerEl.value);
+ }
+ if (resizerRootEl.value) {
+ ro2.observe(resizerRootEl.value);
+ }
+ calcScale();
+});
+
+onBeforeUnmount(() => {
+ ro1.disconnect();
+ ro2.disconnect();
+});
+//#endregion
+</script>
+
+<style module>
+.previewContainer {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ user-select: none;
+ -webkit-user-drag: none;
+}
+
+.previewTitle {
+ position: absolute;
+ z-index: 100;
+ top: 8px;
+ left: 8px;
+ padding: 6px 10px;
+ border-radius: 6px;
+ font-size: 85%;
+}
+
+.previewWrapper {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ pointer-events: none;
+ user-select: none;
+ -webkit-user-drag: none;
+}
+
+.previewResizerRoot {
+ position: relative;
+ flex: 1 0;
+}
+
+.previewResizer {
+ position: absolute;
+ container-type: inline-size;
+ top: 50%;
+ left: 50%;
+ width: 280px;
+}
+</style>
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index cf7c2cda80..a27613c24c 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root">
+<div :class="$style.root" class="_gaps_s">
<template v-if="edit">
<header :class="$style.editHeader">
<MkSelect v-model="widgetAdderSelected" :items="widgetAdderSelectedDef" style="margin-bottom: var(--MI-margin)" data-cy-widget-select>
@@ -13,27 +13,23 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton inline primary data-cy-widget-add @click="addWidget"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
<MkButton inline @click="emit('exit')">{{ i18n.ts.close }}</MkButton>
</header>
- <Sortable
+ <MkDraggable
:modelValue="props.widgets"
- itemKey="id"
- handle=".handle"
- :animation="150"
- :group="{ name: 'SortableMkWidgets' }"
- :class="$style.editEditing"
+ direction="vertical"
+ withGaps
+ group="MkWidgets"
@update:modelValue="v => emit('updateWidgets', v)"
>
- <template #item="{element}">
+ <template #default="{ item }">
<div :class="[$style.widget, $style.customizeContainer]" data-cy-customize-container>
- <button :class="$style.customizeContainerConfig" class="_button" @click.prevent.stop="configWidget(element.id)"><i class="ti ti-settings"></i></button>
- <button :class="$style.customizeContainerRemove" data-cy-customize-container-remove class="_button" @click.prevent.stop="removeWidget(element)"><i class="ti ti-x"></i></button>
- <div class="handle">
- <component :is="`widget-${element.name}`" :ref="el => widgetRefs[element.id] = el" class="widget" :class="$style.customizeContainerHandleWidget" :widget="element" @updateProps="updateWidget(element.id, $event)"/>
- </div>
+ <button :class="$style.customizeContainerConfig" class="_button" @click.prevent.stop="configWidget(item.id)"><i class="ti ti-settings"></i></button>
+ <button :class="$style.customizeContainerRemove" data-cy-customize-container-remove class="_button" @click.prevent.stop="removeWidget(item)"><i class="ti ti-x"></i></button>
+ <component :is="`widget-${item.name}`" :ref="(el: any) => widgetRefs[item.id] = el" :class="$style.customizeContainerHandleWidget" :widget="item" @updateProps="updateWidget(item.id, $event)"/>
</div>
</template>
- </Sortable>
+ </MkDraggable>
</template>
- <component :is="`widget-${widget.name}`" v-for="widget in _widgets" v-else :key="widget.id" :ref="el => widgetRefs[widget.id] = el" :class="$style.widget" :widget="widget" @updateProps="updateWidget(widget.id, $event)" @contextmenu.stop="onContextmenu(widget, $event)"/>
+ <component :is="`widget-${widget.name}`" v-for="widget in _widgets" v-else :key="widget.id" :ref="(el: any) => widgetRefs[widget.id] = el" :class="$style.widget" :widget="widget" @updateProps="updateWidget(widget.id, $event)" @contextmenu.stop="onContextmenu(widget, $event)"/>
</div>
</template>
@@ -49,19 +45,19 @@ export type DefaultStoredWidget = {
</script>
<script lang="ts" setup>
-import { defineAsyncComponent, ref, computed } from 'vue';
+import { computed } from 'vue';
import { isLink } from '@@/js/is-link.js';
+import type { Component } from 'vue';
import { genId } from '@/utility/id.js';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
import { widgets as widgetDefs, federationWidgets } from '@/widgets/index.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { useMkSelect } from '@/composables/use-mkselect.js';
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-
const props = defineProps<{
widgets: Widget[];
edit: boolean;
@@ -69,13 +65,13 @@ const props = defineProps<{
const _widgetDefs = computed(() => {
if (instance.federation === 'none') {
- return widgetDefs.filter(x => !federationWidgets.includes(x));
+ return widgetDefs.filter(x => !federationWidgets.includes(x as any));
} else {
return widgetDefs;
}
});
-const _widgets = computed(() => props.widgets.filter(x => _widgetDefs.value.includes(x.name)));
+const _widgets = computed(() => props.widgets.filter(x => _widgetDefs.value.includes(x.name as any)));
const emit = defineEmits<{
(ev: 'updateWidgets', widgets: Widget[]): void;
@@ -85,10 +81,11 @@ const emit = defineEmits<{
(ev: 'exit'): void;
}>();
-const widgetRefs = {};
-const configWidget = (id: string) => {
+const widgetRefs = {} as Record<string, Component & { configure: () => void }>;
+
+function configWidget(id: string) {
widgetRefs[id].configure();
-};
+}
const {
model: widgetAdderSelected,
@@ -98,7 +95,7 @@ const {
initialValue: null,
});
-const addWidget = () => {
+function addWidget() {
if (widgetAdderSelected.value == null) return;
emit('addWidget', {
@@ -108,23 +105,25 @@ const addWidget = () => {
});
widgetAdderSelected.value = null;
-};
-const removeWidget = (widget) => {
+}
+
+function removeWidget(widget: Widget) {
emit('removeWidget', widget);
-};
-const updateWidget = (id: Widget['id'], data: Widget['data']) => {
+}
+
+function updateWidget(id: Widget['id'], data: Widget['data']) {
emit('updateWidget', { id, data });
-};
+}
-function onContextmenu(widget: Widget, ev: MouseEvent) {
+function onContextmenu(widget: Widget, ev: PointerEvent) {
const element = ev.target as HTMLElement | null;
if (element && isLink(element)) return;
- if (element && (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(element.tagName) || element.attributes['contenteditable'])) return;
+ if (element && (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(element.tagName) || element.attributes.getNamedItem('contenteditable') != null)) return;
if (window.getSelection()?.toString() !== '') return;
os.contextMenu([{
type: 'label',
- text: i18n.ts._widgets[widget.name],
+ text: i18n.ts._widgets[widget.name as typeof widgetDefs[number]],
}, {
icon: 'ti ti-settings',
text: i18n.ts.settings,
@@ -142,11 +141,6 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
.widget {
contain: content;
- margin: var(--MI-margin) 0;
-
- &:first-of-type {
- margin-top: 0;
- }
}
.edit {
@@ -158,10 +152,6 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
padding: 4px;
}
}
-
- &Editing {
- min-height: 100px;
- }
}
.customizeContainer {
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index e5ac791d0b..c79bf44794 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-for="button in buttonsLeft" v-tooltip="button.title" class="_button" :class="[$style.headerButton, { [$style.highlighted]: button.highlighted }]" @click="button.onClick"><i :class="button.icon"></i></button>
</template>
</span>
- <span :class="$style.headerTitle" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown">
+ <span :class="$style.headerTitle" @pointerdown.prevent="onHeaderPointerdown">
<slot name="header"></slot>
</span>
<span :class="$style.headerRight">
@@ -39,14 +39,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<template v-if="canResize && !minimized">
- <div :class="$style.handleTop" @mousedown.prevent="onTopHandleMousedown"></div>
- <div :class="$style.handleRight" @mousedown.prevent="onRightHandleMousedown"></div>
- <div :class="$style.handleBottom" @mousedown.prevent="onBottomHandleMousedown"></div>
- <div :class="$style.handleLeft" @mousedown.prevent="onLeftHandleMousedown"></div>
- <div :class="$style.handleTopLeft" @mousedown.prevent="onTopLeftHandleMousedown"></div>
- <div :class="$style.handleTopRight" @mousedown.prevent="onTopRightHandleMousedown"></div>
- <div :class="$style.handleBottomRight" @mousedown.prevent="onBottomRightHandleMousedown"></div>
- <div :class="$style.handleBottomLeft" @mousedown.prevent="onBottomLeftHandleMousedown"></div>
+ <div :class="$style.handleTop" @pointerdown.prevent="onTopHandlePointerdown"></div>
+ <div :class="$style.handleRight" @pointerdown.prevent="onRightHandlePointerdown"></div>
+ <div :class="$style.handleBottom" @pointerdown.prevent="onBottomHandlePointerdown"></div>
+ <div :class="$style.handleLeft" @pointerdown.prevent="onLeftHandlePointerdown"></div>
+ <div :class="$style.handleTopLeft" @pointerdown.prevent="onTopLeftHandlePointerdown"></div>
+ <div :class="$style.handleTopRight" @pointerdown.prevent="onTopRightHandlePointerdown"></div>
+ <div :class="$style.handleBottomRight" @pointerdown.prevent="onBottomRightHandlePointerdown"></div>
+ <div :class="$style.handleBottomLeft" @pointerdown.prevent="onBottomLeftHandlePointerdown"></div>
</template>
</div>
</Transition>
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, provide, useTemplateRef, ref } from 'vue';
import type { MenuItem } from '@/types/menu.js';
-import contains from '@/utility/contains.js';
+import { elementContains } from '@/utility/element-contains.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
@@ -70,20 +70,39 @@ type WindowButton = {
const minHeight = 50;
const minWidth = 250;
-function dragListen(fn: (ev: MouseEvent | TouchEvent) => void) {
- window.addEventListener('mousemove', fn);
- window.addEventListener('touchmove', fn);
- window.addEventListener('mouseleave', dragClear.bind(null, fn));
- window.addEventListener('mouseup', dragClear.bind(null, fn));
- window.addEventListener('touchend', dragClear.bind(null, fn));
+function dragListen(fn: (ev: PointerEvent) => void) {
+ window.addEventListener('pointermove', fn);
+ const clear = () => {
+ dragClear(fn);
+ };
+ window.addEventListener('pointerup', clear, { once: true });
+ window.addEventListener('pointercancel', clear, { once: true });
+ window.addEventListener('blur', clear, { once: true });
}
-function dragClear(fn) {
- window.removeEventListener('mousemove', fn);
- window.removeEventListener('touchmove', fn);
- window.removeEventListener('mouseleave', dragClear);
- window.removeEventListener('mouseup', dragClear);
- window.removeEventListener('touchend', dragClear);
+function dragClear(fn: (ev: PointerEvent) => void) {
+ window.removeEventListener('pointermove', fn);
+}
+
+function capturePointer(evt: PointerEvent) {
+ const target = evt.currentTarget;
+ if (!(target instanceof HTMLElement)) return;
+ if (!target.setPointerCapture) return;
+
+ try {
+ target.setPointerCapture(evt.pointerId);
+ } catch {
+ return;
+ }
+
+ const release = () => {
+ if (target.hasPointerCapture(evt.pointerId)) {
+ target.releasePointerCapture(evt.pointerId);
+ }
+ };
+
+ window.addEventListener('pointerup', release, { once: true });
+ window.addEventListener('pointercancel', release, { once: true });
}
const props = withDefaults(defineProps<{
@@ -128,7 +147,7 @@ function close() {
showing.value = false;
}
-function onKeydown(evt) {
+function onKeydown(evt: KeyboardEvent) {
if (evt.which === 27) { // Esc
evt.preventDefault();
evt.stopPropagation();
@@ -136,7 +155,7 @@ function onKeydown(evt) {
}
}
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
if (props.contextmenu) {
os.contextMenu(props.contextmenu, ev);
}
@@ -209,15 +228,17 @@ function onDblClick() {
}
}
-function getPositionX(event: MouseEvent | TouchEvent) {
- return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientX : 'clientX' in event ? event.clientX : 0;
+function getPositionX(event: PointerEvent) {
+ return event.clientX;
}
-function getPositionY(event: MouseEvent | TouchEvent) {
- return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientY : 'clientY' in event ? event.clientY : 0;
+function getPositionY(event: PointerEvent) {
+ return event.clientY;
}
-function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
+function onHeaderPointerdown(evt: PointerEvent) {
+ capturePointer(evt);
+
// 右クリックはコンテキストメニューを開こうとした可能性が高いため無視
if ('button' in evt && evt.button === 2) return;
@@ -240,7 +261,7 @@ function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
const main = rootEl.value;
if (main == null) return;
- if (!contains(main, window.document.activeElement)) main.focus();
+ if (!elementContains(main, window.document.activeElement)) main.focus();
const position = main.getBoundingClientRect();
@@ -289,7 +310,9 @@ function onHeaderMousedown(evt: MouseEvent | TouchEvent) {
}
// 上ハンドル掴み時
-function onTopHandleMousedown(evt: MouseEvent | TouchEvent) {
+function onTopHandlePointerdown(evt: PointerEvent) {
+ capturePointer(evt);
+
const main = rootEl.value;
// どういうわけかnullになることがある
if (main == null) return;
@@ -317,7 +340,9 @@ function onTopHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 右ハンドル掴み時
-function onRightHandleMousedown(evt: MouseEvent | TouchEvent) {
+function onRightHandlePointerdown(evt: PointerEvent) {
+ capturePointer(evt);
+
const main = rootEl.value;
if (main == null) return;
@@ -342,7 +367,9 @@ function onRightHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 下ハンドル掴み時
-function onBottomHandleMousedown(evt: MouseEvent | TouchEvent) {
+function onBottomHandlePointerdown(evt: PointerEvent) {
+ capturePointer(evt);
+
const main = rootEl.value;
if (main == null) return;
@@ -367,7 +394,9 @@ function onBottomHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 左ハンドル掴み時
-function onLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
+function onLeftHandlePointerdown(evt: PointerEvent) {
+ capturePointer(evt);
+
const main = rootEl.value;
if (main == null) return;
@@ -394,48 +423,48 @@ function onLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
}
// 左上ハンドル掴み時
-function onTopLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
- onTopHandleMousedown(evt);
- onLeftHandleMousedown(evt);
+function onTopLeftHandlePointerdown(evt: PointerEvent) {
+ onTopHandlePointerdown(evt);
+ onLeftHandlePointerdown(evt);
}
// 右上ハンドル掴み時
-function onTopRightHandleMousedown(evt: MouseEvent | TouchEvent) {
- onTopHandleMousedown(evt);
- onRightHandleMousedown(evt);
+function onTopRightHandlePointerdown(evt: PointerEvent) {
+ onTopHandlePointerdown(evt);
+ onRightHandlePointerdown(evt);
}
// 右下ハンドル掴み時
-function onBottomRightHandleMousedown(evt: MouseEvent | TouchEvent) {
- onBottomHandleMousedown(evt);
- onRightHandleMousedown(evt);
+function onBottomRightHandlePointerdown(evt: PointerEvent) {
+ onBottomHandlePointerdown(evt);
+ onRightHandlePointerdown(evt);
}
// 左下ハンドル掴み時
-function onBottomLeftHandleMousedown(evt: MouseEvent | TouchEvent) {
- onBottomHandleMousedown(evt);
- onLeftHandleMousedown(evt);
+function onBottomLeftHandlePointerdown(evt: PointerEvent) {
+ onBottomHandlePointerdown(evt);
+ onLeftHandlePointerdown(evt);
}
// 高さを適用
-function applyTransformHeight(height) {
+function applyTransformHeight(height: number) {
if (height > window.innerHeight) height = window.innerHeight;
if (rootEl.value) rootEl.value.style.height = height + 'px';
}
// 幅を適用
-function applyTransformWidth(width) {
+function applyTransformWidth(width: number) {
if (width > window.innerWidth) width = window.innerWidth;
if (rootEl.value) rootEl.value.style.width = width + 'px';
}
// Y座標を適用
-function applyTransformTop(top) {
+function applyTransformTop(top: number) {
if (rootEl.value) rootEl.value.style.top = top + 'px';
}
// X座標を適用
-function applyTransformLeft(left) {
+function applyTransformLeft(left: number) {
if (rootEl.value) rootEl.value.style.left = left + 'px';
}
@@ -566,6 +595,7 @@ defineExpose({
overflow: hidden;
text-overflow: ellipsis;
cursor: move;
+ touch-action: none;
}
.content {
@@ -579,6 +609,7 @@ $handleSize: 8px;
.handle {
position: absolute;
+ touch-action: none;
}
.handleTop {
diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue
index 2375bcc9eb..20c4475779 100644
--- a/packages/frontend/src/components/MkYouTubePlayer.vue
+++ b/packages/frontend/src/components/MkYouTubePlayer.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkWindow :initialWidth="640" :initialHeight="402" :canResize="true" :closeButton="true">
+<MkWindow :initialWidth="640" :initialHeight="402" :canResize="true" :closeButton="true" @closed="emit('closed')">
<template #header>
<i class="icon ti ti-brand-youtube" style="margin-right: 0.5em;"></i>
<span>{{ title ?? 'YouTube' }}</span>
@@ -34,6 +34,10 @@ const props = defineProps<{
url: string;
}>();
+const emit = defineEmits<{
+ (ev: 'closed'): void;
+}>();
+
const requestUrl = new URL(props.url);
if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
diff --git a/packages/frontend/src/components/global/I18n.vue b/packages/frontend/src/components/global/I18n.vue
index 9866e50958..1fad1ee9e6 100644
--- a/packages/frontend/src/components/global/I18n.vue
+++ b/packages/frontend/src/components/global/I18n.vue
@@ -46,6 +46,6 @@ const parsed = computed(() => {
});
const render = () => {
- return h(props.tag, parsed.value.map(x => typeof x === 'string' ? (props.textTag ? h(props.textTag, x) : x) : slots[x.arg]()));
+ return h(props.tag, parsed.value.map(x => typeof x === 'string' ? (props.textTag ? h(props.textTag, x) : x) : (slots as any)[x.arg]()));
};
</script>
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 99693a4c00..7d2908d4be 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -48,7 +48,7 @@ const active = computed(() => {
return resolved.route.name === router.currentRoute.value.name;
});
-function onContextmenu(ev) {
+function onContextmenu(ev: PointerEvent) {
const selection = window.getSelection();
if (selection && selection.toString() !== '') return;
os.contextMenu([{
@@ -85,7 +85,7 @@ function openWindow() {
os.pageWindow(props.to);
}
-function nav(ev: MouseEvent) {
+function nav(ev: PointerEvent) {
// 制御キーとの組み合わせは無視(shiftを除く)
if (ev.metaKey || ev.altKey || ev.ctrlKey) return;
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index e7208ed574..b413fef3b8 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -11,16 +11,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="user.isCat" :class="[$style.ears]">
<div :class="$style.earLeft">
<div v-if="false" :class="$style.layer">
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
</div>
</div>
<div :class="$style.earRight">
<div v-if="false" :class="$style.layer">
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
- <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
+ <div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"></div>
</div>
</div>
</div>
@@ -77,7 +77,7 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'click', v: MouseEvent): void;
+ (ev: 'click', v: PointerEvent): void;
}>();
const showDecoration = props.forceShowDecoration || prefer.s.showAvatarDecorations;
@@ -91,7 +91,7 @@ const url = computed(() => {
return props.user.avatarUrl;
});
-function onClick(ev: MouseEvent): void {
+function onClick(ev: PointerEvent): void {
if (props.link) return;
emit('click', ev);
}
diff --git a/packages/frontend/src/components/global/MkCondensedLine.vue b/packages/frontend/src/components/global/MkCondensedLine.vue
index 473d444c16..baa8d783f1 100644
--- a/packages/frontend/src/components/global/MkCondensedLine.vue
+++ b/packages/frontend/src/components/global/MkCondensedLine.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<span :class="$style.container">
<span ref="content" :class="$style.content" :style="{ maxWidth: `${100 / minScale}%` }">
- <slot/>
+ <slot></slot>
</span>
</span>
</template>
@@ -23,8 +23,8 @@ const observer = new ResizeObserver((entries) => {
transform: string;
}[] = [];
for (const entry of entries) {
- const content = (entry.target[contentSymbol] ? entry.target : entry.target.firstElementChild) as HTMLSpanElement;
- const props: Required<Props> = content[contentSymbol];
+ const content = ((entry.target as any)[contentSymbol] ? entry.target : entry.target.firstElementChild) as HTMLSpanElement;
+ const props: Required<Props> = (content as any)[contentSymbol];
const container = content.parentElement as HTMLSpanElement;
const contentWidth = content.getBoundingClientRect().width;
const containerWidth = container.getBoundingClientRect().width;
@@ -46,15 +46,15 @@ const props = withDefaults(defineProps<Props>(), {
const content = ref<HTMLSpanElement>();
watch(content, (value, oldValue) => {
- if (oldValue) {
- delete oldValue[contentSymbol];
+ if (oldValue != null) {
+ delete (oldValue as any)[contentSymbol];
observer.unobserve(oldValue);
if (oldValue.parentElement) {
observer.unobserve(oldValue.parentElement);
}
}
- if (value) {
- value[contentSymbol] = props;
+ if (value != null) {
+ (value as any)[contentSymbol] = props;
observer.observe(value);
if (value.parentElement) {
observer.observe(value.parentElement);
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index 31c358eee7..9a171876a0 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -102,7 +102,7 @@ const url = computed(() => {
const alt = computed(() => `:${customEmojiName.value}:`);
const errored = ref(url.value == null);
-function onClick(ev: MouseEvent) {
+function onClick(ev: PointerEvent) {
if (props.menu) {
const menuItems: MenuItem[] = [];
diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue
index 792f9c7d6f..686720cec2 100644
--- a/packages/frontend/src/components/global/MkEmoji.vue
+++ b/packages/frontend/src/components/global/MkEmoji.vue
@@ -67,7 +67,7 @@ function unmute() {
});
}
-function onClick(ev: MouseEvent) {
+function onClick(ev: PointerEvent) {
if (props.menu) {
const menuItems: MenuItem[] = [];
diff --git a/packages/frontend/src/components/global/MkMfm.ts b/packages/frontend/src/components/global/MkMfm.ts
index 3ad2fda0ee..706ea07417 100644
--- a/packages/frontend/src/components/global/MkMfm.ts
+++ b/packages/frontend/src/components/global/MkMfm.ts
@@ -233,7 +233,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
if (!useAnim) {
return genEl(token.children, scale);
}
- return h(MkSparkle, {}, genEl(token.children, scale));
+ return h(MkSparkle, {}, { default: () => genEl(token.children, scale) });
}
case 'rotate': {
const degrees = safeParseFloat(token.props.args.deg) ?? 90;
@@ -319,7 +319,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
]);
}
case 'clickable': {
- return h('span', { onClick(ev: MouseEvent): void {
+ return h('span', { onClick(ev: PointerEvent): void {
ev.stopPropagation();
ev.preventDefault();
const clickEv = typeof token.props.args.ev === 'string' ? token.props.args.ev : '';
@@ -363,7 +363,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
url: token.props.url,
rel: 'nofollow noopener',
navigationBehavior: props.linkNavigationBehavior,
- }, genEl(token.children, scale, true))];
+ }, { default: () => genEl(token.children, scale, true) })];
}
case 'mention': {
@@ -381,7 +381,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
to: isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/user-tags/${encodeURIComponent(token.props.hashtag)}`,
style: 'color:var(--MI_THEME-hashtag);',
behavior: props.linkNavigationBehavior,
- }, `#${token.props.hashtag}`)];
+ }, { default: () => `#${token.props.hashtag}` })];
}
case 'blockCode': {
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index 1ef75281fd..857fd3d8b4 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts">
export type Tab = {
key: string;
- onClick?: (ev: MouseEvent) => void;
+ onClick?: (ev: PointerEvent) => void;
iconOnly?: boolean;
title: string;
icon?: string;
@@ -70,8 +70,8 @@ const props = withDefaults(defineProps<{
});
const emit = defineEmits<{
- (ev: 'update:tab', key: string);
- (ev: 'tabClick', key: string);
+ (ev: 'update:tab', key: string): void;
+ (ev: 'tabClick', key: string): void;
}>();
const el = useTemplateRef('el');
@@ -96,7 +96,7 @@ function onTabMousedown(tab: Tab, ev: MouseEvent): void {
}
}
-function onTabClick(t: Tab, ev: MouseEvent): void {
+function onTabClick(t: Tab, ev: PointerEvent): void {
emit('tabClick', t.key);
if (t.onClick) {
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 2f6dfed221..e8c93b7092 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -6,10 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div v-if="show" ref="el" :class="[$style.root]">
<div :class="[$style.upper, { [$style.slim]: narrow, [$style.thin]: thin_ }]">
- <div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu">
+ <div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" @click="openAccountMenu">
<MkAvatar :class="$style.avatar" :user="$i"/>
</div>
- <div v-else-if="!thin_ && narrow && !hideTitle" :class="[$style.buttons, $style.buttonsLeft]"></div>
+ <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttons"></div>
<template v-if="pageMetadata">
<div v-if="!hideTitle" :class="$style.titleContainer" @click="top">
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<XTabs v-if="!narrow || hideTitle" :class="$style.tabs" :tab="tab" :tabs="tabs" :rootEl="el" @update:tab="key => emit('update:tab', key)" @tabClick="onTabClick"/>
</template>
- <div v-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="[$style.buttons, $style.buttonsRight]">
+ <div v-if="(!thin_ && narrow && !hideTitle) || (actions && actions.length > 0)" :class="$style.buttons">
<template v-for="action in actions">
<button v-tooltip.noDelay="action.text" class="_button" :class="[$style.button, { [$style.highlighted]: action.highlighted }]" @click.stop="action.handler" @touchstart="preventDrag"><i :class="action.icon"></i></button>
</template>
@@ -61,7 +61,6 @@ export type PageHeaderProps = {
import { onMounted, onUnmounted, ref, inject, useTemplateRef, computed } from 'vue';
import { scrollToTop } from '@@/js/scroll.js';
import XTabs from './MkPageHeader.tabs.vue';
-import { globalEvents } from '@/events.js';
import { getAccountMenu } from '@/accounts.js';
import { $i } from '@/i.js';
import { DI } from '@/di.js';
@@ -72,7 +71,7 @@ const props = withDefaults(defineProps<PageHeaderProps>(), {
});
const emit = defineEmits<{
- (ev: 'update:tab', key: string);
+ (ev: 'update:tab', key: string): void;
}>();
//const viewId = inject(DI.viewId);
@@ -100,7 +99,7 @@ const top = () => {
}
};
-async function openAccountMenu(ev: MouseEvent) {
+async function openAccountMenu(ev: PointerEvent) {
const menuItems = await getAccountMenu({
withExtraOperation: true,
});
diff --git a/packages/frontend/src/components/global/MkResult.vue b/packages/frontend/src/components/global/MkResult.vue
index 2071859e57..0dfb23782d 100644
--- a/packages/frontend/src/components/global/MkResult.vue
+++ b/packages/frontend/src/components/global/MkResult.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<Transition :name="prefer.s.animation ? '_transition_zoom' : ''" appear>
- <div :class="[$style.root, { [$style.warn]: type === 'notFound', [$style.error]: type === 'error' }]" class="_gaps">
+ <div :class="$style.root" class="_gaps">
<img v-if="type === 'empty' && instance.infoImageUrl" :src="instance.infoImageUrl" draggable="false" :class="$style.img"/>
<MkSystemIcon v-else-if="type === 'empty'" type="info" :class="$style.icon"/>
<img v-if="type === 'notFound' && instance.notFoundImageUrl" :src="instance.notFoundImageUrl" draggable="false" :class="$style.img"/>
diff --git a/packages/frontend/src/components/global/MkTip.vue b/packages/frontend/src/components/global/MkTip.vue
index 231957a232..1827c16c89 100644
--- a/packages/frontend/src/components/global/MkTip.vue
+++ b/packages/frontend/src/components/global/MkTip.vue
@@ -32,7 +32,7 @@ function _closeTip() {
closeTip(props.k);
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
os.popupMenu([{
icon: 'ti ti-bulb-off',
text: i18n.ts.hideAllTips,
diff --git a/packages/frontend/src/components/global/PageWithHeader.vue b/packages/frontend/src/components/global/PageWithHeader.vue
index aac87b7669..a11b291418 100644
--- a/packages/frontend/src/components/global/PageWithHeader.vue
+++ b/packages/frontend/src/components/global/PageWithHeader.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div ref="rootEl" :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']">
+<div ref="rootEl" :class="reversed ? '_pageScrollableReversed' : '_pageScrollable'">
<MkStickyContainer>
<template #header>
<MkPageHeader v-if="prefer.s.showPageTabBarBottom && (props.tabs?.length ?? 0) > 0" v-bind="pageHeaderPropsWithoutTabs"/>
diff --git a/packages/frontend/src/components/global/StackingRouterView.vue b/packages/frontend/src/components/global/StackingRouterView.vue
index d52dd9b89d..689954189d 100644
--- a/packages/frontend/src/components/global/StackingRouterView.vue
+++ b/packages/frontend/src/components/global/StackingRouterView.vue
@@ -171,12 +171,6 @@ router.useListener('replace', ({ fullPath }) => {
width: 100%;
height: 100%;
}
-
- .tabContent {
- position: relative;
- width: 100%;
- height: 100%;
- }
}
&:not(:first-child) {
@@ -209,13 +203,17 @@ router.useListener('replace', ({ fullPath }) => {
.tabContent {
flex: 1;
- width: 100%;
- height: 100%;
- background: var(--MI_THEME-bg);
}
}
}
+.tabContent {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: var(--MI_THEME-bg);
+}
+
.tabMenu {
position: relative;
margin-left: auto;
diff --git a/packages/frontend/src/components/grid/MkDataCell.vue b/packages/frontend/src/components/grid/MkDataCell.vue
index 6f1dae8398..8745146ccf 100644
--- a/packages/frontend/src/components/grid/MkDataCell.vue
+++ b/packages/frontend/src/components/grid/MkDataCell.vue
@@ -188,7 +188,7 @@ function onCellKeyDown(ev: KeyboardEvent) {
}
}
-function onInputText(ev: Event) {
+function onInputText(ev: InputEvent) {
editingValue.value = (ev.target as HTMLInputElement).value;
}
diff --git a/packages/frontend/src/components/grid/MkGrid.vue b/packages/frontend/src/components/grid/MkGrid.vue
index 96d9e35773..097a91bad5 100644
--- a/packages/frontend/src/components/grid/MkGrid.vue
+++ b/packages/frontend/src/components/grid/MkGrid.vue
@@ -715,7 +715,7 @@ function onMouseUp(ev: MouseEvent) {
}
}
-function onContextMenu(ev: MouseEvent) {
+function onContextMenu(ev: PointerEvent) {
const cellAddress = getCellAddress(ev.target as HTMLElement);
if (_DEV_) {
console.log(`[grid][context-menu] button: ${ev.button}, cell: ${cellAddress.row}x${cellAddress.col}`);
diff --git a/packages/frontend/src/composables/use-chart-tooltip.ts b/packages/frontend/src/composables/use-chart-tooltip.ts
index a42f70ba02..9816de7c14 100644
--- a/packages/frontend/src/composables/use-chart-tooltip.ts
+++ b/packages/frontend/src/composables/use-chart-tooltip.ts
@@ -4,6 +4,7 @@
*/
import { onUnmounted, onDeactivated, ref } from 'vue';
+import type { Chart, ChartType, TooltipModel } from 'chart.js';
import * as os from '@/os.js';
import MkChartTooltip from '@/components/MkChartTooltip.vue';
@@ -40,7 +41,7 @@ export function useChartTooltip(opts: { position: 'top' | 'middle' } = { positio
tooltipShowing.value = false;
});
- function handler(context) {
+ function handler(context: { chart: Chart; tooltip: TooltipModel<ChartType> }) {
if (context.tooltip.opacity === 0) {
tooltipShowing.value = false;
return;
@@ -48,8 +49,8 @@ export function useChartTooltip(opts: { position: 'top' | 'middle' } = { positio
tooltipTitle.value = context.tooltip.title[0];
tooltipSeries.value = context.tooltip.body.map((b, i) => ({
- backgroundColor: context.tooltip.labelColors[i].backgroundColor,
- borderColor: context.tooltip.labelColors[i].borderColor,
+ backgroundColor: context.tooltip.labelColors[i].backgroundColor as string,
+ borderColor: context.tooltip.labelColors[i].borderColor as string,
text: b.lines[0],
}));
diff --git a/packages/frontend/src/composables/use-form.ts b/packages/frontend/src/composables/use-form.ts
index 38e9b40e20..812b66d1f0 100644
--- a/packages/frontend/src/composables/use-form.ts
+++ b/packages/frontend/src/composables/use-form.ts
@@ -31,7 +31,7 @@ export function useForm<T extends Record<string, any>>(initialState: T, save: (n
watch([currentState, previousState], () => {
for (const key in modifiedStates) {
- modifiedStates[key] = !deepEqual(currentState[key], previousState[key]);
+ (modifiedStates as any)[key] = !deepEqual(currentState[key], previousState[key]);
}
}, { deep: true });
diff --git a/packages/frontend/src/composables/use-mkselect.ts b/packages/frontend/src/composables/use-mkselect.ts
index 7cb470d169..97cbeffa8b 100644
--- a/packages/frontend/src/composables/use-mkselect.ts
+++ b/packages/frontend/src/composables/use-mkselect.ts
@@ -5,7 +5,8 @@
import { ref } from 'vue';
import type { Ref, MaybeRefOrGetter } from 'vue';
-import type { MkSelectItem, OptionValue, GetMkSelectValueTypesFromDef } from '@/components/MkSelect.vue';
+import type { MkSelectItem, GetMkSelectValueTypesFromDef } from '@/components/MkSelect.vue';
+import type { OptionValue } from '@/types/option-value.js';
type UnwrapReadonlyItems<T> = T extends readonly (infer U)[] ? U[] : T;
diff --git a/packages/frontend/src/composables/use-note-capture.ts b/packages/frontend/src/composables/use-note-capture.ts
index 2aeb9074e5..25a9383cd5 100644
--- a/packages/frontend/src/composables/use-note-capture.ts
+++ b/packages/frontend/src/composables/use-note-capture.ts
@@ -7,6 +7,7 @@ import { onUnmounted, reactive } from 'vue';
import * as Misskey from 'misskey-js';
import { EventEmitter } from 'eventemitter3';
import type { Reactive } from 'vue';
+import type { NoteUpdatedEvent } from 'misskey-js/streaming.types.js';
import { useStream } from '@/stream.js';
import { $i } from '@/i.js';
import { store } from '@/store.js';
@@ -15,9 +16,9 @@ import { prefer } from '@/preferences.js';
import { globalEvents } from '@/events.js';
export const noteEvents = new EventEmitter<{
- [ev: `reacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }) => void;
- [ev: `unreacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }) => void;
- [ev: `pollVoted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; choice: string; }) => void;
+ [ev: `reacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; } | null; }) => void;
+ [ev: `unreacted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; } | null; }) => void;
+ [ev: `pollVoted:${string}`]: (ctx: { userId: Misskey.entities.User['id']; choice: number; }) => void;
}>();
const fetchEvent = new EventEmitter<{
@@ -117,7 +118,7 @@ function realtimeSubscribe(props: {
const note = props.note;
const connection = useStream();
- function onStreamNoteUpdated(noteData): void {
+ function onStreamNoteUpdated(noteData: NoteUpdatedEvent): void {
const { type, id, body } = noteData;
if (id !== note.id) return;
@@ -136,7 +137,6 @@ function realtimeSubscribe(props: {
noteEvents.emit(`unreacted:${id}`, {
userId: body.userId,
reaction: body.reaction,
- emoji: body.emoji,
});
break;
}
@@ -194,9 +194,9 @@ export function useNoteCapture(props: {
parentNote: Misskey.entities.Note | null;
mock?: boolean;
}): {
- $note: Reactive<ReactiveNoteData>;
- subscribe: () => void;
- } {
+ $note: Reactive<ReactiveNoteData>;
+ subscribe: () => void;
+} {
const { note, parentNote, mock } = props;
const $note = reactive<ReactiveNoteData>({
@@ -224,7 +224,7 @@ export function useNoteCapture(props: {
const reactionUserMap = new Map<Misskey.entities.User['id'], string | typeof noReaction>();
let latestPollVotedKey: string | null = null;
- function onReacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
+ function onReacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; } | null; }): void {
let normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
normalizedName = normalizedName.match('\u200d') ? normalizedName : normalizedName.replace(/\ufe0f/g, '');
if (reactionUserMap.has(ctx.userId) && reactionUserMap.get(ctx.userId) === normalizedName) return;
@@ -244,7 +244,7 @@ export function useNoteCapture(props: {
}
}
- function onUnreacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; }; }): void {
+ function onUnreacted(ctx: { userId: Misskey.entities.User['id']; reaction: string; emoji?: { name: string; url: string; } | null; }): void {
let normalizedName = ctx.reaction.replace(/^:(\w+):$/, ':$1@.:');
normalizedName = normalizedName.match('\u200d') ? normalizedName : normalizedName.replace(/\ufe0f/g, '');
@@ -263,7 +263,7 @@ export function useNoteCapture(props: {
}
}
- function onPollVoted(ctx: { userId: Misskey.entities.User['id']; choice: string; }): void {
+ function onPollVoted(ctx: { userId: Misskey.entities.User['id']; choice: number; }): void {
const newPollVotedKey = `${ctx.userId}:${ctx.choice}`;
if (newPollVotedKey === latestPollVotedKey) return;
latestPollVotedKey = newPollVotedKey;
diff --git a/packages/frontend/src/composables/use-tooltip.ts b/packages/frontend/src/composables/use-tooltip.ts
index af76a3a1e8..e369928208 100644
--- a/packages/frontend/src/composables/use-tooltip.ts
+++ b/packages/frontend/src/composables/use-tooltip.ts
@@ -22,7 +22,7 @@ export function useTooltip(
let changeShowingState: (() => void) | null;
- let autoHidingTimer;
+ let autoHidingTimer: number | null = null;
const open = () => {
close();
@@ -43,7 +43,7 @@ export function useTooltip(
isHovering = false;
window.clearTimeout(timeoutId);
close();
- window.clearInterval(autoHidingTimer);
+ if (autoHidingTimer != null) window.clearInterval(autoHidingTimer);
}
}, 1000);
};
@@ -66,7 +66,7 @@ export function useTooltip(
if (!isHovering) return;
isHovering = false;
window.clearTimeout(timeoutId);
- window.clearInterval(autoHidingTimer);
+ if (autoHidingTimer != null) window.clearInterval(autoHidingTimer);
close();
};
@@ -81,7 +81,7 @@ export function useTooltip(
if (!isHovering) return;
isHovering = false;
window.clearTimeout(timeoutId);
- window.clearInterval(autoHidingTimer);
+ if (autoHidingTimer != null) window.clearInterval(autoHidingTimer);
close();
};
diff --git a/packages/frontend/src/composables/use-uploader.ts b/packages/frontend/src/composables/use-uploader.ts
index 8ffb1e656b..fabc04895e 100644
--- a/packages/frontend/src/composables/use-uploader.ts
+++ b/packages/frontend/src/composables/use-uploader.ts
@@ -664,7 +664,7 @@ export function useUploader(options: {
if (needsCompress) {
const config = {
- mimeType: isWebpSupported() ? 'image/webp' : 'image/jpeg',
+ mimeType: (isWebpSupported() ? 'image/webp' : 'image/jpeg') as 'image/webp' | 'image/jpeg',
maxWidth: compressionSettings.maxWidth,
maxHeight: compressionSettings.maxHeight,
quality: isWebpSupported() ? 0.85 : 0.8,
diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts
index 45d4b40fd7..e04d540c38 100644
--- a/packages/frontend/src/custom-emojis.ts
+++ b/packages/frontend/src/custom-emojis.ts
@@ -60,11 +60,11 @@ export async function fetchCustomEmojis(force = false) {
set('lastEmojisFetchedAt', now);
}
-let cachedTags;
+let cachedTags: string[] | null = null;
export function getCustomEmojiTags() {
if (cachedTags) return cachedTags;
- const tags = new Set();
+ const tags = new Set<string>();
for (const emoji of customEmojis.value) {
for (const tag of emoji.aliases) {
tags.add(tag);
diff --git a/packages/frontend/src/deck.ts b/packages/frontend/src/deck.ts
index 208adae8fe..e85ca76ca1 100644
--- a/packages/frontend/src/deck.ts
+++ b/packages/frontend/src/deck.ts
@@ -316,14 +316,14 @@ export function updateColumn(id: Column['id'], column: Partial<Column>) {
const currentColumn = deepClone(columns.value[columnIndex]);
if (currentColumn == null) return;
for (const [k, v] of Object.entries(column)) {
- currentColumn[k] = v;
+ (currentColumn[k as keyof typeof column] as any) = v;
}
newColumns[columnIndex] = currentColumn;
columns.value = newColumns;
saveCurrentDeckProfile();
}
-export function switchProfileMenu(ev: MouseEvent) {
+export function switchProfileMenu(ev: PointerEvent) {
const items: MenuItem[] = prefer.s['deck.profile'] ? [{
text: prefer.s['deck.profile'],
active: true,
diff --git a/packages/frontend/src/directives/appear.ts b/packages/frontend/src/directives/appear.ts
index 117dc397da..599f2378d1 100644
--- a/packages/frontend/src/directives/appear.ts
+++ b/packages/frontend/src/directives/appear.ts
@@ -16,7 +16,7 @@ export const appearDirective = {
const fn = binding.value;
if (fn == null) return;
- const check = throttle<IntersectionObserverCallback>(1000, (entries) => {
+ const check = throttle<IntersectionObserverCallback>(500, (entries) => {
if (entries.some(entry => entry.isIntersecting)) {
fn();
}
diff --git a/packages/frontend/src/directives/index.ts b/packages/frontend/src/directives/index.ts
index 07b756b95d..a88778efaf 100644
--- a/packages/frontend/src/directives/index.ts
+++ b/packages/frontend/src/directives/index.ts
@@ -39,7 +39,7 @@ export const directives = {
} as Record<string, Directive>;
declare module 'vue' {
- export interface ComponentCustomProperties {
+ export interface GlobalDirectives {
vUserPreview: typeof userPreviewDirective;
vGetSize: typeof getSizeDirective;
vRipple: typeof rippleDirective;
diff --git a/packages/frontend/src/drag-and-drop.ts b/packages/frontend/src/drag-and-drop.ts
index 670912241e..725e7a70b3 100644
--- a/packages/frontend/src/drag-and-drop.ts
+++ b/packages/frontend/src/drag-and-drop.ts
@@ -9,6 +9,7 @@ type DragDataMap = {
driveFiles: Misskey.entities.DriveFile[];
driveFolders: Misskey.entities.DriveFolder[];
deckColumn: string;
+ MkDraggable: { item: { id: string }; instanceId: string; group: string; };
};
// NOTE: dataTransfer の format は大文字小文字区別されないっぽいので toLowerCase が必要
diff --git a/packages/frontend/src/filters/bytes.ts b/packages/frontend/src/filters/bytes.ts
index 49b44167d4..a5b6fad3b1 100644
--- a/packages/frontend/src/filters/bytes.ts
+++ b/packages/frontend/src/filters/bytes.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-export default (v, digits = 0) => {
+export default (v: number | null, digits = 0) => {
if (v == null) return '?';
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'RB', 'QB'];
if (v === 0) return '0';
diff --git a/packages/frontend/src/filters/kmg.ts b/packages/frontend/src/filters/kmg.ts
index 9608e420f6..0aaa70d50d 100644
--- a/packages/frontend/src/filters/kmg.ts
+++ b/packages/frontend/src/filters/kmg.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-export default (v, fractionDigits = 0) => {
+export default (v: number | null, fractionDigits = 0) => {
if (v == null) return 'N/A';
if (v === 0) return '0';
const sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q'];
diff --git a/packages/frontend/src/filters/note.ts b/packages/frontend/src/filters/note.ts
index ce31021469..3fcd50c5e3 100644
--- a/packages/frontend/src/filters/note.ts
+++ b/packages/frontend/src/filters/note.ts
@@ -3,6 +3,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-export const notePage = note => {
+export const notePage = (note: { id: string }) => {
return `/notes/${note.id}`;
};
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index c9d83a4dbe..6f15826aaa 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -43,7 +43,7 @@ export async function fetchInstance(force = false): Promise<Misskey.entities.Met
});
for (const [k, v] of Object.entries(meta)) {
- instance[k] = v;
+ (instance[k as keyof typeof meta] as any) = v;
}
miLocalStorage.setItem('instance', JSON.stringify(instance));
@@ -51,9 +51,3 @@ export async function fetchInstance(force = false): Promise<Misskey.entities.Met
return instance;
}
-
-export type ClientOptions = {
- entrancePageStyle: 'classic' | 'simple';
- showTimelineForVisitor: boolean;
- showActivitiesForVisitor: boolean;
-};
diff --git a/packages/frontend/src/lib/nirax.ts b/packages/frontend/src/lib/nirax.ts
index 74dda9decd..ce77a2baa9 100644
--- a/packages/frontend/src/lib/nirax.ts
+++ b/packages/frontend/src/lib/nirax.ts
@@ -125,7 +125,7 @@ type GetSinglePathQuery<Def extends RouteDef, Path extends FlattenAllPaths<Route
? ChildPath extends FlattenAllPaths<Children>
? GetPathQuery<Children, ChildPath>
: Record<string, never>
- : never
+ : never
: never
: never
: Def['path'] extends Path
@@ -133,9 +133,9 @@ type GetSinglePathQuery<Def extends RouteDef, Path extends FlattenAllPaths<Route
? Query extends Record<string, string>
? UnwrapReadOnly<{ [Key in keyof Query]?: string; }>
: Record<string, never>
+ : Record<string, never>
: Record<string, never>
- : Record<string, never>
- >;
+>;
type GetPathQuery<Defs extends RouteDef[], Path extends FlattenAllPaths<Defs>> = GetSinglePathQuery<Defs[number], Path>;
@@ -320,11 +320,11 @@ export class Nirax<DEF extends RouteDef[]> extends EventEmitter<RouterEvents> {
if (route.query != null && queryString != null) {
const queryObject = [...new URLSearchParams(queryString).entries()]
- .reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {});
+ .reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {}) as Record<string, string>;
for (const q in route.query) {
const as = route.query[q];
- if (queryObject[q]) {
+ if (queryObject[q] != null) {
props.set(as, safeURIDecode(queryObject[q]));
}
}
diff --git a/packages/frontend/src/lib/pizzax.ts b/packages/frontend/src/lib/pizzax.ts
index 80543d10e4..0dd8a82957 100644
--- a/packages/frontend/src/lib/pizzax.ts
+++ b/packages/frontend/src/lib/pizzax.ts
@@ -7,7 +7,7 @@
// TODO: Misskeyのドメイン知識があるのでutilityなどに移動する
-import { onUnmounted, ref, watch } from 'vue';
+import { customRef, ref, watch, onScopeDispose } from 'vue';
import { BroadcastChannel } from 'broadcast-channel';
import type { Ref } from 'vue';
import { $i } from '@/i.js';
@@ -223,44 +223,43 @@ export class Pizzax<T extends StateDef> {
}
/**
- * 特定のキーの、簡易的なgetter/setterを作ります
+ * 特定のキーの、簡易的なcomputed refを作ります
* 主にvue上で設定コントロールのmodelとして使う用
*/
- // TODO: 廃止
- public makeGetterSetter<K extends keyof T, R = T[K]['default']>(
+ public model<K extends keyof T, R = T[K]['default']>(
+ key: K,
+ ): Ref<R>;
+ public model<K extends keyof T, R extends Exclude<any, T[K]['default']>>(
+ key: K,
+ getter: (v: T[K]['default']) => R,
+ setter: (v: R) => T[K]['default'],
+ ): Ref<R>;
+
+ public model<K extends keyof T, R>(
key: K,
getter?: (v: T[K]['default']) => R,
setter?: (v: R) => T[K]['default'],
- ): {
- get: () => R;
- set: (value: R) => void;
- } {
- const valueRef = ref(this.s[key]);
+ ): Ref<R> {
+ return customRef<R>((track, trigger) => {
+ const watchStop = watch(this.r[key], () => {
+ trigger();
+ });
- const stop = watch(this.r[key], val => {
- valueRef.value = val;
- });
+ onScopeDispose(() => {
+ watchStop();
+ }, true);
- // NOTE: vueコンポーネント内で呼ばれない限りは、onUnmounted は無意味なのでメモリリークする
- onUnmounted(() => {
- stop();
+ return {
+ get: () => {
+ track();
+ return (getter != null ? getter(this.s[key]) : this.s[key]) as R;
+ },
+ set: (value) => {
+ const val = setter != null ? setter(value) : value;
+ this.set(key, val as T[K]['default']);
+ },
+ };
});
-
- // TODO: VueのcustomRef使うと良い感じになるかも
- return {
- get: () => {
- if (getter) {
- return getter(valueRef.value);
- } else {
- return valueRef.value;
- }
- },
- set: (value) => {
- const val = setter ? setter(value) : value;
- this.set(key, val);
- valueRef.value = val;
- },
- };
}
// localStorage => indexedDBのマイグレーション
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index a162b3aa9e..c5b77c498a 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -6,6 +6,7 @@
import { computed, reactive } from 'vue';
import { ui } from '@@/js/config.js';
import { clearCache } from './utility/clear-cache.js';
+import type { ComputedRef } from 'vue';
import { $i } from '@/i.js';
import { miLocalStorage } from '@/local-storage.js';
import { openInstanceMenu, openToolsMenu } from '@/ui/_common_/common.js';
@@ -14,7 +15,17 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { unisonReload } from '@/utility/unison-reload.js';
-export const navbarItemDef = reactive({
+export const navbarItemDef = reactive<{
+ [key: string]: {
+ title: string;
+ icon: string;
+ show?: ComputedRef<boolean>;
+ indicated?: ComputedRef<boolean>;
+ indicateValue?: ComputedRef<string>;
+ to?: string;
+ action?: (ev: PointerEvent) => void;
+ };
+}>({
notifications: {
title: i18n.ts.notifications,
icon: 'ti ti-bell',
@@ -137,7 +148,7 @@ export const navbarItemDef = reactive({
ui: {
title: i18n.ts.switchUi,
icon: 'ti ti-devices',
- action: (ev: MouseEvent) => {
+ action: (ev) => {
os.popupMenu([{
text: i18n.ts.default,
active: ui === 'default' || ui === null,
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index aafa1c4b21..f7b59612c4 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -8,13 +8,16 @@
import { markRaw, ref, defineAsyncComponent, nextTick } from 'vue';
import { EventEmitter } from 'eventemitter3';
import * as Misskey from 'misskey-js';
-import type { Component, Ref } from 'vue';
+import type { Component, MaybeRef } from 'vue';
import type { ComponentEmit, ComponentProps as CP } from 'vue-component-type-helpers';
import type { Form, GetFormResultType } from '@/utility/form.js';
import type { MenuItem } from '@/types/menu.js';
import type { PostFormProps } from '@/types/post-form.js';
import type { UploaderFeatures } from '@/composables/use-uploader.js';
-import type { MkSelectItem, OptionValue } from '@/components/MkSelect.vue';
+import type { MkSelectItem } from '@/components/MkSelect.vue';
+import type { OptionValue } from '@/types/option-value.js';
+import type { MkDialogReturnType } from '@/components/MkDialog.vue';
+import type { OverloadToUnion } from '@/types/overload-to-union.js';
import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue';
import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -159,12 +162,34 @@ export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number {
}
// props に ref を許可するようにする
-type ComponentProps<T extends Component> = { [K in keyof CP<T>]: CP<T>[K] | Ref<CP<T>[K]> };
+type PropsWithRefs<P> = { [K in keyof P]: MaybeRef<P[K]> };
+type ComponentProps<T extends Component> = PropsWithRefs<CP<T>>;
+// 関数の引数が any[] (もっとも広義なもの) かどうかを判定し、any[] の場合は排除 (never) するヘルパー
+type FilterSpecificFunc<T> = T extends (...args: any[]) => void
+ ? (any[] extends Parameters<T> ? never : T)
+ : T;
+
+// オブジェクトの各プロパティに対して再帰的、あるいは単純に適用する型関数
+type CleanFunctions<T> = {
+ [K in keyof T]: T[K] extends (...args: any[]) => any
+ ? FilterSpecificFunc<T[K]>
+ : T[K];
+};
+
+// emitの関数群をオブジェクト型に変換する(InstanceType<Component>['$emit']はFunctionalComponent = ジェネリックコンポーネントでは使用できない)
+type ComponentEmitsObject<C extends Component, IE = OverloadToUnion<ComponentEmit<C>>> = CleanFunctions<{
+ [K in IE extends (evName: infer U, ...args: any[]) => any ? U & PropertyKey : never]: IE extends (evName: K, ...args: infer A) => infer R
+ ? (...args: A) => R
+ : (...args: any[]) => void;
+}>;
+
+// NOTE: ジェネリック型つきのコンポーネントでは、emitsの型推論がうまく働かない(型変数を取り出すことはできないため)
+// NOTE: emitsがOverloadToUnionで対応しているオーバーロードの数を超える場合は、OverloadToUnionの個数を増やせばOK
export function popup<T extends Component>(
component: T,
props: ComponentProps<T>,
- events: Partial<ComponentEmit<T>> = {},
+ events: Partial<ComponentEmitsObject<T>> = {},
): { dispose: () => void } {
markRaw(component);
@@ -192,10 +217,10 @@ export function popup<T extends Component>(
export async function popupAsyncWithDialog<T extends Component>(
componentFetching: Promise<T>,
props: ComponentProps<T>,
- events: Partial<ComponentEmit<T>> = {},
+ events: Partial<ComponentEmitsObject<T>> = {},
): Promise<{ dispose: () => void }> {
let component: T;
- let closeWaiting = () => {};
+ let closeWaiting = () => { };
const timer = window.setTimeout(() => {
closeWaiting = waiting();
@@ -291,23 +316,19 @@ export function confirm(props: {
});
}
-// TODO: const T extends ... にしたい
-// https://zenn.dev/general_link/articles/813e47b7a0eef7#const-type-parameters
-export function actions<T extends {
+type ActionsAction = {
value: string;
text: string;
- primary?: boolean,
- danger?: boolean,
-}[]>(props: {
+ primary?: boolean;
+ danger?: boolean;
+};
+
+export function actions<const T extends ActionsAction[]>(props: {
type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
title?: string;
text?: string;
actions: T;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: T[number]['value'];
-}> {
+}): Promise<MkDialogReturnType<T[number]['value']>> {
return new Promise(resolve => {
const { dispose } = popup(MkDialog, {
...props,
@@ -321,7 +342,7 @@ export function actions<T extends {
})),
}, {
done: result => {
- resolve(result ? result : { canceled: true });
+ resolve(result as MkDialogReturnType<T[number]['value']>);
},
closed: () => dispose(),
});
@@ -338,11 +359,7 @@ export function inputText(props: {
default: string;
minLength?: number;
maxLength?: number;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: string;
-}>;
+}): Promise<MkDialogReturnType<string>>;
// min lengthが指定されてたら result は null になり得ないことを保証する overload function
export function inputText(props: {
type?: 'text' | 'email' | 'password' | 'url';
@@ -353,11 +370,7 @@ export function inputText(props: {
default?: string;
minLength: number;
maxLength?: number;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: string;
-}>;
+}): Promise<MkDialogReturnType<string>>;
export function inputText(props: {
type?: 'text' | 'email' | 'password' | 'url';
title?: string;
@@ -367,11 +380,7 @@ export function inputText(props: {
default?: string | null;
minLength?: number;
maxLength?: number;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: string | null;
-}>;
+}): Promise<MkDialogReturnType<string | null>>;
export function inputText(props: {
type?: 'text' | 'email' | 'password' | 'url';
title?: string;
@@ -381,11 +390,7 @@ export function inputText(props: {
default?: string | null;
minLength?: number;
maxLength?: number;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: string | null;
-}> {
+}): Promise<MkDialogReturnType<string | null>> {
return new Promise(resolve => {
const { dispose } = popup(MkDialog, {
title: props.title,
@@ -400,7 +405,7 @@ export function inputText(props: {
},
}, {
done: result => {
- resolve(result ? result : { canceled: true });
+ resolve(result as MkDialogReturnType<string | null>);
},
closed: () => dispose(),
});
@@ -414,33 +419,21 @@ export function inputNumber(props: {
placeholder?: string | null;
autocomplete?: string;
default: number;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: number;
-}>;
+}): Promise<MkDialogReturnType<number>>;
export function inputNumber(props: {
title?: string;
text?: string;
placeholder?: string | null;
autocomplete?: string;
default?: number | null;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: number | null;
-}>;
+}): Promise<MkDialogReturnType<number | null>>;
export function inputNumber(props: {
title?: string;
text?: string;
placeholder?: string | null;
autocomplete?: string;
default?: number | null;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: number | null;
-}> {
+}): Promise<MkDialogReturnType<number | null>> {
return new Promise(resolve => {
const { dispose } = popup(MkDialog, {
title: props.title,
@@ -453,7 +446,7 @@ export function inputNumber(props: {
},
}, {
done: result => {
- resolve(result ? result : { canceled: true });
+ resolve(result as MkDialogReturnType<number | null>);
},
closed: () => dispose(),
});
@@ -465,11 +458,7 @@ export function inputDatetime(props: {
text?: string;
placeholder?: string | null;
default?: string | null;
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: Date;
-}> {
+}): Promise<MkDialogReturnType<Date>> {
return new Promise(resolve => {
const { dispose } = popup(MkDialog, {
title: props.title,
@@ -481,7 +470,7 @@ export function inputDatetime(props: {
},
}, {
done: result => {
- resolve(result != null && result.result != null ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true });
+ resolve(result != null && typeof result.result === 'string' ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true });
},
closed: () => dispose(),
});
@@ -508,11 +497,7 @@ export function select<C extends OptionValue, D extends C | null = null>(props:
text?: string;
default?: D;
items: (MkSelectItem<C> | undefined)[];
-}): Promise<{
- canceled: true; result: undefined;
-} | {
- canceled: false; result: Exclude<D, undefined> extends null ? C | null : C;
-}> {
+}): Promise<MkDialogReturnType<Exclude<D, undefined> extends null ? C | null : C>> {
return new Promise(resolve => {
const { dispose } = popup(MkDialog, {
title: props.title,
@@ -523,7 +508,7 @@ export function select<C extends OptionValue, D extends C | null = null>(props:
},
}, {
done: result => {
- resolve(result ? result : { canceled: true });
+ resolve(result as MkDialogReturnType<Exclude<D, undefined> extends null ? C | null : C>);
},
closed: () => dispose(),
});
@@ -582,7 +567,7 @@ export function form<F extends Form>(title: string, f: F): Promise<{ canceled: t
return new Promise(resolve => {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, {
done: result => {
- resolve(result);
+ resolve(result as { canceled?: false, result: GetFormResultType<F> });
},
closed: () => dispose(),
});
@@ -634,16 +619,16 @@ export async function pickEmoji(anchorElement: HTMLElement, opts: ComponentProps
});
}
-export async function cropImageFile(imageFile: File | Blob, options: {
+export async function cropImageFile<F extends File | Blob>(imageFile: F, options: {
aspectRatio: number | null;
-}): Promise<File> {
+}): Promise<F> {
return new Promise(resolve => {
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), {
imageFile: imageFile,
aspectRatio: options.aspectRatio,
}, {
ok: x => {
- resolve(x);
+ resolve(x as F);
},
closed: () => dispose(),
});
@@ -654,6 +639,7 @@ export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElemen
align?: string;
width?: number;
onClosing?: () => void;
+ onClosed?: () => void;
}): Promise<void> {
if (!(anchorElement instanceof HTMLElement)) {
anchorElement = null;
@@ -672,6 +658,7 @@ export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElemen
resolve();
dispose();
returnFocusTo = null;
+ options?.onClosed?.();
},
closing: () => {
options?.onClosing?.();
@@ -680,7 +667,7 @@ export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElemen
}));
}
-export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
+export function contextMenu(items: MenuItem[], ev: PointerEvent): Promise<void> {
if (
prefer.s.contextMenu === 'native' ||
(prefer.s.contextMenu === 'appWithShift' && !ev.shiftKey)
@@ -709,8 +696,8 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
}));
}
-export function post(props: PostFormProps = {}): Promise<void> {
- pleaseLogin({
+export async function post(props: PostFormProps = {}): Promise<void> {
+ const isLoggedIn = await pleaseLogin({
openOnRemote: (props.initialText || props.initialNote ? {
type: 'share',
params: {
@@ -720,6 +707,7 @@ export function post(props: PostFormProps = {}): Promise<void> {
},
} : undefined),
});
+ if (!isLoggedIn) return;
showMovedDialog();
return new Promise(resolve => {
@@ -779,7 +767,7 @@ export function chooseFileFromPc(
});
}
-export function launchUploader(
+export async function launchUploader(
files: File[],
options?: {
folderId?: string | null;
@@ -787,9 +775,10 @@ export function launchUploader(
features?: UploaderFeatures;
},
): Promise<Misskey.entities.DriveFile[]> {
- return new Promise(async (res, rej) => {
+ return new Promise((res, rej) => {
if (files.length === 0) return rej();
- const { dispose } = await popupAsyncWithDialog(import('@/components/MkUploaderDialog.vue').then(x => x.default), {
+ let dispose: () => void;
+ popupAsyncWithDialog(import('@/components/MkUploaderDialog.vue').then(x => x.default), {
files: markRaw(files),
folderId: options?.folderId,
multiple: options?.multiple,
@@ -800,7 +789,7 @@ export function launchUploader(
res(driveFiles);
},
closed: () => dispose(),
- });
+ }).then(d => dispose = d.dispose, rej);
});
}
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index bbfb9a3b7c..c109000108 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -97,7 +97,7 @@ const paginator = markRaw(new Paginator('federation/instances', {
})),
}));
-function getStatus(instance) {
+function getStatus(instance: Misskey.entities.FederationInstance) {
if (instance.isSuspended) return 'Suspended';
if (instance.isBlocked) return 'Blocked';
if (instance.isSilenced) return 'Silenced';
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 22e377c75d..b084eb5ab2 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -105,7 +105,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts._role.policies }}</template>
<div class="_gaps">
<div v-for="policy in Object.keys(info.policies)" :key="policy">
- {{ policy }} ... {{ info.policies[policy] }}
+ {{ policy }} ... {{ info.policies[policy as keyof typeof info.policies] }}
</div>
</div>
</MkFolder>
@@ -209,6 +209,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, defineAsyncComponent, watch, ref, markRaw } from 'vue';
import * as Misskey from 'misskey-js';
import { url } from '@@/js/config.js';
+import type { ChartSrc } from '@/components/MkChart.vue';
import MkChart from '@/components/MkChart.vue';
import MkObjectView from '@/components/MkObjectView.vue';
import MkTextarea from '@/components/MkTextarea.vue';
@@ -231,7 +232,6 @@ import { ensureSignin, iAmAdmin, iAmModerator } from '@/i.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
import MkPagination from '@/components/MkPagination.vue';
import { Paginator } from '@/utility/paginator.js';
-import type { ChartSrc } from '@/components/MkChart.vue';
const $i = ensureSignin();
@@ -251,7 +251,7 @@ const {
} = useMkSelect({
items: [
{ label: i18n.ts.notes, value: 'per-user-notes' },
-],
+ ],
initialValue: 'per-user-notes',
});
const user = ref(result.user);
@@ -344,7 +344,7 @@ async function resetPassword() {
}
}
-async function toggleSuspend(v) {
+async function toggleSuspend(v: boolean) {
const confirm = await os.confirm({
type: 'warning',
text: v ? i18n.ts.suspendConfirm : i18n.ts.unsuspendConfirm,
@@ -475,7 +475,7 @@ async function assignRole() {
refreshUser();
}
-async function unassignRole(role: typeof info.value.roles[number], ev: MouseEvent) {
+async function unassignRole(role: typeof info.value.roles[number], ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.unassign,
icon: 'ti ti-x',
@@ -503,7 +503,7 @@ async function createAnnouncement() {
});
}
-async function editAnnouncement(announcement) {
+async function editAnnouncement(announcement: Misskey.entities.AdminAnnouncementsListResponse[number]) {
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkUserAnnouncementEditDialog.vue').then(x => x.default), {
user: user.value,
announcement,
diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
index 9d9db9158d..384282262d 100644
--- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue
+++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
@@ -6,9 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps">
<div :class="$style.header">
- <MkSelect v-model="type" :items="typeDef" :class="$style.typeSelect">
+ <MkSelect v-model="typeModelForMkSelect" :items="typeDef" :class="$style.typeSelect">
</MkSelect>
- <button v-if="draggable" class="drag-handle _button" :class="$style.dragHandle">
+ <button v-if="draggable" class="_button" :class="$style.dragHandle" :draggable="true" @dragstart.stop="dragStartCallback">
<i class="ti ti-menu-2"></i>
</button>
<button v-if="draggable" class="_button" :class="$style.remove" @click="removeSelf">
@@ -16,55 +16,69 @@ SPDX-License-Identifier: AGPL-3.0-only
</button>
</div>
- <div v-if="type === 'and' || type === 'or'" class="_gaps">
- <Sortable v-model="v.values" tag="div" class="_gaps" itemKey="id" handle=".drag-handle" :group="{ name: 'roleFormula' }" :animation="150" :swapThreshold="0.5">
- <template #item="{element}">
+ <div v-if="v.type === 'and' || v.type === 'or'" class="_gaps">
+ <MkDraggable
+ v-model="v.values"
+ direction="vertical"
+ withGaps
+ canNest
+ manualDragStart
+ group="roleFormula"
+ >
+ <template #default="{ item, dragStart }">
<div :class="$style.item">
- <!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->
- <RolesEditorFormula :modelValue="element" draggable @update:modelValue="updated => valuesItemUpdated(updated)" @remove="removeItem(element)"/>
+ <!-- divが無いとエラーになる -->
+ <RolesEditorFormula
+ :modelValue="item"
+ :dragStartCallback="dragStart"
+ draggable
+ @update:modelValue="updated => childValuesItemUpdated(updated)"
+ @remove="removeChildItem(item.id)"
+ />
</div>
</template>
- </Sortable>
- <MkButton rounded style="margin: 0 auto;" @click="addValue"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ </MkDraggable>
+ <MkButton rounded style="margin: 0 auto;" @click="addChildValue"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
</div>
- <div v-else-if="type === 'not'" :class="$style.item">
+ <div v-else-if="v.type === 'not'" :class="$style.item">
<RolesEditorFormula v-model="v.value"/>
</div>
- <MkInput v-else-if="type === 'createdLessThan' || type === 'createdMoreThan'" v-model="v.sec" type="number">
+ <MkInput v-else-if="v.type === 'createdLessThan' || v.type === 'createdMoreThan'" v-model="v.sec" type="number">
<template #suffix>sec</template>
</MkInput>
- <MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq', 'notesLessThanOrEq', 'notesMoreThanOrEq'].includes(type)" v-model="v.value" type="number">
+ <MkInput v-else-if="v.type === 'followersLessThanOrEq' || v.type === 'followersMoreThanOrEq' || v.type === 'followingLessThanOrEq' || v.type === 'followingMoreThanOrEq' || v.type === 'notesLessThanOrEq' || v.type === 'notesMoreThanOrEq'" v-model="v.value" type="number">
</MkInput>
- <MkSelect v-else-if="type === 'roleAssignedTo'" v-model="v.roleId" :items="assignedToDef">
+ <MkSelect v-else-if="v.type === 'roleAssignedTo'" v-model="v.roleId" :items="assignedToDef">
</MkSelect>
</div>
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, watch } from 'vue';
+import { computed, ref, watch } from 'vue';
+import * as Misskey from 'misskey-js';
+import type { GetMkSelectValueTypesFromDef, MkSelectItem } from '@/components/MkSelect.vue';
import { genId } from '@/utility/id.js';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
-import type { GetMkSelectValueTypesFromDef, MkSelectItem } from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
import { i18n } from '@/i18n.js';
import { deepClone } from '@/utility/clone.js';
import { rolesCache } from '@/cache.js';
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-
const emit = defineEmits<{
- (ev: 'update:modelValue', value: any): void;
+ (ev: 'update:modelValue', value: Misskey.entities.Role['condFormula']): void;
(ev: 'remove'): void;
}>();
const props = defineProps<{
- modelValue: any;
+ modelValue: Misskey.entities.Role['condFormula'];
draggable?: boolean;
+ dragStartCallback?: (ev: DragEvent) => void;
}>();
const v = ref(deepClone(props.modelValue));
@@ -102,38 +116,51 @@ const typeDef = [
{ label: i18n.ts._role._condition.not, value: 'not' },
] as const satisfies MkSelectItem[];
-const type = computed<GetMkSelectValueTypesFromDef<typeof typeDef>>({
+type KeyOfUnion<T> = T extends T ? keyof T : never;
+
+type DistributiveOmit<T, K extends KeyOfUnion<T>> = T extends T
+ ? Omit<T, K>
+ : never;
+
+const typeModelForMkSelect = computed<GetMkSelectValueTypesFromDef<typeof typeDef>>({
get: () => v.value.type,
set: (t) => {
- if (t === 'and') v.value.values = [];
- if (t === 'or') v.value.values = [];
- if (t === 'not') v.value.value = { id: genId(), type: 'isRemote' };
- if (t === 'roleAssignedTo') v.value.roleId = '';
- if (t === 'createdLessThan') v.value.sec = 86400;
- if (t === 'createdMoreThan') v.value.sec = 86400;
- if (t === 'followersLessThanOrEq') v.value.value = 10;
- if (t === 'followersMoreThanOrEq') v.value.value = 10;
- if (t === 'followingLessThanOrEq') v.value.value = 10;
- if (t === 'followingMoreThanOrEq') v.value.value = 10;
- if (t === 'notesLessThanOrEq') v.value.value = 10;
- if (t === 'notesMoreThanOrEq') v.value.value = 10;
- v.value.type = t;
+ let newValue: DistributiveOmit<Misskey.entities.Role['condFormula'], 'id'>;
+ switch (t) {
+ case 'and': newValue = { type: 'and', values: [] }; break;
+ case 'or': newValue = { type: 'or', values: [] }; break;
+ case 'not': newValue = { type: 'not', value: { id: genId(), type: 'isRemote' } }; break;
+ case 'roleAssignedTo': newValue = { type: 'roleAssignedTo', roleId: '' }; break;
+ case 'createdLessThan': newValue = { type: 'createdLessThan', sec: 86400 }; break;
+ case 'createdMoreThan': newValue = { type: 'createdMoreThan', sec: 86400 }; break;
+ case 'followersLessThanOrEq': newValue = { type: 'followersLessThanOrEq', value: 10 }; break;
+ case 'followersMoreThanOrEq': newValue = { type: 'followersMoreThanOrEq', value: 10 }; break;
+ case 'followingLessThanOrEq': newValue = { type: 'followingLessThanOrEq', value: 10 }; break;
+ case 'followingMoreThanOrEq': newValue = { type: 'followingMoreThanOrEq', value: 10 }; break;
+ case 'notesLessThanOrEq': newValue = { type: 'notesLessThanOrEq', value: 10 }; break;
+ case 'notesMoreThanOrEq': newValue = { type: 'notesMoreThanOrEq', value: 10 }; break;
+ default: newValue = { type: t }; break;
+ }
+ v.value = { id: v.value.id, ...newValue };
},
});
const assignedToDef = computed(() => roles.filter(r => r.target === 'manual').map(r => ({ label: r.name, value: r.id })) satisfies MkSelectItem[]);
-function addValue() {
+function addChildValue() {
+ if (v.value.type !== 'and' && v.value.type !== 'or') return;
v.value.values.push({ id: genId(), type: 'isRemote' });
}
-function valuesItemUpdated(item) {
+function childValuesItemUpdated(item: Misskey.entities.Role['condFormula']) {
+ if (v.value.type !== 'and' && v.value.type !== 'or') return;
const i = v.value.values.findIndex(_item => _item.id === item.id);
v.value.values[i] = item;
}
-function removeItem(item) {
- v.value.values = v.value.values.filter(_item => _item.id !== item.id);
+function removeChildItem(itemId: string) {
+ if (v.value.type !== 'and' && v.value.type !== 'or') return;
+ v.value.values = v.value.values.filter(_item => _item.id !== itemId);
}
function removeSelf() {
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
index 7c3f736506..591d8fa736 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.editor.vue
@@ -37,8 +37,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts._abuseReport._notificationRecipient.notifiedWebhook }}</template>
</MkSelect>
<MkButton rounded :class="$style.systemWebhookEditButton" @click="onEditSystemWebhookClicked">
- <span v-if="systemWebhookId === null" class="ti ti-plus" style="line-height: normal"/>
- <span v-else class="ti ti-settings" style="line-height: normal"/>
+ <span v-if="systemWebhookId === null" class="ti ti-plus" style="line-height: normal"></span>
+ <span v-else class="ti ti-settings" style="line-height: normal"></span>
</MkButton>
</div>
</div>
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
index 36d586bd23..ba5830c2e8 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root" class="_panel _gaps_s">
- <div :class="$style.rightDivider" style="width: 80px;"><span :class="`ti ${methodIcon}`"/> {{ methodName }}</div>
+ <div :class="$style.rightDivider" style="width: 80px;"><span :class="`ti ${methodIcon}`"></span> {{ methodName }}</div>
<div :class="$style.rightDivider" style="flex: 0.5">{{ entity.name }}</div>
<div :class="$style.rightDivider" style="flex: 1">
<div v-if="method === 'email' && user">
@@ -19,10 +19,10 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="$style.recipientButtons" style="margin-left: auto">
<button :class="$style.recipientButton" @click="onEditButtonClicked()">
- <span class="ti ti-settings"/>
+ <span class="ti ti-settings"></span>
</button>
<button :class="$style.recipientButton" @click="onDeleteButtonClicked()">
- <span class="ti ti-trash"/>
+ <span class="ti ti-trash"></span>
</button>
</div>
</div>
diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
index 893bd8d6d3..a9cf372c0e 100644
--- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
+++ b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.root" class="_gaps_m">
<div :class="$style.addButton">
<MkButton primary @click="onAddButtonClicked">
- <span class="ti ti-plus"/> {{ i18n.ts._abuseReport._notificationRecipient.createRecipient }}
+ <span class="ti ti-plus"></span> {{ i18n.ts._abuseReport._notificationRecipient.createRecipient }}
</MkButton>
</div>
<div :class="$style.subMenus" class="_gaps_s">
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index 76bf20b409..2d204987cb 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -105,7 +105,7 @@ const paginator = markRaw(new Paginator('admin/abuse-user-reports', {
})),
}));
-function resolved(reportId) {
+function resolved(reportId: string) {
paginator.removeItem(reportId);
}
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 94940a84ae..0efd1a2e28 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -22,22 +22,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.imageUrl }}</template>
</MkInput>
- <MkRadios v-model="ad.place">
+ <MkRadios
+ v-model="ad.place"
+ :options="[
+ { value: 'square' },
+ { value: 'horizontal' },
+ { value: 'horizontal-big' },
+ ]"
+ >
<template #label>Form</template>
- <option value="square">square</option>
- <option value="horizontal">horizontal</option>
- <option value="horizontal-big">horizontal-big</option>
</MkRadios>
- <!--
- <div style="margin: 32px 0;">
- {{ i18n.ts.priority }}
- <MkRadio v-model="ad.priority" value="high">{{ i18n.ts.high }}</MkRadio>
- <MkRadio v-model="ad.priority" value="middle">{{ i18n.ts.middle }}</MkRadio>
- <MkRadio v-model="ad.priority" value="low">{{ i18n.ts.low }}</MkRadio>
- </div>
- -->
-
<FormSplit>
<MkInput v-model="ad.ratio" type="number">
<template #label>{{ i18n.ts.ratio }}</template>
@@ -109,7 +104,11 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { useMkSelect } from '@/composables/use-mkselect.js';
-const ads = ref<Misskey.entities.Ad[]>([]);
+type Ad = Misskey.entities.Ad & {
+ place: 'square' | 'horizontal' | 'horizontal-big';
+};
+
+const ads = ref<Ad[]>([]);
// ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
const localTime = new Date();
@@ -136,7 +135,7 @@ misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => {
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
return {
- ...r,
+ ...(r as Ad),
expiresAt: exdate.toISOString().slice(0, 16),
startsAt: stdate.toISOString().slice(0, 16),
};
@@ -239,7 +238,7 @@ function more() {
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
return {
- ...r,
+ ...(r as Ad),
expiresAt: exdate.toISOString().slice(0, 16),
startsAt: stdate.toISOString().slice(0, 16),
};
@@ -256,7 +255,7 @@ function refresh() {
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
return {
- ...r,
+ ...(r as Ad),
expiresAt: exdate.toISOString().slice(0, 16),
startsAt: stdate.toISOString().slice(0, 16),
};
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index b90a724b17..87fc6e70f4 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_spacer" style="--MI_SPACER-w: 900px;">
<div class="_gaps">
<MkInfo>{{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }}</MkInfo>
- <MkInfo v-if="announcements.length > 5" warn>{{ i18n.ts._announcement.tooManyActiveAnnouncementDescription }}</MkInfo>
+ <MkInfo v-if="announcementsStatus === 'active' && announcements.length > 5" warn>{{ i18n.ts._announcement.tooManyActiveAnnouncementDescription }}</MkInfo>
<MkSelect v-model="announcementsStatus" :items="announcementsStatusDef">
<template #label>{{ i18n.ts.filter }}</template>
@@ -45,18 +45,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-model="announcement.imageUrl" type="url">
<template #label>{{ i18n.ts.imageUrl }}</template>
</MkInput>
- <MkRadios v-model="announcement.icon">
+ <MkRadios
+ v-model="announcement.icon"
+ :options="[
+ { value: 'info', icon: 'ti ti-info-circle' },
+ { value: 'warning', icon: 'ti ti-alert-triangle', iconStyle: 'color: var(--MI_THEME-warn);' },
+ { value: 'error', icon: 'ti ti-circle-x', iconStyle: 'color: var(--MI_THEME-error);' },
+ { value: 'success', icon: 'ti ti-check', iconStyle: 'color: var(--MI_THEME-success);' },
+ ]"
+ >
<template #label>{{ i18n.ts.icon }}</template>
- <option value="info"><i class="ti ti-info-circle"></i></option>
- <option value="warning"><i class="ti ti-alert-triangle" style="color: var(--MI_THEME-warn);"></i></option>
- <option value="error"><i class="ti ti-circle-x" style="color: var(--MI_THEME-error);"></i></option>
- <option value="success"><i class="ti ti-check" style="color: var(--MI_THEME-success);"></i></option>
</MkRadios>
- <MkRadios v-model="announcement.display">
+ <MkRadios
+ v-model="announcement.display"
+ :options="[
+ { value: 'normal', label: i18n.ts.normal },
+ { value: 'banner', label: i18n.ts.banner },
+ { value: 'dialog', label: i18n.ts.dialog },
+ ]"
+ >
<template #label>{{ i18n.ts.display }}</template>
- <option value="normal">{{ i18n.ts.normal }}</option>
- <option value="banner">{{ i18n.ts.banner }}</option>
- <option value="dialog">{{ i18n.ts.dialog }}</option>
</MkRadios>
<MkInfo v-if="announcement.display === 'dialog'" warn>{{ i18n.ts._announcement.dialogAnnouncementUxWarn }}</MkInfo>
<MkSwitch v-model="announcement.forExistingUsers" :helpText="i18n.ts._announcement.forExistingUsersDescription">
@@ -83,6 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed, watch } from 'vue';
+import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
@@ -112,7 +121,12 @@ const {
const loading = ref(true);
const loadingMore = ref(false);
-const announcements = ref<any[]>([]);
+const announcements = ref<(Omit<Misskey.entities.AdminAnnouncementsListResponse[number], 'id' | 'createdAt' | 'updatedAt' | 'reads' | 'isActive'> & {
+ id: string | null;
+ _id?: string;
+ isActive?: Misskey.entities.AdminAnnouncementsListResponse[number]['isActive'];
+ reads?: Misskey.entities.AdminAnnouncementsListResponse[number]['reads'];
+})[]>([]);
watch(announcementsStatus, (to) => {
loading.value = true;
@@ -136,42 +150,55 @@ function add() {
forExistingUsers: false,
silence: false,
needConfirmationToRead: false,
+ userId: null,
});
}
-function del(announcement) {
- os.confirm({
+async function del(announcement: (typeof announcements)['value'][number]) {
+ if (announcement.id == null) return;
+ const { canceled } = await os.confirm({
type: 'warning',
text: i18n.tsx.deleteAreYouSure({ x: announcement.title }),
- }).then(({ canceled }) => {
- if (canceled) return;
- announcements.value = announcements.value.filter(x => x !== announcement);
- misskeyApi('admin/announcements/delete', announcement);
+ });
+ if (canceled) return;
+ announcements.value = announcements.value.filter(x => x !== announcement);
+ misskeyApi('admin/announcements/delete', {
+ id: announcement.id,
});
}
-async function archive(announcement) {
+async function archive(announcement: (typeof announcements)['value'][number]) {
+ if (announcement.id == null) return;
+ const { _id, ...data } = announcement; // _idを消す
await os.apiWithDialog('admin/announcements/update', {
- ...announcement,
+ ...data,
+ id: announcement.id, // TSを黙らすため
isActive: false,
});
refresh();
}
-async function unarchive(announcement) {
+async function unarchive(announcement: (typeof announcements)['value'][number]) {
+ if (announcement.id == null) return;
+ const { _id, ...data } = announcement; // _idを消す
await os.apiWithDialog('admin/announcements/update', {
- ...announcement,
+ ...data,
+ id: announcement.id, // TSを黙らすため
isActive: true,
});
refresh();
}
-async function save(announcement) {
+async function save(announcement: (typeof announcements)['value'][number]) {
+ const { _id, ...data } = announcement; // _idを消す
if (announcement.id == null) {
- await os.apiWithDialog('admin/announcements/create', announcement);
+ await os.apiWithDialog('admin/announcements/create', data);
refresh();
} else {
- os.apiWithDialog('admin/announcements/update', announcement);
+ os.apiWithDialog('admin/announcements/update', {
+ ...data,
+ id: announcement.id, // TSを黙らすため
+ });
}
}
@@ -179,7 +206,7 @@ function more() {
loadingMore.value = true;
misskeyApi('admin/announcements/list', {
status: announcementsStatus.value,
- untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id,
+ untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id!,
}).then(announcementResponse => {
announcements.value = announcements.value.concat(announcementResponse);
loadingMore.value = false;
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index 7ed280358a..481969e1a3 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -19,13 +19,17 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div class="_gaps_m">
- <MkRadios v-model="botProtectionForm.state.provider">
- <option value="none">{{ i18n.ts.none }} ({{ i18n.ts.notRecommended }})</option>
- <option value="hcaptcha">hCaptcha</option>
- <option value="mcaptcha">mCaptcha</option>
- <option value="recaptcha">reCAPTCHA</option>
- <option value="turnstile">Turnstile</option>
- <option value="testcaptcha">testCaptcha</option>
+ <MkRadios
+ v-model="botProtectionForm.state.provider"
+ :options="[
+ { value: 'none', label: `${i18n.ts.none} (${i18n.ts.notRecommended})` },
+ { value: 'hcaptcha', label: 'hCaptcha' },
+ { value: 'mcaptcha', label: 'mCaptcha' },
+ { value: 'recaptcha', label: 'reCAPTCHA' },
+ { value: 'turnstile', label: 'Turnstile' },
+ { value: 'testcaptcha', label: 'testCaptcha' },
+ ]"
+ >
</MkRadios>
<template v-if="botProtectionForm.state.provider === 'hcaptcha'">
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index e5e0f087e1..016d1b6a89 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -9,10 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker path="/admin/branding" :label="i18n.ts.branding" :keywords="['branding']" icon="ti ti-paint">
<div class="_gaps_m">
<SearchMarker :keywords="['entrance', 'welcome', 'landing', 'front', 'home', 'page', 'style']">
- <MkRadios v-model="entrancePageStyle">
+ <MkRadios
+ v-model="entrancePageStyle"
+ :options="[
+ { value: 'classic' },
+ { value: 'simple' },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts._serverSettings.entrancePageStyle }}</SearchLabel></template>
- <option value="classic">Classic</option>
- <option value="simple">Simple</option>
</MkRadios>
</SearchMarker>
@@ -151,8 +155,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed } from 'vue';
import JSON5 from 'json5';
+import * as Misskey from 'misskey-js';
import { host } from '@@/js/config.js';
-import type { ClientOptions } from '@/instance.js';
import MkInput from '@/components/MkInput.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import * as os from '@/os.js';
@@ -168,11 +172,11 @@ import MkSwitch from '@/components/MkSwitch.vue';
const meta = await misskeyApi('admin/meta');
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-const entrancePageStyle = ref<ClientOptions['entrancePageStyle']>(meta.clientOptions.entrancePageStyle ?? 'classic');
+const entrancePageStyle = ref<Misskey.entities.MetaClientOptions['entrancePageStyle']>(meta.clientOptions.entrancePageStyle ?? 'classic');
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-const showTimelineForVisitor = ref<ClientOptions['showTimelineForVisitor']>(meta.clientOptions.showTimelineForVisitor ?? true);
+const showTimelineForVisitor = ref<Misskey.entities.MetaClientOptions['showTimelineForVisitor']>(meta.clientOptions.showTimelineForVisitor ?? true);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
-const showActivitiesForVisitor = ref<ClientOptions['showActivitiesForVisitor']>(meta.clientOptions.showActivitiesForVisitor ?? true);
+const showActivitiesForVisitor = ref<Misskey.entities.MetaClientOptions['showActivitiesForVisitor']>(meta.clientOptions.showActivitiesForVisitor ?? true);
const iconUrl = ref(meta.iconUrl);
const app192IconUrl = ref(meta.app192IconUrl);
@@ -191,11 +195,11 @@ const manifestJsonOverride = ref(meta.manifestJsonOverride === '' ? '{}' : JSON.
function save() {
os.apiWithDialog('admin/update-meta', {
- clientOptions: ({
+ clientOptions: {
entrancePageStyle: entrancePageStyle.value,
showTimelineForVisitor: showTimelineForVisitor.value,
showActivitiesForVisitor: showActivitiesForVisitor.value,
- } as ClientOptions) as any,
+ },
iconUrl: iconUrl.value,
app192IconUrl: app192IconUrl.value,
app512IconUrl: app512IconUrl.value,
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
index 250abeebe2..6f58ab9857 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.local.list.vue
@@ -51,6 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts">
import type { SortOrder } from '@/components/MkSortOrderEditor.define.js';
import type { GridSortOrderKey } from './custom-emojis-manager.impl.js';
+import type { PageHeaderItem } from '@/types/page-header.js';
export type EmojiSearchQuery = {
name: string | null;
@@ -250,7 +251,7 @@ function setupGrid(): GridSetting {
icon: 'ti ti-trash',
action: () => {
removeDataFromGrid(context, (cell) => {
- gridItems.value[cell.row.index][cell.column.setting.bindTo] = undefined;
+ (gridItems.value[cell.row.index] as any)[cell.column.setting.bindTo] = undefined;
});
},
},
@@ -454,7 +455,7 @@ function onGridCellValidation(event: GridCellValidationEvent) {
function onGridCellValueChange(event: GridCellValueChangeEvent) {
const { row, column, newValue } = event;
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
- gridItems.value[row.index][column.setting.bindTo] = newValue;
+ (gridItems.value[row.index] as any)[column.setting.bindTo] = newValue;
}
}
@@ -525,7 +526,7 @@ const headerPageMetadata = computed(() => ({
icon: 'ti ti-icons',
}));
-const headerActions = computed(() => [{
+const headerActions = computed<PageHeaderItem[]>(() => [{
icon: 'ti ti-search',
text: i18n.ts.search,
handler: async () => {
@@ -552,7 +553,7 @@ const headerActions = computed(() => [{
}, {
icon: 'ti ti-list-numbers',
text: i18n.ts._customEmojisManager._gridCommon.searchLimit,
- handler: (ev: MouseEvent) => {
+ handler: (ev) => {
async function changeSearchLimit(to: number) {
if (updatedItemsCount.value > 0) {
const { canceled } = await os.confirm({
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue
index c343d88eb1..7ccb166481 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.register.vue
@@ -58,7 +58,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script setup lang="ts">
-/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as Misskey from 'misskey-js';
import { computed, onMounted, ref, useCssModule } from 'vue';
import type { RequestLogItem } from '@/pages/admin/custom-emojis-manager.impl.js';
@@ -339,7 +338,7 @@ function onGridCellValidation(event: GridCellValidationEvent) {
function onGridCellValueChange(event: GridCellValueChangeEvent) {
const { row, column, newValue } = event;
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
- gridItems.value[row.index][column.setting.bindTo] = newValue;
+ (gridItems.value[row.index] as any)[column.setting.bindTo] = newValue;
}
}
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
index 6317fc0b47..d5bfdffe34 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
@@ -306,7 +306,7 @@ function onGridEvent(event: GridEvent) {
function onGridCellValueChange(event: GridCellValueChangeEvent) {
const { row, column, newValue } = event;
if (gridItems.value.length > row.index && column.setting.bindTo in gridItems.value[row.index]) {
- gridItems.value[row.index][column.setting.bindTo] = newValue;
+ (gridItems.value[row.index] as any)[column.setting.bindTo] = newValue;
}
}
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager2.vue b/packages/frontend/src/pages/admin/custom-emojis-manager2.vue
index 14773d7f04..c947dc3256 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager2.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager2.vue
@@ -6,8 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<PageWithHeader v-model:tab="headerTab" :tabs="headerTabs">
<XGridLocalComponent v-if="headerTab === 'local'" :class="$style.local"/>
- <XGridRemoteComponent v-else-if="headerTab === 'remote'" :class="$style.remote"/>
- <XRegisterComponent v-else-if="headerTab === 'register'" :class="$style.register"/>
+ <XGridRemoteComponent v-else-if="headerTab === 'remote'"/>
+ <XRegisterComponent v-else-if="headerTab === 'register'"/>
</PageWithHeader>
</template>
diff --git a/packages/frontend/src/pages/admin/federation-job-queue.chart.chart.vue b/packages/frontend/src/pages/admin/federation-job-queue.chart.chart.vue
index 420219c22c..04de781a28 100644
--- a/packages/frontend/src/pages/admin/federation-job-queue.chart.chart.vue
+++ b/packages/frontend/src/pages/admin/federation-job-queue.chart.chart.vue
@@ -28,7 +28,7 @@ const { handler: externalTooltipHandler } = useChartTooltip();
let chartInstance: Chart | null = null;
-function setData(values) {
+function setData(values: number[]) {
if (chartInstance == null || chartInstance.data.labels == null) return;
for (const value of values) {
chartInstance.data.labels.push('');
@@ -41,7 +41,7 @@ function setData(values) {
chartInstance.update();
}
-function pushData(value) {
+function pushData(value: number) {
if (chartInstance == null || chartInstance.data.labels == null) return;
chartInstance.data.labels.push('');
chartInstance.data.datasets[0].data.push(value);
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 94994dc94c..b3a929faf4 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -294,7 +294,7 @@ function invite() {
});
}
-function adminLookup(ev: MouseEvent) {
+function adminLookup(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.user,
icon: 'ti ti-user',
diff --git a/packages/frontend/src/pages/admin/job-queue.vue b/packages/frontend/src/pages/admin/job-queue.vue
index b18049cb11..97b6c2bc67 100644
--- a/packages/frontend/src/pages/admin/job-queue.vue
+++ b/packages/frontend/src/pages/admin/job-queue.vue
@@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>
<MkTabs
v-model:tab="jobState"
- :class="$style.jobsTabs" :tabs="[{
+ :tabs="[{
key: 'all',
title: 'All',
icon: 'ti ti-code-asterisk',
@@ -359,8 +359,4 @@ definePage(() => ({
font-size: 85%;
margin: 6px 0;
}
-
-.jobsTabs {
-
-}
</style>
diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue
index 32a5a6976e..9854ca7fc6 100644
--- a/packages/frontend/src/pages/admin/overview.active-users.vue
+++ b/packages/frontend/src/pages/admin/overview.active-users.vue
@@ -47,7 +47,7 @@ async function renderChart() {
return new Date(y, m, d - ago);
};
- const format = (arr) => {
+ const format = (arr: number[]) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index 2c550bd9c3..90799647ff 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -104,7 +104,7 @@ const filesPagination = {
noPaging: true,
};
-function onInstanceClick(i) {
+function onInstanceClick(i: Misskey.entities.FederationInstance) {
os.pageWindow(`/instance-info/${i.host}`);
}
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index b24b640527..e806f68162 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -21,8 +21,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, ref } from 'vue';
import * as Misskey from 'misskey-js';
-import { genId } from '@/utility/id.js';
import XEditor from './roles.editor.vue';
+import { genId } from '@/utility/id.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
@@ -37,8 +37,13 @@ const props = defineProps<{
id?: string;
}>();
+type RoleLike = Pick<Misskey.entities.Role, 'name' | 'description' | 'isAdministrator' | 'isModerator' | 'color' | 'iconUrl' | 'target' | 'isPublic' | 'isExplorable' | 'asBadge' | 'canEditMembersByModerator' | 'displayOrder' | 'preserveAssignmentOnMoveAccount'> & {
+ condFormula: any;
+ policies: any;
+};
+
const role = ref<Misskey.entities.Role | null>(null);
-const data = ref<any>(null);
+const data = ref<RoleLike | null>(null);
if (props.id) {
role.value = await misskeyApi('admin/roles/show', {
@@ -61,11 +66,13 @@ if (props.id) {
asBadge: false,
canEditMembersByModerator: false,
displayOrder: 0,
+ preserveAssignmentOnMoveAccount: false,
policies: {},
};
}
async function save() {
+ if (data.value === null) return;
rolesCache.delete();
if (role.value) {
os.apiWithDialog('admin/roles/update', {
@@ -75,7 +82,7 @@ async function save() {
router.push('/admin/roles/:id', {
params: {
id: role.value.id,
- }
+ },
});
} else {
const created = await os.apiWithDialog('admin/roles/create', {
@@ -84,7 +91,7 @@ async function save() {
router.push('/admin/roles/:id', {
params: {
id: created.id,
- }
+ },
});
}
}
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 5f8950f07e..7de973a394 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_gaps">
- <MkInput v-if="readonly" :modelValue="role.id" :readonly="true">
+ <MkInput v-if="readonly && role.id != null" :modelValue="role.id" :readonly="true">
<template #label>ID</template>
</MkInput>
@@ -866,12 +866,18 @@ import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { deepClone } from '@/utility/clone.js';
+type RoleLike = Pick<Misskey.entities.Role, 'name' | 'description' | 'isAdministrator' | 'isModerator' | 'color' | 'iconUrl' | 'target' | 'isPublic' | 'isExplorable' | 'asBadge' | 'canEditMembersByModerator' | 'displayOrder' | 'preserveAssignmentOnMoveAccount'> & {
+ id?: Misskey.entities.Role['id'] | null;
+ condFormula: any;
+ policies: any;
+};
+
const emit = defineEmits<{
- (ev: 'update:modelValue', v: any): void;
+ (ev: 'update:modelValue', v: RoleLike): void;
}>();
const props = defineProps<{
- modelValue: any;
+ modelValue: RoleLike;
readonly?: boolean;
}>();
@@ -910,7 +916,7 @@ const rolePermission = computed<GetMkSelectValueTypesFromDef<typeof rolePermissi
const q = ref('');
-function getPriorityIcon(option) {
+function getPriorityIcon(option: { priority: number }): string {
if (option.priority === 2) return 'ti ti-arrows-up';
if (option.priority === 1) return 'ti ti-arrow-narrow-up';
return 'ti ti-point';
@@ -936,6 +942,7 @@ const save = throttle(100, () => {
isExplorable: role.value.isExplorable,
asBadge: role.value.asBadge,
canEditMembersByModerator: role.value.canEditMembersByModerator,
+ preserveAssignmentOnMoveAccount: role.value.preserveAssignmentOnMoveAccount,
policies: role.value.policies,
};
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 2e249eee50..7fc51979af 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -28,15 +28,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #default="{ items }">
<div class="_gaps_s">
- <div v-for="item in items" :key="item.user.id" :class="[$style.userItem, { [$style.userItemOpened]: expandedItems.includes(item.id) }]">
+ <div v-for="item in items" :key="item.user.id" :class="[$style.userItem, { [$style.userItemOpened]: expandedItemIds.includes(item.id) }]">
<div :class="$style.userItemMain">
<MkA :class="$style.userItemMainBody" :to="`/admin/user/${item.user.id}`">
<MkUserCardMini :user="item.user"/>
</MkA>
- <button class="_button" :class="$style.userToggle" @click="toggleItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button>
- <button class="_button" :class="$style.unassign" @click="unassign(item.user, $event)"><i class="ti ti-x"></i></button>
+ <button class="_button" :class="$style.userToggle" @click="toggleItem(item.id)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button>
+ <button class="_button" :class="$style.unassign" @click="unassign(item.user.id, $event)"><i class="ti ti-x"></i></button>
</div>
- <div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub">
+ <div v-if="expandedItemIds.includes(item.id)" :class="$style.userItemSub">
<div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div>
<div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div>
<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
@@ -55,6 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, markRaw, reactive, ref } from 'vue';
+import * as Misskey from 'misskey-js';
import XEditor from './roles.editor.vue';
import MkFolder from '@/components/MkFolder.vue';
import * as os from '@/os.js';
@@ -81,7 +82,7 @@ const usersPaginator = markRaw(new Paginator('admin/roles/users', {
}) : undefined),
}));
-const expandedItems = ref<string[]>([]);
+const expandedItemIds = ref<Misskey.entities.AdminRolesUsersResponse[number]['id'][]>([]);
const role = reactive(await misskeyApi('admin/roles/show', {
roleId: props.id,
@@ -91,7 +92,7 @@ function edit() {
router.push('/admin/roles/:id/edit', {
params: {
id: role.id,
- }
+ },
});
}
@@ -140,23 +141,23 @@ async function assign() {
//role.users.push(user);
}
-async function unassign(user, ev) {
+async function unassign(userId: Misskey.entities.User['id'], ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.unassign,
icon: 'ti ti-x',
danger: true,
action: async () => {
- await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.id });
- //role.users = role.users.filter(u => u.id !== user.id);
+ await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: userId });
+ //role.users = role.users.filter(u => u.id !== userId);
},
}], ev.currentTarget ?? ev.target);
}
-async function toggleItem(item) {
- if (expandedItems.value.includes(item.id)) {
- expandedItems.value = expandedItems.value.filter(x => x !== item.id);
+async function toggleItem(itemId: string) {
+ if (expandedItemIds.value.includes(itemId)) {
+ expandedItemIds.value = expandedItemIds.value.filter(x => x !== itemId);
} else {
- expandedItems.value.push(item.id);
+ expandedItemIds.value.push(itemId);
}
}
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index fa93124daa..f310f26107 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -25,11 +25,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<div><SearchText>{{ i18n.ts._sensitiveMediaDetection.description }}</SearchText></div>
- <MkRadios v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection">
- <option value="none">{{ i18n.ts.none }}</option>
- <option value="all">{{ i18n.ts.all }}</option>
- <option value="local">{{ i18n.ts.localOnly }}</option>
- <option value="remote">{{ i18n.ts.remoteOnly }}</option>
+ <MkRadios
+ v-model="sensitiveMediaDetectionForm.state.sensitiveMediaDetection"
+ :options="[
+ { value: 'none', label: i18n.ts.none },
+ { value: 'all', label: i18n.ts.all },
+ { value: 'local', label: i18n.ts.localOnly },
+ { value: 'remote', label: i18n.ts.remoteOnly },
+ ]"
+ >
</MkRadios>
<SearchMarker :keywords="['sensitivity']">
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index d26f02b41c..02aad732f6 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -12,28 +12,25 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<div><SearchText>{{ i18n.ts._serverRules.description }}</SearchText></div>
- <Sortable
+ <MkDraggable
v-model="serverRules"
- class="_gaps_m"
- :itemKey="(_, i) => i"
- :animation="150"
- :handle="'.' + $style.itemHandle"
- @start="e => e.item.classList.add('active')"
- @end="e => e.item.classList.remove('active')"
+ direction="vertical"
+ withGaps
+ manualDragStart
>
- <template #item="{element,index}">
+ <template #default="{ item, index, dragStart }">
<div :class="$style.item">
<div :class="$style.itemHeader">
- <div :class="$style.itemNumber" v-text="String(index + 1)"/>
- <span :class="$style.itemHandle"><i class="ti ti-menu"/></span>
- <button class="_button" :class="$style.itemRemove" @click="remove(index)"><i class="ti ti-x"></i></button>
+ <div :class="$style.itemNumber">{{ index + 1 }}</div>
+ <span :class="$style.itemHandle" :draggable="true" @dragstart.stop="dragStart"><i class="ti ti-menu"></i></span>
+ <button class="_button" :class="$style.itemRemove" @click="remove(item.id)"><i class="ti ti-x"></i></button>
</div>
- <MkInput v-model="serverRules[index]"/>
+ <MkInput :modelValue="item.text" @update:modelValue="serverRules[index].text = $event"/>
</div>
</template>
- </Sortable>
+ </MkDraggable>
<div :class="$style.commands">
- <MkButton rounded @click="serverRules.push('')"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+ <MkButton rounded @click="add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
</div>
</div>
@@ -42,28 +39,31 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { defineAsyncComponent, ref, computed } from 'vue';
+import { ref } from 'vue';
import * as os from '@/os.js';
import { fetchInstance, instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkFolder from '@/components/MkFolder.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
+const serverRules = ref<{ text: string; id: string; }[]>(instance.serverRules.map(text => ({ text, id: Math.random().toString() })));
-const serverRules = ref<string[]>(instance.serverRules);
-
-const save = async () => {
+async function save() {
await os.apiWithDialog('admin/update-meta', {
- serverRules: serverRules.value,
+ serverRules: serverRules.value.map(r => r.text),
});
fetchInstance(true);
-};
+}
-const remove = (index: number): void => {
- serverRules.value.splice(index, 1);
-};
+function add(): void {
+ serverRules.value.push({ text: '', id: Math.random().toString() });
+}
+
+function remove(id: string): void {
+ serverRules.value = serverRules.value.filter(r => r.id !== id);
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 541ee7c0cd..99d4455939 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -258,11 +258,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps">
<SearchMarker>
- <MkRadios v-model="federationForm.state.federation">
+ <MkRadios
+ v-model="federationForm.state.federation"
+ :options="[
+ { value: 'all', label: i18n.ts.all },
+ { value: 'specified', label: i18n.ts.specifyHost },
+ { value: 'none', label: i18n.ts.none },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel><span v-if="federationForm.modifiedStates.federation" class="_modified">{{ i18n.ts.modified }}</span></template>
- <option value="all">{{ i18n.ts.all }}</option>
- <option value="specified">{{ i18n.ts.specifyHost }}</option>
- <option value="none">{{ i18n.ts.none }}</option>
</MkRadios>
</SearchMarker>
diff --git a/packages/frontend/src/pages/admin/system-webhook.item.vue b/packages/frontend/src/pages/admin/system-webhook.item.vue
index b53667e98c..9807cbb313 100644
--- a/packages/frontend/src/pages/admin/system-webhook.item.vue
+++ b/packages/frontend/src/pages/admin/system-webhook.item.vue
@@ -8,14 +8,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ entity.name || entity.url }}</template>
<template v-if="entity.name != null && entity.name != ''" #caption>{{ entity.url }}</template>
<template #icon>
- <i v-if="!entity.isActive" class="ti ti-player-pause"/>
- <i v-else-if="entity.latestStatus === null" class="ti ti-circle"/>
+ <i v-if="!entity.isActive" class="ti ti-player-pause"></i>
+ <i v-else-if="entity.latestStatus === null" class="ti ti-circle"></i>
<i
v-else-if="[200, 201, 204].includes(entity.latestStatus)"
class="ti ti-check"
:style="{ color: 'var(--MI_THEME-success)' }"
- />
- <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--MI_THEME-error)' }"/>
+ ></i>
+ <i v-else class="ti ti-alert-triangle" :style="{ color: 'var(--MI_THEME-error)' }"></i>
</template>
<template #suffix>
<MkTime v-if="entity.latestSentAt" :time="entity.latestSentAt" style="margin-right: 8px"/>
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index 2f7ecca521..eb9806d668 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -46,6 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, markRaw, ref, watchEffect } from 'vue';
+import * as Misskey from 'misskey-js';
import { defaultMemoryStorage } from '@/memory-storage';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
@@ -146,7 +147,7 @@ async function addUser() {
});
}
-function show(user) {
+function show(user: Misskey.entities.UserDetailed) {
os.pageWindow(`/admin/user/${user.id}`);
}
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 4c34c3c74b..150808fcbd 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkA>
</div>
- <div v-if="tab !== 'past' && $i && !announcement.silence && !announcement.isRead" :class="$style.footer">
+ <div v-if="tab !== 'past' && $i != null && !announcement.silence && !announcement.isRead" :class="$style.footer">
<MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
</div>
</section>
@@ -45,6 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed, markRaw } from 'vue';
+import * as Misskey from 'misskey-js';
import MkPagination from '@/components/MkPagination.vue';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
@@ -65,7 +66,9 @@ const paginator = markRaw(new Paginator('announcements', {
const tab = ref('current');
-async function read(target) {
+async function read(target: Misskey.entities.Announcement) {
+ if ($i == null) return;
+
if (target.needConfirmationToRead) {
const confirm = await os.confirm({
type: 'question',
@@ -81,7 +84,7 @@ async function read(target) {
}));
misskeyApi('i/read-announcement', { announcementId: target.id });
updateCurrentAccountPartial({
- unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== target.id),
+ unreadAnnouncements: $i.unreadAnnouncements.filter(a => a.id !== target.id),
});
}
diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue
index f436fc72fa..8377dc074d 100644
--- a/packages/frontend/src/pages/api-console.vue
+++ b/packages/frontend/src/pages/api-console.vue
@@ -73,7 +73,7 @@ function onEndpointChange() {
return;
}
- const endpointBody = {};
+ const endpointBody = {} as Record<string, unknown>;
for (const p of resp.params) {
endpointBody[p.name] =
p.type === 'String' ? '' :
diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue
index 1a0c9b36c4..bc585950b4 100644
--- a/packages/frontend/src/pages/auth.form.vue
+++ b/packages/frontend/src/pages/auth.form.vue
@@ -5,10 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<section>
- <div v-if="app.permission.length > 0">
+ <div v-if="permissions.length > 0">
<p>{{ i18n.tsx._auth.permission({ name }) }}</p>
<ul>
- <li v-for="p in app.permission" :key="p">{{ i18n.ts._permissions[p] }}</li>
+ <li v-for="p in permissions" :key="p">{{ i18n.ts._permissions[p] ?? p }}</li>
</ul>
</div>
<div>{{ i18n.tsx._auth.shareAccess({ name: `${name} (${app.id})` }) }}</div>
@@ -37,6 +37,10 @@ const emit = defineEmits<{
const app = computed(() => props.session.app);
+const permissions = computed(() => {
+ return props.session.app.permission.filter((p): p is typeof Misskey.permissions[number] => typeof p === 'string');
+});
+
const name = computed(() => {
const el = window.document.createElement('div');
el.textContent = app.value.name;
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index 83bf7221d0..14b13e511a 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -67,7 +67,7 @@ function accepted() {
}
}
-function onLogin(res) {
+function onLogin(res: Misskey.entities.SigninFlowResponse & { finished: true }) {
login(res.i);
}
diff --git a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
index a8ce527523..68e8d6a4d0 100644
--- a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
+++ b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
@@ -78,7 +78,7 @@ import { ensureSignin } from '@/i.js';
const $i = ensureSignin();
const props = defineProps<{
- avatarDecoration?: any,
+ avatarDecoration?: Misskey.entities.AdminAvatarDecorationsListResponse[number],
}>();
const emit = defineEmits<{
@@ -109,7 +109,7 @@ async function addRole() {
rolesThatCanBeUsedThisDecoration.value.push(roles.find(r => r.id === roleId)!);
}
-async function removeRole(role, ev) {
+async function removeRole(role: Misskey.entities.Role, ev: PointerEvent) {
rolesThatCanBeUsedThisDecoration.value = rolesThatCanBeUsedThisDecoration.value.filter(x => x.id !== role.id);
}
@@ -147,6 +147,8 @@ async function done() {
}
async function del() {
+ if (props.avatarDecoration == null) return;
+
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.tsx.removeAreYouSure({ x: name.value }),
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index f96c02a567..4c5457504e 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -45,7 +45,7 @@ function load() {
load();
-async function add(ev: MouseEvent) {
+async function add(ev: PointerEvent) {
const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration-edit-dialog.vue').then(x => x.default), {
}, {
done: result => {
@@ -57,7 +57,7 @@ async function add(ev: MouseEvent) {
});
}
-async function edit(avatarDecoration) {
+async function edit(avatarDecoration: Misskey.entities.AdminAvatarDecorationsListResponse[number]) {
const { dispose } = await os.popupAsyncWithDialog(import('./avatar-decoration-edit-dialog.vue').then(x => x.default), {
avatarDecoration: avatarDecoration,
}, {
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 251f5d557d..4b73b6c6b3 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -41,20 +41,19 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps">
<MkButton primary rounded @click="addPinnedNote()"><i class="ti ti-plus"></i></MkButton>
- <Sortable
- v-model="pinnedNotes"
- itemKey="id"
- :handle="'.' + $style.pinnedNoteHandle"
- :animation="150"
+ <MkDraggable
+ :modelValue="pinnedNoteIds.map(id => ({ id }))"
+ direction="vertical"
+ @update:modelValue="v => pinnedNoteIds = v.map(x => x.id)"
>
- <template #item="{element,index}">
+ <template #default="{ item }">
<div :class="$style.pinnedNote">
<button class="_button" :class="$style.pinnedNoteHandle"><i class="ti ti-menu"></i></button>
- {{ element.id }}
- <button class="_button" :class="$style.pinnedNoteRemove" @click="removePinnedNote(index)"><i class="ti ti-x"></i></button>
+ {{ item.id }}
+ <button class="_button" :class="$style.pinnedNoteRemove" @click="removePinnedNote(item.id)"><i class="ti ti-x"></i></button>
</div>
</template>
- </Sortable>
+ </MkDraggable>
</div>
</MkFolder>
@@ -68,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, ref, watch, defineAsyncComponent } from 'vue';
+import { computed, ref, watch } from 'vue';
import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
@@ -81,10 +80,9 @@ import { i18n } from '@/i18n.js';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkTextarea from '@/components/MkTextarea.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
import { useRouter } from '@/router.js';
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-
const router = useRouter();
const props = defineProps<{
@@ -99,7 +97,7 @@ const bannerId = ref<string | null>(null);
const color = ref('#000');
const isSensitive = ref(false);
const allowRenoteToExternal = ref(true);
-const pinnedNotes = ref<{ id: Misskey.entities.Note['id'] }[]>([]);
+const pinnedNoteIds = ref<Misskey.entities.Note['id'][]>([]);
watch(() => bannerId.value, async () => {
if (bannerId.value == null) {
@@ -123,9 +121,7 @@ async function fetchChannel() {
bannerId.value = result.bannerId;
bannerUrl.value = result.bannerUrl;
isSensitive.value = result.isSensitive;
- pinnedNotes.value = result.pinnedNoteIds.map(id => ({
- id,
- }));
+ pinnedNoteIds.value = result.pinnedNoteIds;
color.value = result.color;
allowRenoteToExternal.value = result.allowRenoteToExternal;
@@ -143,13 +139,11 @@ async function addPinnedNote() {
const note = await os.apiWithDialog('notes/show', {
noteId: fromUrl ?? value,
});
- pinnedNotes.value = [{
- id: note.id,
- }, ...pinnedNotes.value];
+ pinnedNoteIds.value.unshift(note.id);
}
-function removePinnedNote(index: number) {
- pinnedNotes.value.splice(index, 1);
+function removePinnedNote(id: string) {
+ pinnedNoteIds.value = pinnedNoteIds.value.filter(x => x !== id);
}
function save() {
@@ -166,7 +160,7 @@ function save() {
os.apiWithDialog('channels/update', {
...params,
channelId: props.channelId,
- pinnedNoteIds: pinnedNotes.value.map(x => x.id),
+ pinnedNoteIds: pinnedNoteIds.value,
});
} else {
os.apiWithDialog('channels/create', params).then(created => {
@@ -197,7 +191,7 @@ async function archive() {
});
}
-function setBannerImage(evt) {
+function setBannerImage(evt: PointerEvent) {
selectFile({
anchorElement: evt.currentTarget ?? evt.target,
multiple: false,
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index f77034e318..26dc5b80df 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -11,9 +11,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
<template #prefix><i class="ti ti-search"></i></template>
</MkInput>
- <MkRadios v-model="searchType" @update:modelValue="search()">
- <option value="nameAndDescription">{{ i18n.ts._channel.nameAndDescription }}</option>
- <option value="nameOnly">{{ i18n.ts._channel.nameOnly }}</option>
+ <MkRadios
+ v-model="searchType"
+ :options="[
+ { value: 'nameAndDescription', label: i18n.ts._channel.nameAndDescription },
+ { value: 'nameOnly', label: i18n.ts._channel.nameOnly },
+ ]"
+ @update:modelValue="search()"
+ >
</MkRadios>
<MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton>
</div>
@@ -72,15 +77,17 @@ import { Paginator } from '@/utility/paginator.js';
const router = useRouter();
+type SearchType = 'nameAndDescription' | 'nameOnly';
+
const props = defineProps<{
query: string;
- type?: string;
+ type?: SearchType;
}>();
const key = ref('');
const tab = ref('featured');
const searchQuery = ref('');
-const searchType = ref('nameAndDescription');
+const searchType = ref<SearchType>('nameAndDescription');
const channelPaginator = shallowRef();
onMounted(() => {
diff --git a/packages/frontend/src/pages/chat/XMessage.vue b/packages/frontend/src/pages/chat/XMessage.vue
index 613c4e4dcc..f759e45e48 100644
--- a/packages/frontend/src/pages/chat/XMessage.vue
+++ b/packages/frontend/src/pages/chat/XMessage.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:enableEmojiMenu="true"
:enableEmojiMenuReaction="true"
/>
- <MkMediaList v-if="message.file" :mediaList="[message.file]" :class="$style.file"/>
+ <MkMediaList v-if="message.file" :mediaList="[message.file]"/>
</MkFukidashi>
<MkUrlPreview v-for="url in urls" :key="url" :url="url" style="margin: 8px 0;"/>
<div :class="$style.footer">
@@ -94,7 +94,7 @@ provide(DI.mfmEmojiReactCallback, (reaction) => {
});
});
-function react(ev: MouseEvent) {
+function react(ev: PointerEvent) {
if ($i.policies.chatAvailability !== 'available') return;
const targetEl = getHTMLElementOrNull(ev.currentTarget ?? ev.target);
@@ -128,14 +128,14 @@ function onReactionClick(record: Misskey.entities.ChatMessage['reactions'][0]) {
}
}
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
if (ev.target && isLink(ev.target as HTMLElement)) return;
if (window.getSelection()?.toString() !== '') return;
showMenu(ev, true);
}
-function showMenu(ev: MouseEvent, contextmenu = false) {
+function showMenu(ev: PointerEvent, contextmenu = false) {
const menu: MenuItem[] = [];
if (!isMe.value && $i.policies.chatAvailability === 'available') {
diff --git a/packages/frontend/src/pages/chat/home.home.vue b/packages/frontend/src/pages/chat/home.home.vue
index 756bf8a342..ed04253046 100644
--- a/packages/frontend/src/pages/chat/home.home.vue
+++ b/packages/frontend/src/pages/chat/home.home.vue
@@ -64,7 +64,7 @@ const searchQuery = ref('');
const searched = ref(false);
const searchResults = ref<Misskey.entities.ChatMessage[]>([]);
-function start(ev: MouseEvent) {
+function start(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts._chat.individualChat,
caption: i18n.ts._chat.individualChat_description,
@@ -89,7 +89,7 @@ async function startUser() {
router.push('/chat/user/:userId', {
params: {
userId: user.id,
- }
+ },
});
});
}
@@ -108,7 +108,7 @@ async function createRoom() {
router.push('/chat/room/:roomId', {
params: {
roomId: room.id,
- }
+ },
});
}
diff --git a/packages/frontend/src/pages/chat/room.form.vue b/packages/frontend/src/pages/chat/room.form.vue
index 17b68d6eb9..72aeba0a45 100644
--- a/packages/frontend/src/pages/chat/room.form.vue
+++ b/packages/frontend/src/pages/chat/room.form.vue
@@ -167,7 +167,7 @@ function onKeydown(ev: KeyboardEvent) {
}
}
-function chooseFile(ev: MouseEvent) {
+function chooseFile(ev: PointerEvent) {
selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
diff --git a/packages/frontend/src/pages/chat/room.vue b/packages/frontend/src/pages/chat/room.vue
index ef9191b4a5..a4204435b3 100644
--- a/packages/frontend/src/pages/chat/room.vue
+++ b/packages/frontend/src/pages/chat/room.vue
@@ -391,7 +391,7 @@ async function leaveRoom() {
router.push('/chat');
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
const menuItems: MenuItem[] = [];
if (room.value) {
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 8176fb519b..8feddf70b0 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -34,6 +34,7 @@ import { computed, watch, provide, ref, markRaw } from 'vue';
import * as Misskey from 'misskey-js';
import { url } from '@@/js/config.js';
import type { MenuItem } from '@/types/menu.js';
+import type { PageHeaderItem } from '@/types/page-header.js';
import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
@@ -105,7 +106,7 @@ async function unfavorite() {
});
}
-const headerActions = computed(() => clip.value && isOwned.value ? [{
+const headerActions = computed<PageHeaderItem[] | null>(() => clip.value && isOwned.value ? [{
icon: 'ti ti-pencil',
text: i18n.ts.edit,
handler: async (): Promise<void> => {
@@ -144,7 +145,7 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
}, ...(clip.value.isPublic ? [{
icon: 'ti ti-share',
text: i18n.ts.share,
- handler: (ev: MouseEvent): void => {
+ handler: (ev): void => {
const menuItems: MenuItem[] = [];
menuItems.push({
@@ -177,7 +178,7 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
},
-}] : []), {
+}] satisfies PageHeaderItem[] : []), {
icon: 'ti ti-trash',
text: i18n.ts.delete,
danger: true,
@@ -196,7 +197,7 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
clipsCache.delete();
},
-}] : null);
+}] satisfies PageHeaderItem[] : null);
definePage(() => ({
title: clip.value ? clip.value.name : i18n.ts.clip,
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 0f306896c9..5cb88f0b1a 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template>
<template #default="{items}">
<div class="ldhfsamy">
- <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)">
+ <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji as RemoteEmoji, $event)">
<img :src="getProxiedImageUrl(emoji.url, 'emoji')" class="img" :alt="emoji.name"/>
<div class="body">
<div class="name _monospace">{{ emoji.name }}</div>
@@ -71,7 +71,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, markRaw, ref } from 'vue';
+import * as Misskey from 'misskey-js';
+import { computed, markRaw, ref } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkPagination from '@/components/MkPagination.vue';
@@ -93,6 +94,8 @@ const host = ref<string | null>(null);
const selectMode = ref(false);
const selectedEmojis = ref<string[]>([]);
+type RemoteEmoji = Misskey.entities.AdminEmojiListRemoteResponse[number] & { host: string };
+
const paginator = markRaw(new Paginator('admin/emoji/list', {
limit: 30,
computedParams: computed(() => ({
@@ -116,7 +119,7 @@ const selectAll = () => {
}
};
-const toggleSelect = (emoji) => {
+const toggleSelect = (emoji: Misskey.entities.EmojiDetailed) => {
if (selectedEmojis.value.includes(emoji.id)) {
selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id);
} else {
@@ -124,19 +127,23 @@ const toggleSelect = (emoji) => {
}
};
-const add = async (ev: MouseEvent) => {
+const add = async () => {
const { dispose } = await os.popupAsyncWithDialog(import('./emoji-edit-dialog.vue').then(x => x.default), {
}, {
done: result => {
if (result.created) {
- paginator.prepend(result.created);
+ const nowIso = (new Date()).toISOString();
+ paginator.prepend({
+ ...result.created,
+ createdAt: nowIso,
+ });
}
},
closed: () => dispose(),
});
};
-const edit = async (emoji) => {
+const edit = async (emoji: Misskey.entities.EmojiDetailed) => {
const { dispose } = await os.popupAsyncWithDialog(import('./emoji-edit-dialog.vue').then(x => x.default), {
emoji: emoji,
}, {
@@ -154,7 +161,13 @@ const edit = async (emoji) => {
});
};
-const detailRemoteEmoji = (emoji) => {
+const detailRemoteEmoji = (emoji: {
+ id: string,
+ name: string,
+ host: string,
+ license: string | null,
+ url: string
+}) => {
const { dispose } = os.popup(MkRemoteEmojiEditDialog, {
emoji: emoji,
}, {
@@ -167,13 +180,19 @@ const detailRemoteEmoji = (emoji) => {
});
};
-const importEmoji = (emoji) => {
+const importEmoji = (emojiId: string) => {
os.apiWithDialog('admin/emoji/copy', {
- emojiId: emoji.id,
+ emojiId: emojiId,
});
};
-const remoteMenu = (emoji, ev: MouseEvent) => {
+const remoteMenu = (emoji: {
+ id: string,
+ name: string,
+ host: string,
+ license: string | null,
+ url: string
+}, ev: PointerEvent) => {
os.popupMenu([{
type: 'label',
text: ':' + emoji.name + ':',
@@ -184,11 +203,11 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
}, {
text: i18n.ts.import,
icon: 'ti ti-plus',
- action: () => { importEmoji(emoji); },
+ action: () => { importEmoji(emoji.id); },
}], ev.currentTarget ?? ev.target);
};
-const menu = (ev: MouseEvent) => {
+const menu = (ev: PointerEvent) => {
os.popupMenu([{
icon: 'ti ti-download',
text: i18n.ts.export,
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index e3cc1d988e..6b57684188 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -131,10 +131,11 @@ function move() {
const f = file.value;
- selectDriveFolder(null).then(folder => {
+ selectDriveFolder(null).then(({ canceled, folders }) => {
+ if (canceled) return;
misskeyApi('drive/files/update', {
fileId: f.id,
- folderId: folder[0] ? folder[0].id : null,
+ folderId: folders[0] ? folders[0].id : null,
}).then(async () => {
await _fetch_();
});
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index 88300d8a74..21e4657b2c 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div ref="containerEl" :class="[$style.gameContainer, { [$style.gameOver]: isGameOver && !replaying }]" @contextmenu.stop.prevent @click.stop.prevent="onClick" @touchmove.stop.prevent="onTouchmove" @touchend="onTouchend" @mousemove="onMousemove">
<img v-if="store.s.darkMode" src="/client-assets/drop-and-fusion/frame-dark.svg" :class="$style.mainFrameImg"/>
<img v-else src="/client-assets/drop-and-fusion/frame-light.svg" :class="$style.mainFrameImg"/>
- <canvas ref="canvasEl" :class="$style.canvas"/>
+ <canvas ref="canvasEl" :class="$style.canvas"></canvas>
<Transition
:enterActiveClass="$style.transition_combo_enterActive"
:leaveActiveClass="$style.transition_combo_leaveActive"
@@ -82,7 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</Transition>
<template v-if="dropReady && currentPick">
<img src="/client-assets/drop-and-fusion/drop-arrow.svg" :class="$style.currentMonoArrow"/>
- <div :class="$style.dropGuide"/>
+ <div :class="$style.dropGuide"></div>
</template>
</div>
<div v-if="isGameOver && !replaying" :class="$style.gameOverLabel">
@@ -729,7 +729,7 @@ async function start() {
}, 1500);
}
-function onClick(ev: MouseEvent) {
+function onClick(ev: PointerEvent) {
if (!containerElRect) return;
if (replaying.value) return;
const x = (ev.clientX - containerElRect.left) / viewScale;
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index ea4863950d..edd3987524 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-for="role in rolesThatCanBeUsedThisEmojiAsReaction" :key="role.id" :class="$style.roleItem">
<MkRolePreview :class="$style.role" :role="role" :forModeration="true" :detailed="false" style="pointer-events: none;"/>
- <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="removeRole(role, $event)"><i class="ti ti-x"></i></button>
+ <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="removeRole(role)"><i class="ti ti-x"></i></button>
<button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
</div>
@@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo warn>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}</MkInfo>
</div>
</MkFolder>
- <MkSwitch v-model="isSensitive">isSensitive</MkSwitch>
+ <MkSwitch v-model="isSensitive">{{ i18n.ts.sensitive }}</MkSwitch>
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
<MkButton v-if="emoji" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
</div>
@@ -99,7 +99,7 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
- (ev: 'done', v: { deleted?: boolean; updated?: Misskey.entities.AdminEmojiUpdateRequest; created?: Misskey.entities.AdminEmojiUpdateRequest }): void,
+ (ev: 'done', v: { deleted?: boolean; updated?: Misskey.entities.EmojiDetailed; created?: Misskey.entities.EmojiDetailed }): void,
(ev: 'closed'): void
}>();
@@ -120,7 +120,7 @@ watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? props.emoji.url : null);
-async function changeImage(ev: Event) {
+async function changeImage(ev: PointerEvent) {
file.value = await selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
@@ -143,7 +143,7 @@ async function addRole() {
rolesThatCanBeUsedThisEmojiAsReaction.value.push(roles.find(r => r.id === roleId)!);
}
-async function removeRole(role: Misskey.entities.RoleLite, ev: Event) {
+async function removeRole(role: Misskey.entities.RoleLite) {
rolesThatCanBeUsedThisEmojiAsReaction.value = rolesThatCanBeUsedThisEmojiAsReaction.value.filter(x => x.id !== role.id);
}
@@ -157,19 +157,29 @@ async function done() {
localOnly: localOnly.value,
roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id),
fileId: file.value ? file.value.id : undefined,
- };
+ } satisfies Misskey.entities.AdminEmojiUpdateRequest;
if (props.emoji) {
+ const emojiDetailed = {
+ id: props.emoji.id,
+ aliases: params.aliases,
+ name: params.name,
+ category: params.category,
+ host: props.emoji.host,
+ url: file.value ? file.value.url : props.emoji.url,
+ license: params.license,
+ isSensitive: params.isSensitive,
+ localOnly: params.localOnly,
+ roleIdsThatCanBeUsedThisEmojiAsReaction: params.roleIdsThatCanBeUsedThisEmojiAsReaction,
+ } satisfies Misskey.entities.EmojiDetailed;
+
await os.apiWithDialog('admin/emoji/update', {
id: props.emoji.id,
...params,
});
emit('done', {
- updated: {
- id: props.emoji.id,
- ...params,
- },
+ updated: emojiDetailed,
});
windowEl.value?.close();
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index aaf433e78e..bed7f2166a 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -15,7 +15,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
-import { defineAsyncComponent } from 'vue';
import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/utility/misskey-api.js';
@@ -28,7 +27,7 @@ const props = defineProps<{
emoji: Misskey.entities.EmojiSimple;
}>();
-function menu(ev) {
+function menu(ev: PointerEvent) {
const menuItems: MenuItem[] = [];
menuItems.push({
type: 'label',
@@ -57,22 +56,21 @@ function menu(ev) {
menuItems.push({
text: i18n.ts.edit,
icon: 'ti ti-pencil',
- action: () => {
- edit(props.emoji);
+ action: async () => {
+ const detailedEmoji = await misskeyApiGet('emoji', {
+ name: props.emoji.name,
+ });
+ const { dispose } = await os.popupAsyncWithDialog(import('@/pages/emoji-edit-dialog.vue').then(x => x.default), {
+ emoji: detailedEmoji,
+ }, {
+ closed: () => dispose(),
+ });
},
});
}
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
}
-
-const edit = async (emoji) => {
- const { dispose } = await os.popupAsyncWithDialog(import('@/pages/emoji-edit-dialog.vue').then(x => x.default), {
- emoji: emoji,
- }, {
- closed: () => dispose(),
- });
-};
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index b3e8e88c23..3d9de0584a 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -395,7 +395,7 @@ const {
});
const script = ref(flash.value?.script ?? PRESET_DEFAULT);
-function selectPreset(ev: MouseEvent) {
+function selectPreset(ev: PointerEvent) {
os.popupMenu([{
text: 'Omikuji',
action: () => {
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index efc9ee014f..449f1af60a 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -104,7 +104,7 @@ function fetchFlash() {
});
}
-function share(ev: MouseEvent) {
+function share(ev: PointerEvent) {
if (!flash.value) return;
const menuItems: MenuItem[] = [];
@@ -151,9 +151,11 @@ function shareWithNote() {
});
}
-function like() {
+async function like() {
if (!flash.value) return;
- pleaseLogin();
+
+ const isLoggedIn = await pleaseLogin();
+ if (!isLoggedIn) return;
os.apiWithDialog('flash/like', {
flashId: flash.value.id,
@@ -165,7 +167,9 @@ function like() {
async function unlike() {
if (!flash.value) return;
- pleaseLogin();
+
+ const isLoggedIn = await pleaseLogin();
+ if (!isLoggedIn) return;
const confirm = await os.confirm({
type: 'warning',
@@ -208,7 +212,7 @@ async function run() {
const version = utils.getLangVersion(flash.value.script);
const isLegacy = getIsLegacy(version);
- const { Interpreter, Parser, values } = isLegacy ? (await import('@syuilo/aiscript-0-19-0') as any) : await import('@syuilo/aiscript');
+ const { Interpreter, Parser, values } = (isLegacy ? (await import('@syuilo/aiscript-0-19-0')) : await import('@syuilo/aiscript')) as typeof import('@syuilo/aiscript');
const parser = new Parser();
@@ -225,10 +229,10 @@ async function run() {
THIS_URL: values.STR(`${url}/play/${flash.value.id}`),
}, {
in: aiScriptReadline,
- out: (value) => {
+ out: () => {
// nop
},
- log: (type, params) => {
+ log: () => {
// nop
},
});
@@ -269,7 +273,7 @@ async function reportAbuse() {
});
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
if (!flash.value) return;
const menu: MenuItem[] = [
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index ba24d7abc6..2404fd9744 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<p class="acct">@{{ acct(displayUser(req)) }}</p>
</div>
<div v-if="tab === 'list'" class="commands">
- <MkButton class="command" rounded primary @click="accept(displayUser(req))"><i class="ti ti-check"/> {{ i18n.ts.accept }}</MkButton>
- <MkButton class="command" rounded danger @click="reject(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
+ <MkButton class="command" rounded primary @click="accept(displayUser(req))"><i class="ti ti-check"></i> {{ i18n.ts.accept }}</MkButton>
+ <MkButton class="command" rounded danger @click="reject(displayUser(req))"><i class="ti ti-x"></i> {{ i18n.ts.reject }}</MkButton>
</div>
<div v-else class="commands">
- <MkButton class="command" rounded danger @click="cancel(displayUser(req))"><i class="ti ti-x"/> {{ i18n.ts.cancel }}</MkButton>
+ <MkButton class="command" rounded danger @click="cancel(displayUser(req))"><i class="ti ti-x"></i> {{ i18n.ts.cancel }}</MkButton>
</div>
</div>
</div>
@@ -89,7 +89,7 @@ async function cancel(user: Misskey.entities.UserLite) {
});
}
-function displayUser(req) {
+function displayUser(req: Misskey.entities.FollowingRequestsListResponse[number]) {
return tab.value === 'list' ? req.follower : req.followee;
}
diff --git a/packages/frontend/src/pages/gallery/edit.root.vue b/packages/frontend/src/pages/gallery/edit.root.vue
index 45493ab561..ec0a293494 100644
--- a/packages/frontend/src/pages/gallery/edit.root.vue
+++ b/packages/frontend/src/pages/gallery/edit.root.vue
@@ -58,7 +58,7 @@ const description = ref(props.post?.description ?? null);
const title = ref(props.post?.title ?? '');
const isSensitive = ref(props.post?.isSensitive ?? false);
-function chooseFile(evt) {
+function chooseFile(evt: MouseEvent) {
selectFile({
anchorElement: evt.currentTarget ?? evt.target,
multiple: true,
@@ -67,7 +67,7 @@ function chooseFile(evt) {
});
}
-function remove(file) {
+function remove(file: NonNullable<Misskey.entities.GalleryPost['files']>[number]) {
files.value = files.value.filter(f => f.id !== file.id);
}
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index f60bbc0b74..92cb663ee1 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></button>
<button v-tooltip="i18n.ts.copyLink" v-click-anime class="_button" @click="copyLink"><i class="ti ti-link ti-fw"></i></button>
<button v-if="isSupportShare()" v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ti ti-share ti-fw"></i></button>
- <button v-if="$i && $i.id !== post.user.id" v-click-anime class="_button" @mousedown="showMenu"><i class="ti ti-dots ti-fw"></i></button>
+ <button v-if="$i && $i.id !== post.user.id" v-click-anime class="_button" @click="showMenu"><i class="ti ti-dots ti-fw"></i></button>
</div>
</div>
<div class="user">
@@ -175,7 +175,7 @@ async function reportAbuse() {
});
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
if (!post.value) return;
const menuItems: MenuItem[] = [];
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 132c55571a..92a5d25983 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -110,7 +110,7 @@ function addUser() {
});
}
-async function removeUser(item, ev) {
+async function removeUser(item: Misskey.entities.UsersListsGetMembershipsResponse[number], ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.remove,
icon: 'ti ti-x',
@@ -127,7 +127,7 @@ async function removeUser(item, ev) {
}], ev.currentTarget ?? ev.target);
}
-async function showMembershipMenu(item, ev) {
+async function showMembershipMenu(item: Misskey.entities.UsersListsGetMembershipsResponse[number], ev: PointerEvent) {
const withRepliesRef = ref(item.withReplies);
os.popupMenu([{
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 5d308e6b29..37ec6284a3 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, markRaw, ref } from 'vue';
import { notificationTypes } from 'misskey-js';
+import type { PageHeaderItem } from '@/types/page-header.js';
import MkStreamingNotificationsTimeline from '@/components/MkStreamingNotificationsTimeline.vue';
import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
import * as os from '@/os.js';
@@ -44,7 +45,7 @@ const directNotesPaginator = markRaw(new Paginator('notes/mentions', {
},
}));
-function setFilter(ev) {
+function setFilter(ev: PointerEvent) {
const typeItems = notificationTypes.map(t => ({
text: i18n.ts._notification._types[t],
active: (includeTypes.value && includeTypes.value.includes(t)) ?? false,
@@ -62,7 +63,7 @@ function setFilter(ev) {
os.popupMenu(items, ev.currentTarget ?? ev.target);
}
-const headerActions = computed(() => [tab.value === 'all' ? {
+const headerActions = computed<PageHeaderItem[]>(() => ([tab.value === 'all' ? {
text: i18n.ts.filter,
icon: 'ti ti-filter',
highlighted: includeTypes.value != null,
@@ -73,7 +74,7 @@ const headerActions = computed(() => [tab.value === 'all' ? {
handler: () => {
os.apiWithDialog('notifications/mark-all-as-read', {});
},
-} : undefined].filter(x => x !== undefined));
+} : undefined] as (PageHeaderItem | undefined)[]).filter(x => x !== undefined));
const headerTabs = computed(() => [{
key: 'all',
diff --git a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
index f191320180..18f6c40013 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
@@ -4,36 +4,41 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<Sortable :modelValue="modelValue" tag="div" itemKey="id" handle=".drag-handle" :group="{ name: 'blocks' }" :animation="150" :swapThreshold="0.5" @update:modelValue="v => emit('update:modelValue', v)">
- <template #item="{element}">
- <div :class="$style.item">
- <!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->
- <component :is="getComponent(element.type)" :modelValue="element" @update:modelValue="updateItem" @remove="() => removeItem(element)"/>
+<MkDraggable
+ :modelValue="modelValue"
+ direction="vertical"
+ withGaps
+ canNest
+ group="pageBlocks"
+ @update:modelValue="v => emit('update:modelValue', v)"
+>
+ <template #default="{ item }">
+ <div>
+ <!-- divが無いとエラーになる -->
+ <component :is="getComponent(item.type) as any" :modelValue="item" @update:modelValue="updateItem" @remove="() => removeItem(item)"/>
</div>
</template>
-</Sortable>
+</MkDraggable>
</template>
<script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
import * as Misskey from 'misskey-js';
import XSection from './els/page-editor.el.section.vue';
import XText from './els/page-editor.el.text.vue';
import XImage from './els/page-editor.el.image.vue';
import XNote from './els/page-editor.el.note.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
-function getComponent(type: string) {
+function getComponent(type: Misskey.entities.Page['content'][number]['type']) {
switch (type) {
case 'section': return XSection;
case 'text': return XText;
case 'image': return XImage;
case 'note': return XNote;
- default: return null;
+ default: return XText;
}
}
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-
const props = defineProps<{
modelValue: Misskey.entities.Page['content'];
}>();
@@ -42,7 +47,7 @@ const emit = defineEmits<{
(ev: 'update:modelValue', value: Misskey.entities.Page['content']): void;
}>();
-function updateItem(v) {
+function updateItem(v: Misskey.entities.PageBlock) {
const i = props.modelValue.findIndex(x => x.id === v.id);
const newValue = [
...props.modelValue.slice(0, i),
@@ -52,8 +57,8 @@ function updateItem(v) {
emit('update:modelValue', newValue);
}
-function removeItem(el) {
- const i = props.modelValue.findIndex(x => x.id === el.id);
+function removeItem(v: Misskey.entities.PageBlock) {
+ const i = props.modelValue.findIndex(x => x.id === v.id);
const newValue = [
...props.modelValue.slice(0, i),
...props.modelValue.slice(i + 1),
@@ -61,11 +66,3 @@ function removeItem(el) {
emit('update:modelValue', newValue);
}
</script>
-
-<style lang="scss" module>
-.item {
- & + .item {
- margin-top: 16px;
- }
-}
-</style>
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index 3b36f7fa2d..85871c993c 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -247,9 +247,9 @@ async function add() {
}
}
-function setEyeCatchingImage(img: Event) {
+function setEyeCatchingImage(ev: PointerEvent) {
selectFile({
- anchorElement: img.currentTarget ?? img.target,
+ anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
}).then(file => {
eyeCatchingImageId.value = file.id;
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index c3b52a24fd..212c8140c8 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA v-if="page.userId === $i?.id" v-tooltip="i18n.ts._pages.editThisPage" :to="`/pages/edit/${page.id}`" class="_button" :class="$style.generalActionButton"><i class="ti ti-pencil ti-fw"></i></MkA>
<button v-tooltip="i18n.ts.copyLink" class="_button" :class="$style.generalActionButton" @click="copyLink"><i class="ti ti-link ti-fw"></i></button>
<button v-tooltip="i18n.ts.share" class="_button" :class="$style.generalActionButton" @click="share"><i class="ti ti-share ti-fw"></i></button>
- <button v-if="$i" v-click-anime class="_button" :class="$style.generalActionButton" @mousedown="showMenu"><i class="ti ti-dots ti-fw"></i></button>
+ <button v-if="$i" v-click-anime class="_button" :class="$style.generalActionButton" @click="showMenu"><i class="ti ti-dots ti-fw"></i></button>
</div>
</div>
<div :class="$style.pageUser">
@@ -163,7 +163,7 @@ function fetchPage() {
});
}
-function share(ev: MouseEvent) {
+function share(ev: PointerEvent) {
if (!page.value) return;
const menuItems: MenuItem[] = [];
@@ -237,7 +237,7 @@ async function unlike() {
});
}
-function pin(pin) {
+function pin(pin: boolean) {
if (!page.value) return;
os.apiWithDialog('i/update', {
@@ -258,7 +258,7 @@ async function reportAbuse() {
});
}
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
if (!page.value) return;
const menuItems: MenuItem[] = [];
diff --git a/packages/frontend/src/pages/qr.read.vue b/packages/frontend/src/pages/qr.read.vue
index 251dccd0f0..5e3633c052 100644
--- a/packages/frontend/src/pages/qr.read.vue
+++ b/packages/frontend/src/pages/qr.read.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>
<div :class="$style.view">
<video ref="videoEl" :class="$style.video" autoplay muted playsinline></video>
- <div ref="overlayEl" :class="$style.overlay"></div>
+ <div ref="overlayEl"></div>
<div :class="$style.controls">
<MkButton v-tooltip="i18n.ts._qr.scanFile" iconOnly @click="upload"><i class="ti ti-photo-plus"></i></MkButton>
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index aae638641a..5988604652 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -151,7 +151,7 @@ import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import { deepClone } from '@/utility/clone.js';
-import { ensureSignin } from '@/i.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { userPage } from '@/filters/user.js';
@@ -160,8 +160,6 @@ import * as os from '@/os.js';
import { confetti } from '@/utility/confetti.js';
import { genId } from '@/utility/id.js';
-const $i = ensureSignin();
-
const props = defineProps<{
game: Misskey.entities.ReversiGameDetailed;
connection?: Misskey.IChannelConnection<Misskey.Channels['reversiGame']> | null;
@@ -182,13 +180,13 @@ const engine = shallowRef<Reversi.Game>(Reversi.Serializer.restoreGame({
}));
const iAmPlayer = computed(() => {
- return game.value.user1Id === $i.id || game.value.user2Id === $i.id;
+ return game.value.user1Id === $i?.id || game.value.user2Id === $i?.id;
});
const myColor = computed(() => {
if (!iAmPlayer.value) return null;
- if (game.value.user1Id === $i.id && game.value.black === 1) return true;
- if (game.value.user2Id === $i.id && game.value.black === 2) return true;
+ if (game.value.user1Id === $i?.id && game.value.black === 1) return true;
+ if (game.value.user2Id === $i?.id && game.value.black === 2) return true;
return false;
});
@@ -219,7 +217,7 @@ const isMyTurn = computed(() => {
if (!iAmPlayer.value) return false;
const u = turnUser.value;
if (u == null) return false;
- return u.id === $i.id;
+ return u.id === $i?.id;
});
const cellsStyle = computed(() => {
@@ -308,7 +306,7 @@ if (!props.game.isEnded) {
}, TIMER_INTERVAL_SEC * 1000, { immediate: false, afterMounted: true });
}
-async function onStreamLog(log) {
+async function onStreamLog(log: Reversi.Serializer.Log & { id: string | null }) {
game.value.logs = Reversi.Serializer.serializeLogs([
...Reversi.Serializer.deserializeLogs(game.value.logs),
log,
@@ -348,10 +346,13 @@ async function onStreamLog(log) {
}
}
-function onStreamEnded(x) {
+function onStreamEnded(x: {
+ winnerId: Misskey.entities.User['id'] | null;
+ game: Misskey.entities.ReversiGameDetailed;
+}) {
game.value = deepClone(x.game);
- if (game.value.winnerId === $i.id) {
+ if (game.value.winnerId === $i?.id) {
confetti({
duration: 1000 * 3,
});
@@ -384,7 +385,7 @@ function checkEnd() {
}
}
-function restoreGame(_game) {
+function restoreGame(_game: Misskey.entities.ReversiGameDetailed) {
game.value = deepClone(_game);
engine.value = Reversi.Serializer.restoreGame({
diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue
index 1e01496bbb..f3f89d163b 100644
--- a/packages/frontend/src/pages/reversi/game.setting.vue
+++ b/packages/frontend/src/pages/reversi/game.setting.vue
@@ -35,22 +35,28 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder :defaultOpen="true">
<template #label>{{ i18n.ts._reversi.blackOrWhite }}</template>
- <MkRadios v-model="game.bw">
- <option value="random">{{ i18n.ts.random }}</option>
- <option :value="'1'">
+ <MkRadios
+ v-model="game.bw"
+ :options="[
+ { value: 'random', label: i18n.ts.random },
+ { value: '1', slotId: 'user1' },
+ { value: '2', slotId: 'user2' },
+ ]"
+ >
+ <template #option-user1>
<I18n :src="i18n.ts._reversi.blackIs" tag="span">
<template #name>
<b><MkUserName :user="game.user1"/></b>
</template>
</I18n>
- </option>
- <option :value="'2'">
+ </template>
+ <template #option-user2>
<I18n :src="i18n.ts._reversi.blackIs" tag="span">
<template #name>
<b><MkUserName :user="game.user2"/></b>
</template>
</I18n>
- </option>
+ </template>
</MkRadios>
</MkFolder>
@@ -58,15 +64,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts._reversi.timeLimitForEachTurn }}</template>
<template #suffix>{{ game.timeLimitForEachTurn }}{{ i18n.ts._time.second }}</template>
- <MkRadios v-model="game.timeLimitForEachTurn">
- <option :value="5">5{{ i18n.ts._time.second }}</option>
- <option :value="10">10{{ i18n.ts._time.second }}</option>
- <option :value="30">30{{ i18n.ts._time.second }}</option>
- <option :value="60">60{{ i18n.ts._time.second }}</option>
- <option :value="90">90{{ i18n.ts._time.second }}</option>
- <option :value="120">120{{ i18n.ts._time.second }}</option>
- <option :value="180">180{{ i18n.ts._time.second }}</option>
- <option :value="3600">3600{{ i18n.ts._time.second }}</option>
+ <MkRadios
+ v-model="game.timeLimitForEachTurn"
+ :options="gameTurnOptionsDef"
+ >
</MkRadios>
</MkFolder>
@@ -110,22 +111,21 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, watch, ref, onMounted, shallowRef, onUnmounted } from 'vue';
+import { computed, watch, ref, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js';
import * as Reversi from 'misskey-reversi';
+import type { MenuItem } from '@/types/menu.js';
import { i18n } from '@/i18n.js';
-import { ensureSignin } from '@/i.js';
+import { $i } from '@/i.js';
import { deepClone } from '@/utility/clone.js';
import MkButton from '@/components/MkButton.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkFolder from '@/components/MkFolder.vue';
import * as os from '@/os.js';
-import type { MenuItem } from '@/types/menu.js';
+import type { MkRadiosOption } from '@/components/MkRadios.vue';
import { useRouter } from '@/router.js';
-const $i = ensureSignin();
-
const router = useRouter();
const mapCategories = Array.from(new Set(Object.values(Reversi.maps).map(x => x.category)));
@@ -139,19 +139,30 @@ const shareWhenStart = defineModel<boolean>('shareWhenStart', { default: false }
const game = ref<Misskey.entities.ReversiGameDetailed>(deepClone(props.game));
+const gameTurnOptionsDef = [
+ { value: 5, label: '5' + i18n.ts._time.second },
+ { value: 10, label: '10' + i18n.ts._time.second },
+ { value: 30, label: '30' + i18n.ts._time.second },
+ { value: 60, label: '60' + i18n.ts._time.second },
+ { value: 90, label: '90' + i18n.ts._time.second },
+ { value: 120, label: '120' + i18n.ts._time.second },
+ { value: 180, label: '180' + i18n.ts._time.second },
+ { value: 3600, label: '3600' + i18n.ts._time.second },
+] as MkRadiosOption<number>[];
+
const mapName = computed(() => {
if (game.value.map == null) return 'Random';
const found = Object.values(Reversi.maps).find(x => x.data.join('') === game.value.map.join(''));
return found ? found.name! : '-Custom-';
});
const isReady = computed(() => {
- if (game.value.user1Id === $i.id && game.value.user1Ready) return true;
- if (game.value.user2Id === $i.id && game.value.user2Ready) return true;
+ if (game.value.user1Id === $i?.id && game.value.user1Ready) return true;
+ if (game.value.user2Id === $i?.id && game.value.user2Ready) return true;
return false;
});
const isOpReady = computed(() => {
- if (game.value.user1Id !== $i.id && game.value.user1Ready) return true;
- if (game.value.user2Id !== $i.id && game.value.user2Ready) return true;
+ if (game.value.user1Id !== $i?.id && game.value.user1Ready) return true;
+ if (game.value.user2Id !== $i?.id && game.value.user2Ready) return true;
return false;
});
@@ -165,7 +176,7 @@ watch(() => game.value.timeLimitForEachTurn, () => {
updateSettings('timeLimitForEachTurn');
});
-function chooseMap(ev: MouseEvent) {
+function chooseMap(ev: PointerEvent) {
const menu: MenuItem[] = [];
for (const c of mapCategories) {
@@ -212,7 +223,10 @@ function unready() {
props.connection.send('ready', false);
}
-function onChangeReadyStates(states) {
+function onChangeReadyStates(states: {
+ user1: boolean;
+ user2: boolean;
+}) {
game.value.user1Ready = states.user1;
game.value.user2Ready = states.user2;
}
@@ -225,7 +239,7 @@ function updateSettings(key: typeof Misskey.reversiUpdateKeys[number]) {
}
function onUpdateSettings<K extends typeof Misskey.reversiUpdateKeys[number]>({ userId, key, value }: { userId: string; key: K; value: Misskey.entities.ReversiGameDetailed[K]; }) {
- if (userId === $i.id) return;
+ if (userId === $i?.id) return;
if (game.value[key] === value) return;
game.value[key] = value;
if (isReady.value) {
diff --git a/packages/frontend/src/pages/reversi/game.vue b/packages/frontend/src/pages/reversi/game.vue
index b1ba4da247..926d825b66 100644
--- a/packages/frontend/src/pages/reversi/game.vue
+++ b/packages/frontend/src/pages/reversi/game.vue
@@ -17,15 +17,13 @@ import GameBoard from './game.board.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { definePage } from '@/page.js';
import { useStream } from '@/stream.js';
-import { ensureSignin } from '@/i.js';
+import { $i } from '@/i.js';
import { useRouter } from '@/router.js';
import * as os from '@/os.js';
import { url } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { useInterval } from '@@/js/use-interval.js';
-const $i = ensureSignin();
-
const router = useRouter();
const props = defineProps<{
@@ -74,7 +72,7 @@ async function fetchGame() {
connection.value.on('canceled', x => {
connection.value?.dispose();
- if (x.userId !== $i.id) {
+ if (x.userId !== $i?.id) {
os.alert({
type: 'warning',
text: i18n.ts._reversi.gameCanceled,
diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue
index 0ae374649d..9a737e93ac 100644
--- a/packages/frontend/src/pages/reversi/index.vue
+++ b/packages/frontend/src/pages/reversi/index.vue
@@ -197,7 +197,8 @@ async function matchHeatbeat() {
}
async function matchUser() {
- pleaseLogin();
+ const isLoggedIn = await pleaseLogin();
+ if (!isLoggedIn) return;
const user = await os.selectUser({ includeSelf: false, localOnly: true });
if (user == null) return;
@@ -207,8 +208,9 @@ async function matchUser() {
matchHeatbeat();
}
-function matchAny(ev: MouseEvent) {
- pleaseLogin();
+async function matchAny(ev: PointerEvent) {
+ const isLoggedIn = await pleaseLogin();
+ if (!isLoggedIn) return;
os.popupMenu([{
text: i18n.ts._reversi.allowIrregularRules,
@@ -237,11 +239,11 @@ function cancelMatching() {
}
}
-async function accept(user) {
+async function accept(user: Misskey.entities.UserLite) {
const game = await misskeyApi('reversi/match', {
userId: user.id,
});
- if (game) {
+ if (game != null) {
startGame(game);
}
}
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 4e02556c83..b3b899517e 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -97,7 +97,7 @@ watch(code, () => {
miLocalStorage.setItem('scratchpad', code.value);
});
-function stringifyUiProps(uiProps) {
+function stringifyUiProps(uiProps: AsUiComponent) {
return JSON.stringify(
{ ...uiProps, type: undefined, id: undefined },
(k, v) => typeof v === 'function' ? '<function>' : v,
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index fb34d592a6..ab36f2e6c5 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -19,11 +19,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header>{{ i18n.ts.options }}</template>
<div class="_gaps_m">
- <MkRadios v-model="searchScope">
- <option v-if="instance.federation !== 'none' && noteSearchableScope === 'global'" value="all">{{ i18n.ts._search.searchScopeAll }}</option>
- <option value="local">{{ instance.federation === 'none' ? i18n.ts._search.searchScopeAll : i18n.ts._search.searchScopeLocal }}</option>
- <option v-if="instance.federation !== 'none' && noteSearchableScope === 'global'" value="server">{{ i18n.ts._search.searchScopeServer }}</option>
- <option value="user">{{ i18n.ts._search.searchScopeUser }}</option>
+ <MkRadios
+ v-model="searchScope"
+ :options="searchScopeDef"
+ >
</MkRadios>
<div v-if="instance.federation !== 'none' && searchScope === 'server'" :class="$style.subOptionRoot">
@@ -71,7 +70,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkUserCardMini
:user="user"
:withChart="false"
- :class="$style.userSelectedCard"
/>
</div>
<div>
@@ -128,6 +126,7 @@ import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
import MkRadios from '@/components/MkRadios.vue';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { Paginator } from '@/utility/paginator.js';
+import type { MkRadiosOption } from '@/components/MkRadios.vue';
const props = withDefaults(defineProps<{
query?: string;
@@ -184,6 +183,24 @@ const searchScope = ref<'all' | 'local' | 'server' | 'user'>((() => {
return 'all';
})());
+const searchScopeDef = computed<MkRadiosOption[]>(() => {
+ const options: MkRadiosOption[] = [];
+
+ if (instance.federation !== 'none' && noteSearchableScope === 'global') {
+ options.push({ value: 'all', label: i18n.ts._search.searchScopeAll });
+ }
+
+ options.push({ value: 'local', label: instance.federation === 'none' ? i18n.ts._search.searchScopeAll : i18n.ts._search.searchScopeLocal });
+
+ if (instance.federation !== 'none' && noteSearchableScope === 'global') {
+ options.push({ value: 'server', label: i18n.ts._search.searchScopeServer });
+ }
+
+ options.push({ value: 'user', label: i18n.ts._search.searchScopeUser });
+
+ return options;
+});
+
type SearchParams = {
readonly query: string;
readonly host?: string;
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index 5110fca10c..cc91adb63d 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -9,10 +9,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter.prevent="search">
<template #prefix><i class="ti ti-search"></i></template>
</MkInput>
- <MkRadios v-if="instance.federation !== 'none'" v-model="searchOrigin" @update:modelValue="search()">
- <option value="combined">{{ i18n.ts.all }}</option>
- <option value="local">{{ i18n.ts.local }}</option>
- <option value="remote">{{ i18n.ts.remote }}</option>
+ <MkRadios
+ v-if="instance.federation !== 'none'"
+ v-model="searchOrigin"
+ :options="[
+ { value: 'combined', label: i18n.ts.all },
+ { value: 'local', label: i18n.ts.local },
+ { value: 'remote', label: i18n.ts.remote },
+ ]"
+ @update:modelValue="search()"
+ >
</MkRadios>
<MkButton large primary gradate rounded @click="search">{{ i18n.ts.search }}</MkButton>
</div>
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 2cc13744b1..bf71845a38 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--MI_THEME-success)"></i></template>
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
- <div v-text="i18n.ts._2fa.alreadyRegistered"/>
+ <div>{{ i18n.ts._2fa.alreadyRegistered }}</div>
<template v-if="$i.securityKeysList!.length > 0">
<MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
@@ -85,6 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { defineAsyncComponent, computed } from 'vue';
import { supported as webAuthnSupported, create as webAuthnCreate, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
+import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkSwitch from '@/components/MkSwitch.vue';
@@ -156,7 +157,7 @@ function renewTOTP(): void {
});
}
-async function unregisterKey(key) {
+async function unregisterKey(key: NonNullable<Misskey.entities.MeDetailedOnly['securityKeysList']>[number]) {
const confirm = await os.confirm({
type: 'question',
title: i18n.ts._2fa.removeKey,
@@ -175,7 +176,7 @@ async function unregisterKey(key) {
os.success();
}
-async function renameKey(key) {
+async function renameKey(key: NonNullable<Misskey.entities.MeDetailedOnly['securityKeysList']>[number]) {
const name = await os.inputText({
title: i18n.ts.rename,
default: key.name,
diff --git a/packages/frontend/src/pages/settings/account-data.vue b/packages/frontend/src/pages/settings/account-data.vue
index c75667b06b..b07515a49a 100644
--- a/packages/frontend/src/pages/settings/account-data.vue
+++ b/packages/frontend/src/pages/settings/account-data.vue
@@ -189,7 +189,7 @@ const onImportSuccess = () => {
});
};
-const onError = (ev) => {
+const onError = (ev: Error) => {
os.alert({
type: 'error',
text: ev.message,
@@ -232,7 +232,7 @@ const exportAntennas = () => {
misskeyApi('i/export-antennas', {}).then(onExportSuccess).catch(onError);
};
-const importFollowing = async (ev) => {
+const importFollowing = async (ev: PointerEvent) => {
const file = await selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
@@ -243,7 +243,7 @@ const importFollowing = async (ev) => {
}).then(onImportSuccess).catch(onError);
};
-const importUserLists = async (ev) => {
+const importUserLists = async (ev: PointerEvent) => {
const file = await selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
@@ -251,7 +251,7 @@ const importUserLists = async (ev) => {
misskeyApi('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError);
};
-const importMuting = async (ev) => {
+const importMuting = async (ev: PointerEvent) => {
const file = await selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
@@ -259,7 +259,7 @@ const importMuting = async (ev) => {
misskeyApi('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError);
};
-const importBlocking = async (ev) => {
+const importBlocking = async (ev: PointerEvent) => {
const file = await selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
@@ -267,7 +267,7 @@ const importBlocking = async (ev) => {
misskeyApi('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
};
-const importAntennas = async (ev) => {
+const importAntennas = async (ev: PointerEvent) => {
const file = await selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 764ec72652..55a81bbf38 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -38,7 +38,7 @@ function refreshAllAccounts() {
// TODO
}
-function showMenu(host: string, id: string, ev: MouseEvent) {
+function showMenu(host: string, id: string, ev: PointerEvent) {
let menu: MenuItem[];
menu = [{
@@ -54,7 +54,7 @@ function showMenu(host: string, id: string, ev: MouseEvent) {
os.popupMenu(menu, ev.currentTarget ?? ev.target);
}
-function addAccount(ev: MouseEvent) {
+function addAccount(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.existingAccount,
action: () => { addExistingAccount(); },
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index 10901f737b..e9857b1e0b 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.permission }}</template>
<template #suffix>{{ Object.keys(token.permission).length === 0 ? i18n.ts.none : Object.keys(token.permission).length }}</template>
<ul>
- <li v-for="p in token.permission" :key="p">{{ i18n.ts._permissions[p] }}</li>
+ <li v-for="p in token.permission" :key="p">{{ (i18n.ts._permissions as any)[p] ?? p }}</li>
</ul>
</MkFolder>
</div>
@@ -68,7 +68,7 @@ const paginator = markRaw(new Paginator('i/apps', {
},
}));
-function revoke(token) {
+function revoke(token: Misskey.entities.IAppsResponse[number]) {
misskeyApi('i/revoke-token', { tokenId: token.id }).then(() => {
paginator.reload();
});
diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue
index 7a19b0495b..40fee6caaf 100644
--- a/packages/frontend/src/pages/settings/deck.vue
+++ b/packages/frontend/src/pages/settings/deck.vue
@@ -40,31 +40,43 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['column', 'align']">
<MkPreferenceContainer k="deck.columnAlign">
- <MkRadios v-model="columnAlign">
+ <MkRadios
+ v-model="columnAlign"
+ :options="[
+ { value: 'left', label: i18n.ts.left },
+ { value: 'center', label: i18n.ts.center },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts._deck.columnAlign }}</SearchLabel></template>
- <option value="left">{{ i18n.ts.left }}</option>
- <option value="center">{{ i18n.ts.center }}</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['menu', 'position']">
<MkPreferenceContainer k="deck.menuPosition">
- <MkRadios v-model="menuPosition">
+ <MkRadios
+ v-model="menuPosition"
+ :options="[
+ { value: 'right', label: i18n.ts.right },
+ { value: 'bottom', label: i18n.ts.bottom },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts._deck.deckMenuPosition }}</SearchLabel></template>
- <option value="right">{{ i18n.ts.right }}</option>
- <option value="bottom">{{ i18n.ts.bottom }}</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['navbar', 'position']">
<MkPreferenceContainer k="deck.navbarPosition">
- <MkRadios v-model="navbarPosition">
+ <MkRadios
+ v-model="navbarPosition"
+ :options="[
+ { value: 'left', label: i18n.ts.left },
+ { value: 'top', label: i18n.ts.top },
+ { value: 'bottom', label: i18n.ts.bottom },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts._deck.navbarPosition }}</SearchLabel></template>
- <option value="left">{{ i18n.ts.left }}</option>
- <option value="top">{{ i18n.ts.top }}</option>
- <option value="bottom">{{ i18n.ts.bottom }}</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
@@ -113,7 +125,7 @@ watch(wallpaper, () => {
suggestReload();
});
-function setWallpaper(ev: MouseEvent) {
+function setWallpaper(ev: PointerEvent) {
selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 57192c0fb7..7189e19780 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -60,6 +60,7 @@ import bytes from '@/filters/bytes.js';
import { definePage } from '@/page.js';
import MkSelect from '@/components/MkSelect.vue';
import { useMkSelect } from '@/composables/use-mkselect.js';
+import { useGlobalEvent } from '@/events.js';
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
import { Paginator } from '@/utility/paginator.js';
@@ -115,14 +116,20 @@ function genUsageBar(fsize: number): StyleValue {
};
}
-function onClick(ev: MouseEvent, file) {
+function onClick(ev: PointerEvent, file: Misskey.entities.DriveFile) {
os.popupMenu(getDriveFileMenu(file), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
}
-function onContextMenu(ev: MouseEvent, file): void {
+function onContextMenu(ev: PointerEvent, file: Misskey.entities.DriveFile): void {
os.contextMenu(getDriveFileMenu(file), ev);
}
+useGlobalEvent('driveFilesDeleted', (files) => {
+ for (const f of files) {
+ paginator.removeItem(f.id);
+ }
+});
+
definePage(() => ({
title: i18n.ts.drivecleaner,
icon: 'ti ti-trash',
diff --git a/packages/frontend/src/pages/settings/drive.ImageFrameItem.vue b/packages/frontend/src/pages/settings/drive.ImageFrameItem.vue
index 62922fc964..f92e87375f 100644
--- a/packages/frontend/src/pages/settings/drive.ImageFrameItem.vue
+++ b/packages/frontend/src/pages/settings/drive.ImageFrameItem.vue
@@ -52,7 +52,7 @@ async function edit() {
});
}
-function del(ev: MouseEvent) {
+function del(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.delete,
action: () => {
diff --git a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue
index 0c03a4493a..9e80d719de 100644
--- a/packages/frontend/src/pages/settings/drive.WatermarkItem.vue
+++ b/packages/frontend/src/pages/settings/drive.WatermarkItem.vue
@@ -52,7 +52,7 @@ async function edit() {
});
}
-function del(ev: MouseEvent) {
+function del(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.delete,
action: () => {
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 8d443921a9..b170d17a5a 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -296,8 +296,9 @@ if (prefer.s.uploadFolder) {
}
function chooseUploadFolder() {
- selectDriveFolder(null).then(async folder => {
- prefer.commit('uploadFolder', folder[0] ? folder[0].id : null);
+ selectDriveFolder(null).then(async ({ canceled, folders }) => {
+ if (canceled) return;
+ prefer.commit('uploadFolder', folders[0] ? folders[0].id : null);
os.success();
if (prefer.s.uploadFolder) {
uploadFolder.value = await misskeyApi('drive/folders/show', {
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index 469a3c2f1c..85fea7ae66 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -76,11 +76,11 @@ const $i = ensureSignin();
const emailAddress = ref($i.email ?? '');
-const onChangeReceiveAnnouncementEmail = (v) => {
+function onChangeReceiveAnnouncementEmail(v: boolean) {
misskeyApi('i/update', {
receiveAnnouncementEmail: v,
});
-};
+}
async function saveEmailAddress() {
const auth = await os.authenticateDialog();
diff --git a/packages/frontend/src/pages/settings/emoji-palette.palette.vue b/packages/frontend/src/pages/settings/emoji-palette.palette.vue
index b624d424f3..d8a5f16b7d 100644
--- a/packages/frontend/src/pages/settings/emoji-palette.palette.vue
+++ b/packages/frontend/src/pages/settings/emoji-palette.palette.vue
@@ -18,19 +18,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>
<div v-panel style="border-radius: 6px;">
- <Sortable
- v-model="emojis"
+ <MkDraggable
+ :modelValue="emojis.map(emoji => ({ id: emoji, emoji }))"
+ direction="horizontal"
:class="$style.emojis"
- :itemKey="item => item"
- :animation="150"
- :delay="100"
- :delayOnTouchOnly="true"
- :group="{ name: 'SortableEmojiPalettes' }"
+ group="emojiPalettes"
+ @update:modelValue="v => emojis = v.map(x => x.emoji)"
>
- <template #item="{element}">
- <button class="_button" :class="$style.emojisItem" @click="remove(element, $event)">
- <MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true" :fallbackToImage="true"/>
- <MkEmoji v-else :emoji="element" :normal="true"/>
+ <template #default="{ item }">
+ <button class="_button" :class="$style.emojisItem" @click="remove(item.emoji, $event)">
+ <!-- pointer-eventsをnoneにしておかないとiOSなどでドラッグしたときに画像の方に判定が持ってかれる -->
+ <MkCustomEmoji v-if="item.emoji[0] === ':'" style="pointer-events: none;" :name="item.emoji" :normal="true" :fallbackToImage="true"/>
+ <MkEmoji v-else style="pointer-events: none;" :emoji="item.emoji" :normal="true"/>
</button>
</template>
<template #footer>
@@ -38,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-plus"></i>
</button>
</template>
- </Sortable>
+ </MkDraggable>
</div>
<div :class="$style.editorCaption">{{ i18n.ts.reactionSettingDescription2 }}</div>
</div>
@@ -47,7 +46,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, watch } from 'vue';
-import Sortable from 'vuedraggable';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
@@ -55,6 +53,7 @@ import { deepClone } from '@/utility/clone.js';
import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue';
import MkEmoji from '@/components/global/MkEmoji.vue';
import MkFolder from '@/components/MkFolder.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
const props = defineProps<{
@@ -77,7 +76,7 @@ watch(emojis, () => {
emit('updateEmojis', emojis.value);
}, { deep: true });
-function remove(reaction: string, ev: MouseEvent) {
+function remove(reaction: string, ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.remove,
action: () => {
@@ -86,7 +85,7 @@ function remove(reaction: string, ev: MouseEvent) {
}], getHTMLElement(ev));
}
-function pick(ev: MouseEvent) {
+function pick(ev: PointerEvent) {
os.pickEmoji(getHTMLElement(ev), {
showPinned: false,
}).then(it => {
@@ -97,7 +96,7 @@ function pick(ev: MouseEvent) {
});
}
-function getHTMLElement(ev: MouseEvent): HTMLElement {
+function getHTMLElement(ev: PointerEvent): HTMLElement {
const target = ev.currentTarget ?? ev.target;
return target as HTMLElement;
}
@@ -125,7 +124,7 @@ function paste() {
});
}
-function del(ev: MouseEvent) {
+function del(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.delete,
action: () => {
diff --git a/packages/frontend/src/pages/settings/emoji-palette.vue b/packages/frontend/src/pages/settings/emoji-palette.vue
index 7f31699ed1..cb665554cd 100644
--- a/packages/frontend/src/pages/settings/emoji-palette.vue
+++ b/packages/frontend/src/pages/settings/emoji-palette.vue
@@ -63,38 +63,33 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<SearchMarker :keywords="['emoji', 'picker', 'scale', 'size']">
<MkPreferenceContainer k="emojiPickerScale">
- <MkRadios v-model="emojiPickerScale">
+ <MkRadios
+ v-model="emojiPickerScale"
+ :options="emojiPickerScaleDef"
+ >
<template #label><SearchLabel>{{ i18n.ts.size }}</SearchLabel></template>
- <option :value="1">{{ i18n.ts.small }}</option>
- <option :value="2">{{ i18n.ts.medium }}</option>
- <option :value="3">{{ i18n.ts.large }}</option>
- <option :value="4">{{ i18n.ts.large }}+</option>
- <option :value="5">{{ i18n.ts.large }}++</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['emoji', 'picker', 'width', 'column', 'size']">
<MkPreferenceContainer k="emojiPickerWidth">
- <MkRadios v-model="emojiPickerWidth">
+ <MkRadios
+ v-model="emojiPickerWidth"
+ :options="emojiPickerWidthDef"
+ >
<template #label><SearchLabel>{{ i18n.ts.numberOfColumn }}</SearchLabel></template>
- <option :value="1">5</option>
- <option :value="2">6</option>
- <option :value="3">7</option>
- <option :value="4">8</option>
- <option :value="5">9</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['emoji', 'picker', 'height', 'size']">
<MkPreferenceContainer k="emojiPickerHeight">
- <MkRadios v-model="emojiPickerHeight">
+ <MkRadios
+ v-model="emojiPickerHeight"
+ :options="emojiPickerHeightDef"
+ >
<template #label><SearchLabel>{{ i18n.ts.height }}</SearchLabel></template>
- <option :value="1">{{ i18n.ts.small }}</option>
- <option :value="2">{{ i18n.ts.medium }}</option>
- <option :value="3">{{ i18n.ts.large }}</option>
- <option :value="4">{{ i18n.ts.large }}+</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
@@ -126,6 +121,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, ref, watch } from 'vue';
import XPalette from './emoji-palette.palette.vue';
import type { MkSelectItem } from '@/components/MkSelect.vue';
+import type { MkRadiosOption } from '@/components/MkRadios.vue';
import { genId } from '@/utility/id.js';
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
import MkRadios from '@/components/MkRadios.vue';
@@ -158,8 +154,31 @@ const emojiPaletteForMainDef = computed<MkSelectItem[]>(() => [
})),
]);
const emojiPickerScale = prefer.model('emojiPickerScale');
+const emojiPickerScaleDef = [
+ { label: i18n.ts.small, value: 1 },
+ { label: i18n.ts.medium, value: 2 },
+ { label: i18n.ts.large, value: 3 },
+ { label: i18n.ts.large + '+', value: 4 },
+ { label: i18n.ts.large + '++', value: 5 },
+] as MkRadiosOption<number>[];
+
const emojiPickerWidth = prefer.model('emojiPickerWidth');
+const emojiPickerWidthDef = [
+ { label: '5', value: 1 },
+ { label: '6', value: 2 },
+ { label: '7', value: 3 },
+ { label: '8', value: 4 },
+ { label: '9', value: 5 },
+] as MkRadiosOption<number>[];
+
const emojiPickerHeight = prefer.model('emojiPickerHeight');
+const emojiPickerHeightDef = [
+ { label: i18n.ts.small, value: 1 },
+ { label: i18n.ts.medium, value: 2 },
+ { label: i18n.ts.large, value: 3 },
+ { label: i18n.ts.large + '+', value: 4 },
+] as MkRadiosOption<number>[];
+
const emojiPickerStyle = prefer.model('emojiPickerStyle');
const palettesSyncEnabled = ref(prefer.isSyncEnabled('emojiPalettes'));
@@ -226,12 +245,12 @@ function delPalette(id: string) {
}
}
-function getHTMLElement(ev: MouseEvent): HTMLElement {
+function getHTMLElement(ev: PointerEvent): HTMLElement {
const target = ev.currentTarget ?? ev.target;
return target as HTMLElement;
}
-function previewPicker(ev: MouseEvent) {
+function previewPicker(ev: PointerEvent) {
emojiPicker.show(getHTMLElement(ev));
}
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 39c32d347f..abfac37275 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
<div class="_gaps_s">
<MkInfo v-if="emailNotConfigured" warn class="info">{{ i18n.ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ i18n.ts.configure }}</MkA></MkInfo>
- <MkInfo v-if="!storagePersisted && store.r.showStoragePersistenceSuggestion.value" class="info">
+ <MkInfo v-if="storagePersistenceSupported && !storagePersisted && store.r.showStoragePersistenceSuggestion.value" class="info">
<div>{{ i18n.ts._settings.settingsPersistence_description1 }}</div>
<div>{{ i18n.ts._settings.settingsPersistence_description2 }}</div>
<div><button class="_textButton" @click="enableStoragePersistence">{{ i18n.ts.enable }}</button> | <button class="_textButton" @click="skipStoragePersistence">{{ i18n.ts.skip }}</button></div>
@@ -51,10 +51,12 @@ import { enableAutoBackup, getPreferencesProfileMenu } from '@/preferences/utili
import { store } from '@/store.js';
import { signout } from '@/signout.js';
import { genSearchIndexes } from '@/utility/inapp-search.js';
-import { enableStoragePersistence, storagePersisted, skipStoragePersistence } from '@/utility/storage.js';
+import { enableStoragePersistence, getStoragePersistenceStatusRef, storagePersistenceSupported, skipStoragePersistence } from '@/utility/storage.js';
const searchIndex = await import('search-index:settings').then(({ searchIndexes }) => genSearchIndexes(searchIndexes));
+const storagePersisted = await getStoragePersistenceStatusRef();
+
const indexInfo = {
title: i18n.ts.settings,
icon: 'ti ti-settings',
@@ -166,7 +168,7 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
type: 'button',
icon: 'ti ti-settings-2',
text: i18n.ts.preferencesProfile,
- action: async (ev: MouseEvent) => {
+ action: async (ev) => {
os.popupMenu(getPreferencesProfileMenu(), ev.currentTarget ?? ev.target);
},
}, {
diff --git a/packages/frontend/src/pages/settings/mute-block.emoji-mute.vue b/packages/frontend/src/pages/settings/mute-block.emoji-mute.vue
index ea131381a1..37cd9fa67d 100644
--- a/packages/frontend/src/pages/settings/mute-block.emoji-mute.vue
+++ b/packages/frontend/src/pages/settings/mute-block.emoji-mute.vue
@@ -55,12 +55,12 @@ import {
const emojis = prefer.model('mutingEmojis');
-function getHTMLElement(ev: MouseEvent): HTMLElement {
+function getHTMLElement(ev: PointerEvent): HTMLElement {
const target = ev.currentTarget ?? ev.target;
return target as HTMLElement;
}
-function add(ev: MouseEvent) {
+function add(ev: PointerEvent) {
os.pickEmoji(getHTMLElement(ev), { showPinned: false }).then((emoji) => {
if (emoji) {
muteEmoji(emoji);
@@ -68,7 +68,7 @@ function add(ev: MouseEvent) {
});
}
-function onEmojiClick(ev: MouseEvent, emoji: string) {
+function onEmojiClick(ev: PointerEvent, emoji: string) {
const menuItems : MenuItem[] = [{
type: 'label',
text: emoji,
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 6fd9f07a47..433969f474 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -173,6 +173,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed, watch, markRaw } from 'vue';
+import * as Misskey from 'misskey-js';
import XEmojiMute from './mute-block.emoji-mute.vue';
import XInstanceMute from './mute-block.instance-mute.vue';
import XWordMute from './mute-block.word-mute.vue';
@@ -218,7 +219,7 @@ watch([
suggestReload();
});
-async function unrenoteMute(user, ev) {
+async function unrenoteMute(user: Misskey.entities.UserDetailed, ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.renoteUnmute,
icon: 'ti ti-x',
@@ -229,7 +230,7 @@ async function unrenoteMute(user, ev) {
}], ev.currentTarget ?? ev.target);
}
-async function unmute(user, ev) {
+async function unmute(user: Misskey.entities.UserDetailed, ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.unmute,
icon: 'ti ti-x',
@@ -240,7 +241,7 @@ async function unmute(user, ev) {
}], ev.currentTarget ?? ev.target);
}
-async function unblock(user, ev) {
+async function unblock(user: Misskey.entities.UserDetailed, ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.unblock,
icon: 'ti ti-x',
diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue
index f5837abe98..49d8ecd92d 100644
--- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue
+++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue
@@ -30,7 +30,7 @@ const emit = defineEmits<{
(ev: 'save', value: (string[] | string)[]): void;
}>();
-const render = (mutedWords) => mutedWords.map(x => {
+const render = (mutedWords: (string | string[])[]) => mutedWords.map(x => {
if (Array.isArray(x)) {
return x.join(' ');
} else {
@@ -46,13 +46,13 @@ watch(mutedWords, () => {
});
async function save() {
- const parseMutes = (mutes) => {
+ const parseMutes = (mutes: string) => {
// split into lines, remove empty lines and unnecessary whitespace
- let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== '');
+ let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== '') as (string | string[])[];
// check each line if it is a RegExp or not
for (let i = 0; i < lines.length; i++) {
- const line = lines[i];
+ const line = lines[i] as string;
const regexp = line.match(/^\/(.+)\/(.*)$/);
if (regexp) {
// check that the RegExp is valid
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index d25708dcb4..997a9f00c2 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -9,25 +9,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSlot>
<template #label>{{ i18n.ts.navbar }}</template>
<MkContainer :showHeader="false">
- <Sortable
+ <MkDraggable
v-model="items"
- itemKey="id"
- :animation="150"
- :handle="'.' + $style.itemHandle"
- @start="e => e.item.classList.add('active')"
- @end="e => e.item.classList.remove('active')"
+ direction="vertical"
>
- <template #item="{element,index}">
+ <template #default="{ item }">
<div
- v-if="element.type === '-' || navbarItemDef[element.type]"
+ v-if="item.type === '-' || navbarItemDef[item.type]"
:class="$style.item"
>
<button class="_button" :class="$style.itemHandle"><i class="ti ti-menu"></i></button>
- <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[element.type]?.icon]"></i><span :class="$style.itemText">{{ navbarItemDef[element.type]?.title ?? i18n.ts.divider }}</span>
- <button class="_button" :class="$style.itemRemove" @click="removeItem(index)"><i class="ti ti-x"></i></button>
+ <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item.type]?.icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item.type]?.title ?? i18n.ts.divider }}</span>
+ <button class="_button" :class="$style.itemRemove" @click="removeItem(item.id)"><i class="ti ti-x"></i></button>
</div>
</template>
- </Sortable>
+ </MkDraggable>
</MkContainer>
</FormSlot>
<div class="_buttons">
@@ -36,10 +32,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton primary class="save" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
</div>
- <MkRadios v-model="menuDisplay">
+ <MkRadios
+ v-model="menuDisplay"
+ :options="[
+ { value: 'sideFull', label: i18n.ts._menuDisplay.sideFull },
+ { value: 'sideIcon', label: i18n.ts._menuDisplay.sideIcon },
+ ]"
+ >
<template #label>{{ i18n.ts.display }}</template>
- <option value="sideFull">{{ i18n.ts._menuDisplay.sideFull }}</option>
- <option value="sideIcon">{{ i18n.ts._menuDisplay.sideIcon }}</option>
</MkRadios>
<SearchMarker :keywords="['navbar', 'sidebar', 'toggle', 'button', 'sub']">
@@ -54,13 +54,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, watch } from 'vue';
+import { computed, ref } from 'vue';
import MkRadios from '@/components/MkRadios.vue';
import MkButton from '@/components/MkButton.vue';
import FormSlot from '@/components/form/slot.vue';
import MkContainer from '@/components/MkContainer.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { store } from '@/store.js';
@@ -70,15 +71,13 @@ import { prefer } from '@/preferences.js';
import { getInitialPrefValue } from '@/preferences/manager.js';
import { genId } from '@/utility/id.js';
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-
const items = ref(prefer.s.menu.map(x => ({
id: genId(),
type: x,
})));
const itemTypeValues = computed(() => items.value.map(x => x.type));
-const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
+const menuDisplay = store.model('menuDisplay');
const showNavbarSubButtons = prefer.model('showNavbarSubButtons');
async function addItem() {
@@ -98,8 +97,8 @@ async function addItem() {
}];
}
-function removeItem(index: number) {
- items.value.splice(index, 1);
+function removeItem(itemId: string) {
+ items.value = items.value.filter(i => i.id !== itemId);
}
function save() {
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 2802d3263e..3787e07626 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -13,16 +13,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSection first>
<template #label>{{ i18n.ts.notificationRecieveConfig }}</template>
<div class="_gaps_s">
- <MkFolder v-for="type in notificationTypes.filter(x => !nonConfigurableNotificationTypes.includes(x))" :key="type">
+ <MkFolder v-for="type in configurableNotificationTypes" :key="type">
<template #label>{{ i18n.ts._notification._types[type] }}</template>
<template #suffix>
{{
- $i.notificationRecieveConfig[type]?.type === 'never' ? i18n.ts.none :
- $i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following :
- $i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers :
- $i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
- $i.notificationRecieveConfig[type]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower :
- $i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList :
+ $i.notificationRecieveConfig[type as (typeof configurableNotificationTypes)[number]]?.type === 'never' ? i18n.ts.none :
+ $i.notificationRecieveConfig[type as (typeof configurableNotificationTypes)[number]]?.type === 'following' ? i18n.ts.following :
+ $i.notificationRecieveConfig[type as (typeof configurableNotificationTypes)[number]]?.type === 'follower' ? i18n.ts.followers :
+ $i.notificationRecieveConfig[type as (typeof configurableNotificationTypes)[number]]?.type === 'mutualFollow' ? i18n.ts.mutualFollow :
+ $i.notificationRecieveConfig[type as (typeof configurableNotificationTypes)[number]]?.type === 'followingOrFollower' ? i18n.ts.followingOrFollower :
+ $i.notificationRecieveConfig[type as (typeof configurableNotificationTypes)[number]]?.type === 'list' ? i18n.ts.userList :
i18n.ts.all
}}
</template>
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XNotificationConfig
:userLists="userLists"
:value="$i.notificationRecieveConfig[type] ?? { type: 'all' }"
- :configurableTypes="onlyOnOrOffNotificationTypes.includes(type) ? ['all', 'never'] : undefined"
+ :configurableTypes="(onlyOnOrOffNotificationTypes as string[]).includes(type) ? ['all', 'never'] : undefined"
@update="(res) => updateReceiveConfig(type, res)"
/>
</MkFolder>
@@ -83,9 +83,11 @@ import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
const $i = ensureSignin();
-const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] satisfies (typeof notificationTypes[number])[] as string[];
+const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] as const satisfies (typeof notificationTypes[number])[];
-const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login', 'createToken', 'scheduledNotePosted', 'scheduledNotePostFailed'] satisfies (typeof notificationTypes[number])[] as string[];
+const configurableNotificationTypes = notificationTypes.filter(type => !nonConfigurableNotificationTypes.includes(type as any)) as Exclude<typeof notificationTypes[number], typeof nonConfigurableNotificationTypes[number]>[];
+
+const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login', 'createToken', 'scheduledNotePosted', 'scheduledNotePostFailed'] as const satisfies (typeof notificationTypes[number])[];
const allowButton = useTemplateRef('allowButton');
const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index d4097bde94..4facc696a4 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_s">
<div v-for="policy in Object.keys($i.policies)" :key="policy">
- {{ policy }} ... {{ $i.policies[policy] }}
+ {{ policy }} ... {{ $i.policies[policy as keyof typeof $i.policies] }}
</div>
</div>
</MkFolder>
@@ -142,7 +142,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<hr>
</template>
- <MkButton v-if="!storagePersisted" @click="enableStoragePersistence">{{ i18n.ts._settings.settingsPersistence_title }}</MkButton>
+ <MkButton v-if="storagePersistenceSupported && !storagePersisted" @click="enableStoragePersistence">{{ i18n.ts._settings.settingsPersistence_title }}</MkButton>
<MkButton @click="forceCloudBackup">{{ i18n.ts._preferencesBackup.forceBackup }}</MkButton>
@@ -165,7 +165,7 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
import MkButton from '@/components/MkButton.vue';
import FormSlot from '@/components/form/slot.vue';
import * as os from '@/os.js';
-import { enableStoragePersistence, storagePersisted, skipStoragePersistence } from '@/utility/storage.js';
+import { enableStoragePersistence, getStoragePersistenceStatusRef, storagePersistenceSupported } from '@/utility/storage.js';
import { ensureSignin } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
@@ -180,6 +180,8 @@ import { cloudBackup } from '@/preferences/utility.js';
const $i = ensureSignin();
+const storagePersisted = await getStoragePersistenceStatusRef();
+
const reportError = prefer.model('reportError');
const enableCondensedLine = prefer.model('enableCondensedLine');
const skipNoteRender = prefer.model('skipNoteRender');
diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue
index 7c6ce90e7e..89f457cf69 100644
--- a/packages/frontend/src/pages/settings/plugin.vue
+++ b/packages/frontend/src/pages/settings/plugin.vue
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #key>{{ i18n.ts.permission }}</template>
<template #value>
<ul style="margin-top: 0; margin-bottom: 0;">
- <li v-for="permission in plugin.permissions" :key="permission">{{ i18n.ts._permissions[permission] }}</li>
+ <li v-for="permission in plugin.permissions" :key="permission">{{ i18n.ts._permissions[permission] ?? permission }}</li>
<li v-if="!plugin.permissions || plugin.permissions.length === 0">{{ i18n.ts.none }}</li>
</ul>
</template>
@@ -96,6 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { nextTick, ref, computed } from 'vue';
+import { isSafeMode } from '@@/js/config.js';
import type { Plugin } from '@/plugin.js';
import FormLink from '@/components/form/link.vue';
import MkSwitch from '@/components/MkSwitch.vue';
@@ -110,7 +111,6 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { changePluginActive, configPlugin, pluginLogs, uninstallPlugin, reloadPlugin } from '@/plugin.js';
import { prefer } from '@/preferences.js';
-import { isSafeMode } from '@@/js/config.js';
import * as os from '@/os.js';
const plugins = prefer.r.plugins;
diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue
index 972b50f8cd..1a613466db 100644
--- a/packages/frontend/src/pages/settings/preferences.vue
+++ b/packages/frontend/src/pages/settings/preferences.vue
@@ -31,12 +31,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</SearchMarker>
<SearchMarker :keywords="['device', 'type', 'kind', 'smartphone', 'tablet', 'desktop']">
- <MkRadios v-model="overridedDeviceKind">
+ <MkRadios
+ v-model="overridedDeviceKind"
+ :options="[
+ { value: null, label: i18n.ts.auto },
+ { value: 'smartphone', label: i18n.ts.smartphone, icon: 'ti ti-device-mobile' },
+ { value: 'tablet', label: i18n.ts.tablet, icon: 'ti ti-device-tablet' },
+ { value: 'desktop', label: i18n.ts.desktop, icon: 'ti ti-device-desktop' },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.overridedDeviceKind }}</SearchLabel></template>
- <option :value="null">{{ i18n.ts.auto }}</option>
- <option value="smartphone"><i class="ti ti-device-mobile"/> {{ i18n.ts.smartphone }}</option>
- <option value="tablet"><i class="ti ti-device-tablet"/> {{ i18n.ts.tablet }}</option>
- <option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option>
</MkRadios>
</SearchMarker>
@@ -121,11 +125,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['emoji', 'style', 'native', 'system', 'fluent', 'twemoji']">
<MkPreferenceContainer k="emojiStyle">
<div>
- <MkRadios v-model="emojiStyle">
+ <MkRadios
+ v-model="emojiStyle"
+ :options="[
+ { value: 'native', label: i18n.ts.native },
+ { value: 'fluentEmoji', label: 'Fluent Emoji' },
+ { value: 'twemoji', label: 'Twemoji' },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.emojiStyle }}</SearchLabel></template>
- <option value="native">{{ i18n.ts.native }}</option>
- <option value="fluentEmoji">Fluent Emoji</option>
- <option value="twemoji">Twemoji</option>
</MkRadios>
<div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div>
</div>
@@ -240,11 +248,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['reaction', 'size', 'scale', 'display']">
<MkPreferenceContainer k="reactionsDisplaySize">
- <MkRadios v-model="reactionsDisplaySize">
+ <MkRadios
+ v-model="reactionsDisplaySize"
+ :options="[
+ { value: 'small', label: i18n.ts.small },
+ { value: 'medium', label: i18n.ts.medium },
+ { value: 'large', label: i18n.ts.large },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.reactionsDisplaySize }}</SearchLabel></template>
- <option value="small">{{ i18n.ts.small }}</option>
- <option value="medium">{{ i18n.ts.medium }}</option>
- <option value="large">{{ i18n.ts.large }}</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
@@ -259,16 +271,28 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height']">
<MkPreferenceContainer k="mediaListWithOneImageAppearance">
- <MkRadios v-model="mediaListWithOneImageAppearance">
+ <MkRadios
+ v-model="mediaListWithOneImageAppearance"
+ :options="[
+ { value: 'expand', label: i18n.ts.default },
+ { value: '16_9', label: i18n.tsx.limitTo({ x: '16:9' }) },
+ { value: '1_1', label: i18n.tsx.limitTo({ x: '1:1' }) },
+ { value: '2_3', label: i18n.tsx.limitTo({ x: '2:3' }) },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.mediaListWithOneImageAppearance }}</SearchLabel></template>
- <option value="expand">{{ i18n.ts.default }}</option>
- <option value="16_9">{{ i18n.tsx.limitTo({ x: '16:9' }) }}</option>
- <option value="1_1">{{ i18n.tsx.limitTo({ x: '1:1' }) }}</option>
- <option value="2_3">{{ i18n.tsx.limitTo({ x: '2:3' }) }}</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
+ <SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'grid', 'wide', 'area']">
+ <MkPreferenceContainer k="showMediaListByGridInWideArea">
+ <MkSwitch v-model="showMediaListByGridInWideArea">
+ <template #label><SearchLabel>{{ i18n.ts.showMediaListByGridInWideArea }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
<SearchMarker :keywords="['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation']">
<MkPreferenceContainer k="instanceTicker">
<MkSelect
@@ -386,22 +410,30 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['position']">
<MkPreferenceContainer k="notificationPosition">
- <MkRadios v-model="notificationPosition">
+ <MkRadios
+ v-model="notificationPosition"
+ :options="[
+ { value: 'leftTop', label: i18n.ts.leftTop, icon: 'ti ti-align-box-left-top' },
+ { value: 'rightTop', label: i18n.ts.rightTop, icon: 'ti ti-align-box-right-top' },
+ { value: 'leftBottom', label: i18n.ts.leftBottom, icon: 'ti ti-align-box-left-bottom' },
+ { value: 'rightBottom', label: i18n.ts.rightBottom, icon: 'ti ti-align-box-right-bottom' },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.position }}</SearchLabel></template>
- <option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option>
- <option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option>
- <option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option>
- <option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['stack', 'axis', 'direction']">
<MkPreferenceContainer k="notificationStackAxis">
- <MkRadios v-model="notificationStackAxis">
+ <MkRadios
+ v-model="notificationStackAxis"
+ :options="[
+ { value: 'vertical', label: i18n.ts.vertical, icon: 'ti ti-carousel-vertical' },
+ { value: 'horizontal', label: i18n.ts.horizontal, icon: 'ti ti-carousel-horizontal' },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.stackAxis }}</SearchLabel></template>
- <option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
- <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
@@ -570,12 +602,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</SearchMarker>
<SearchMarker :keywords="['font', 'size']">
- <MkRadios v-model="fontSize">
+ <MkRadios
+ v-model="fontSize"
+ :options="[
+ { value: null, label: 'Aa', labelStyle: 'font-size: 14px;' },
+ { value: '1', label: 'Aa', labelStyle: 'font-size: 15px;' },
+ { value: '2', label: 'Aa', labelStyle: 'font-size: 16px;' },
+ { value: '3', label: 'Aa', labelStyle: 'font-size: 17px;' },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.fontSize }}</SearchLabel></template>
- <option :value="null"><span style="font-size: 14px;">Aa</span></option>
- <option value="1"><span style="font-size: 15px;">Aa</span></option>
- <option value="2"><span style="font-size: 16px;">Aa</span></option>
- <option value="3"><span style="font-size: 17px;">Aa</span></option>
</MkRadios>
</SearchMarker>
@@ -784,10 +820,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker>
<MkPreferenceContainer k="hemisphere">
- <MkRadios v-model="hemisphere">
+ <MkRadios
+ v-model="hemisphere"
+ :options="[
+ { value: 'N', label: i18n.ts._hemisphere.N },
+ { value: 'S', label: i18n.ts._hemisphere.S },
+ ]"
+ >
<template #label><SearchLabel>{{ i18n.ts.hemisphere }}</SearchLabel></template>
- <option value="N">{{ i18n.ts._hemisphere.N }}</option>
- <option value="S">{{ i18n.ts._hemisphere.S }}</option>
<template #caption>{{ i18n.ts._hemisphere.caption }}</template>
</MkRadios>
</MkPreferenceContainer>
@@ -855,7 +895,7 @@ const $i = ensureSignin();
const lang = ref(miLocalStorage.getItem('lang'));
const dataSaver = ref(prefer.s.dataSaver);
-const realtimeMode = computed(store.makeGetterSetter('realtimeMode'));
+const realtimeMode = store.model('realtimeMode');
const overridedDeviceKind = prefer.model('overridedDeviceKind');
const pollingInterval = prefer.model('pollingInterval');
@@ -890,6 +930,7 @@ const notificationStackAxis = prefer.model('notificationStackAxis');
const instanceTicker = prefer.model('instanceTicker');
const highlightSensitiveMedia = prefer.model('highlightSensitiveMedia');
const mediaListWithOneImageAppearance = prefer.model('mediaListWithOneImageAppearance');
+const showMediaListByGridInWideArea = prefer.model('showMediaListByGridInWideArea');
const reactionsDisplaySize = prefer.model('reactionsDisplaySize');
const limitWidthOfReaction = prefer.model('limitWidthOfReaction');
const squareAvatars = prefer.model('squareAvatars');
@@ -916,7 +957,7 @@ const contextMenu = prefer.model('contextMenu');
const menuStyle = prefer.model('menuStyle');
const makeEveryTextElementsSelectable = prefer.model('makeEveryTextElementsSelectable');
-const fontSize = ref(miLocalStorage.getItem('fontSize'));
+const fontSize = ref(miLocalStorage.getItem('fontSize') as '1' | '2' | '3' | null);
const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
watch(lang, () => {
@@ -1042,7 +1083,7 @@ function removePinnedList() {
function enableAllDataSaver() {
const g = { ...prefer.s.dataSaver };
- Object.keys(g).forEach((key) => { g[key] = true; });
+ (Object.keys(g) as (keyof typeof g)[]).forEach((key) => { g[key] = true; });
dataSaver.value = g;
}
@@ -1050,7 +1091,7 @@ function enableAllDataSaver() {
function disableAllDataSaver() {
const g = { ...prefer.s.dataSaver };
- Object.keys(g).forEach((key) => { g[key] = false; });
+ (Object.keys(g) as (keyof typeof g)[]).forEach((key) => { g[key] = false; });
dataSaver.value = g;
}
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 7d3da470d6..a7aea9bde4 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -75,30 +75,27 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.metadataRoot" class="_gaps_s">
<MkInfo>{{ i18n.ts._profile.verifiedLinkDescription }}</MkInfo>
- <Sortable
+ <MkDraggable
v-model="fields"
- class="_gaps_s"
- itemKey="id"
- :animation="150"
- :handle="'.' + $style.dragItemHandle"
- @start="e => e.item.classList.add('active')"
- @end="e => e.item.classList.remove('active')"
+ direction="vertical"
+ withGaps
+ manualDragStart
>
- <template #item="{element, index}">
+ <template #default="{ item, dragStart }">
<div v-panel :class="$style.fieldDragItem">
- <button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1"><i class="ti ti-menu"></i></button>
- <button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(index)"><i class="ti ti-x"></i></button>
+ <button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1" :draggable="true" @dragstart.stop="dragStart"><i class="ti ti-menu"></i></button>
+ <button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(item.id)"><i class="ti ti-x"></i></button>
<div :class="$style.dragItemForm">
<FormSplit :minWidth="200">
- <MkInput v-model="element.name" small :placeholder="i18n.ts._profile.metadataLabel">
+ <MkInput v-model="item.name" small :placeholder="i18n.ts._profile.metadataLabel">
</MkInput>
- <MkInput v-model="element.value" small :placeholder="i18n.ts._profile.metadataContent">
+ <MkInput v-model="item.value" small :placeholder="i18n.ts._profile.metadataContent">
</MkInput>
</FormSplit>
</div>
</div>
</template>
- </Sortable>
+ </MkDraggable>
</div>
</MkFolder>
<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
@@ -165,7 +162,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, reactive, ref, watch, defineAsyncComponent } from 'vue';
+import { computed, reactive, ref, watch } from 'vue';
+import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
@@ -174,6 +172,7 @@ import FormSplit from '@/components/form/split.vue';
import MkFolder from '@/components/MkFolder.vue';
import FormSlot from '@/components/form/slot.vue';
import FormLink from '@/components/form/link.vue';
+import MkDraggable from '@/components/MkDraggable.vue';
import { chooseDriveFile } from '@/utility/drive.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
@@ -188,9 +187,7 @@ import { genId } from '@/utility/id.js';
const $i = ensureSignin();
-const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
-
-const reactionAcceptance = computed(store.makeGetterSetter('reactionAcceptance'));
+const reactionAcceptance = store.model('reactionAcceptance');
function assertVaildLang(lang: string | null): lang is keyof typeof langmap {
return lang != null && lang in langmap;
@@ -228,8 +225,8 @@ while (fields.value.length < 4) {
addField();
}
-function deleteField(index: number) {
- fields.value.splice(index, 1);
+function deleteField(itemId: string) {
+ fields.value = fields.value.filter(f => f.id !== itemId);
}
function saveFields() {
@@ -270,8 +267,8 @@ function save() {
}
}
-function changeAvatar(ev) {
- async function done(driveFile) {
+function changeAvatar(ev: PointerEvent) {
+ async function done(driveFile: Misskey.entities.DriveFile) {
const i = await os.apiWithDialog('i/update', {
avatarId: driveFile.id,
});
@@ -319,8 +316,8 @@ function changeAvatar(ev) {
}], ev.currentTarget ?? ev.target);
}
-function changeBanner(ev) {
- async function done(driveFile) {
+function changeBanner(ev: PointerEvent) {
+ async function done(driveFile: Misskey.entities.DriveFile) {
const i = await os.apiWithDialog('i/update', {
bannerId: driveFile.id,
});
diff --git a/packages/frontend/src/pages/settings/profiles.vue b/packages/frontend/src/pages/settings/profiles.vue
index 4804c11f7a..b3d02ba3fe 100644
--- a/packages/frontend/src/pages/settings/profiles.vue
+++ b/packages/frontend/src/pages/settings/profiles.vue
@@ -15,21 +15,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { ref, computed } from 'vue';
-import type { MenuItem } from '@/types/menu.js';
+import { computed } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os.js';
-import { misskeyApi } from '@/utility/misskey-api.js';
-import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { prefer } from '@/preferences.js';
import { deleteCloudBackup, listCloudBackups } from '@/preferences/utility.js';
const backups = await listCloudBackups();
-function del(backup) {
+function del(backup: { name: string }): void {
deleteCloudBackup(backup.name);
}
diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue
index 31fe9a64db..050586c2e1 100644
--- a/packages/frontend/src/pages/settings/sounds.sound.vue
+++ b/packages/frontend/src/pages/settings/sounds.sound.vue
@@ -32,6 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed, watch } from 'vue';
import type { SoundType } from '@/utility/sound.js';
+import type { SoundStore } from '@/preferences/def.js';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import MkRange from '@/components/MkRange.vue';
@@ -41,7 +42,6 @@ import { useMkSelect } from '@/composables/use-mkselect.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { playMisskeySfxFile, soundsTypes, getSoundDuration } from '@/utility/sound.js';
import { selectFile } from '@/utility/drive.js';
-import type { SoundStore } from '@/preferences/def.js';
const props = defineProps<{
def: SoundStore;
@@ -100,7 +100,7 @@ const friendlyFileName = computed<string>(() => {
return i18n.ts._soundSettings.driveFileWarn;
});
-function selectSound(ev) {
+function selectSound(ev: PointerEvent) {
selectFile({
anchorElement: ev.currentTarget ?? ev.target,
multiple: false,
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 1b851825d6..0d0623f11f 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -100,11 +100,14 @@ function getSoundTypeName(f: SoundType): string {
}
}
-async function updated(type: keyof typeof sounds.value, sound) {
- const v: SoundStore = {
+async function updated(type: keyof typeof sounds.value, sound: { type: SoundType; fileId?: string; fileUrl?: string; volume: number; }) {
+ const v: SoundStore = sound.type === '_driveFile_' ? {
+ type: sound.type,
+ fileId: sound.fileId!,
+ fileUrl: sound.fileUrl!,
+ volume: sound.volume,
+ } : {
type: sound.type,
- fileId: sound.fileId,
- fileUrl: sound.fileUrl,
volume: sound.volume,
};
diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
index b69fd2596d..83c8a7b9a7 100644
--- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
@@ -17,13 +17,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>Black</template>
</MkSwitch>
- <MkRadios v-model="statusbar.size">
+ <MkRadios
+ v-model="statusbar.size"
+ :options="[
+ { value: 'verySmall', label: i18n.ts.small + '+' },
+ { value: 'small', label: i18n.ts.small },
+ { value: 'medium', label: i18n.ts.medium },
+ { value: 'large', label: i18n.ts.large },
+ { value: 'veryLarge', label: i18n.ts.large + '+' },
+ ]"
+ >
<template #label>{{ i18n.ts.size }}</template>
- <option value="verySmall">{{ i18n.ts.small }}+</option>
- <option value="small">{{ i18n.ts.small }}</option>
- <option value="medium">{{ i18n.ts.medium }}</option>
- <option value="large">{{ i18n.ts.large }}</option>
- <option value="veryLarge">{{ i18n.ts.large }}+</option>
</MkRadios>
<template v-if="statusbar.type === 'rss'">
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index 0129aebe94..46b537f866 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -306,7 +306,7 @@ function changeThemesSyncEnabled(value: boolean) {
}
}
-function onThemeContextmenu(theme: Theme, ev: MouseEvent) {
+function onThemeContextmenu(theme: Theme, ev: PointerEvent) {
os.contextMenu([{
type: 'label',
text: theme.name,
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index 047e68f583..1e268e64d2 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, markRaw, ref } from 'vue';
+import type { PageHeaderItem } from '@/types/page-header.js';
import MkNotesTimeline from '@/components/MkNotesTimeline.vue';
import MkButton from '@/components/MkButton.vue';
import { definePage } from '@/page.js';
@@ -50,10 +51,10 @@ async function post() {
paginator.reload();
}
-const headerActions = computed(() => [{
+const headerActions = computed<PageHeaderItem[]>(() => [{
icon: 'ti ti-dots',
text: i18n.ts.more,
- handler: (ev: MouseEvent) => {
+ handler: (ev) => {
os.popupMenu([{
text: i18n.ts.embed,
icon: 'ti ti-code',
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index af3891ac8e..2d2b8ed292 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -160,11 +160,11 @@ function setBgColor(color: typeof bgColors[number]) {
}
}
-function setAccentColor(color) {
+function setAccentColor(color: string) {
theme.value.props.accent = color;
}
-function setFgColor(color) {
+function setFgColor(color: typeof fgColors[number]) {
theme.value.props.fg = theme.value.base === 'light' ? color.forLight : color.forDark;
}
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 89d0991bc0..64c2b2eee3 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -31,6 +31,7 @@ import { computed, watch, provide, useTemplateRef, ref, onMounted, onActivated }
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
import type { MenuItem } from '@/types/menu.js';
import type { BasicTimelineType } from '@/timelines.js';
+import type { PageHeaderItem } from '@/types/page-header.js';
import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import MkPostForm from '@/components/MkPostForm.vue';
import * as os from '@/os.js';
@@ -105,7 +106,7 @@ const withSensitive = computed<boolean>({
const showFixedPostForm = prefer.model('showFixedPostForm');
-async function chooseList(ev: MouseEvent): Promise<void> {
+async function chooseList(ev: PointerEvent): Promise<void> {
const lists = await userListsCache.fetch();
const items: (MenuItem | undefined)[] = [
...lists.map(list => ({
@@ -124,7 +125,7 @@ async function chooseList(ev: MouseEvent): Promise<void> {
os.popupMenu(items.filter(i => i != null), ev.currentTarget ?? ev.target);
}
-async function chooseAntenna(ev: MouseEvent): Promise<void> {
+async function chooseAntenna(ev: PointerEvent): Promise<void> {
const antennas = await antennasCache.fetch();
const items: (MenuItem | undefined)[] = [
...antennas.map(antenna => ({
@@ -144,7 +145,7 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> {
os.popupMenu(items.filter(i => i != null), ev.currentTarget ?? ev.target);
}
-async function chooseChannel(ev: MouseEvent): Promise<void> {
+async function chooseChannel(ev: PointerEvent): Promise<void> {
const channels = await favoritedChannelsCache.fetch();
const items: (MenuItem | undefined)[] = [
...channels.map(channel => {
@@ -203,8 +204,8 @@ onActivated(() => {
switchTlIfNeeded();
});
-const headerActions = computed(() => {
- const items = [{
+const headerActions = computed<PageHeaderItem[]>(() => {
+ const items: PageHeaderItem[] = [{
icon: 'ti ti-dots',
text: i18n.ts.options,
handler: (ev) => {
@@ -254,7 +255,7 @@ const headerActions = computed(() => {
items.unshift({
icon: 'ti ti-refresh',
text: i18n.ts.reload,
- handler: (ev: Event) => {
+ handler: () => {
tlComponent.value?.reloadTimeline();
},
});
diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue
index ec4c854381..75519f2850 100644
--- a/packages/frontend/src/pages/user-tag.vue
+++ b/packages/frontend/src/pages/user-tag.vue
@@ -25,6 +25,7 @@ const props = defineProps<{
const paginator = markRaw(new Paginator('hashtags/users', {
limit: 30,
+ offsetMode: true,
computedParams: computed(() => ({
tag: props.tag,
origin: 'combined',
diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue
index 4310c7ad85..f9a2eed6b9 100644
--- a/packages/frontend/src/pages/user/activity.following.vue
+++ b/packages/frontend/src/pages/user/activity.following.vue
@@ -57,7 +57,7 @@ async function renderChart() {
return new Date(y, m, d - ago);
};
- const format = (arr) => {
+ const format = (arr: number[]) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue
index 6d9c1bedd9..00bfe25430 100644
--- a/packages/frontend/src/pages/user/activity.notes.vue
+++ b/packages/frontend/src/pages/user/activity.notes.vue
@@ -57,7 +57,7 @@ async function renderChart() {
return new Date(y, m, d - ago);
};
- const format = (arr) => {
+ const format = (arr: number[]) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue
index 76df53becd..451f8ba0f7 100644
--- a/packages/frontend/src/pages/user/activity.pv.vue
+++ b/packages/frontend/src/pages/user/activity.pv.vue
@@ -57,7 +57,7 @@ async function renderChart() {
return new Date(y, m, d - ago);
};
- const format = (arr) => {
+ const format = (arr: number[]) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index b61c84cbbc..64b03bc4bc 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="user.isLocked"><i class="ti ti-lock"></i></span>
<span v-if="user.isBot"><i class="ti ti-robot"></i></span>
<button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea">
- <i class="ti ti-edit"/> {{ i18n.ts.addMemo }}
+ <i class="ti ti-edit"></i> {{ i18n.ts.addMemo }}
</button>
</div>
</div>
@@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}">
- <div class="heading" v-text="i18n.ts.memo"/>
+ <div class="heading">{{ i18n.ts.memo }}</div>
<textarea
ref="memoTextareaEl"
v-model="memoDraft"
@@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@focus="isEditingMemo = true"
@blur="updateMemo"
@input="adjustMemoTextarea"
- />
+ ></textarea>
</div>
<div class="description">
<MkOmit>
@@ -186,6 +186,7 @@ import { getStaticImageUrl } from '@/utility/media-proxy.js';
import MkSparkle from '@/components/MkSparkle.vue';
import { prefer } from '@/preferences.js';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
+import { isBirthday } from '@/utility/is-birthday.js';
function calcAge(birthdate: string): number {
const date = new Date(birthdate);
@@ -251,7 +252,7 @@ const age = computed(() => {
return props.user.birthday ? calcAge(props.user.birthday) : NaN;
});
-function menu(ev: MouseEvent) {
+function menu(ev: PointerEvent) {
const { menu, cleanup } = getUserMenu(user.value, router);
os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
}
@@ -319,16 +320,10 @@ function disposeBannerParallaxResizeObserver() {
onMounted(() => {
narrow.value = rootEl.value!.clientWidth < 1000;
- if (props.user.birthday) {
- const m = new Date().getMonth() + 1;
- const d = new Date().getDate();
- const bm = parseInt(props.user.birthday.split('-')[1]);
- const bd = parseInt(props.user.birthday.split('-')[2]);
- if (m === bm && d === bd) {
- confetti({
- duration: 1000 * 4,
- });
- }
+ if (isBirthday(user.value)) {
+ confetti({
+ duration: 1000 * 4,
+ });
}
nextTick(() => {
diff --git a/packages/frontend/src/pages/user/index.activity.vue b/packages/frontend/src/pages/user/index.activity.vue
index 210021618e..10b0582143 100644
--- a/packages/frontend/src/pages/user/index.activity.vue
+++ b/packages/frontend/src/pages/user/index.activity.vue
@@ -36,7 +36,7 @@ const props = withDefaults(defineProps<{
const chartSrc = ref<'per-user-notes' | 'per-user-pv'>('per-user-notes');
-function showMenu(ev: MouseEvent) {
+function showMenu(ev: PointerEvent) {
os.popupMenu([{
text: i18n.ts.notes,
active: chartSrc.value === 'per-user-notes',
diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue
index 58f6b0ca45..1523e99453 100644
--- a/packages/frontend/src/pages/user/index.files.vue
+++ b/packages/frontend/src/pages/user/index.files.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkButton rounded full @click="emit('showMore')">{{ i18n.ts.showMore }} <i class="ti ti-arrow-right"></i></MkButton>
</div>
- <p v-if="!fetching && notes.length == 0" :class="$style.empty">{{ i18n.ts.nothing }}</p>
+ <p v-if="!fetching && notes.length == 0">{{ i18n.ts.nothing }}</p>
</div>
</MkContainer>
</template>
diff --git a/packages/frontend/src/pages/user/notes.vue b/packages/frontend/src/pages/user/notes.vue
index 1e6dba73bd..137c6cb872 100644
--- a/packages/frontend/src/pages/user/notes.vue
+++ b/packages/frontend/src/pages/user/notes.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div class="_spacer" style="--MI_SPACER-w: 800px;">
- <div :class="$style.root">
+ <div>
<MkStickyContainer>
<template #header>
<MkTab
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 393ba98d30..3a4a558605 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -89,7 +89,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<Suspense>
<template #default>
- <MkServerSetupWizard :token="token" @finished="onWizardFinished"/>
+ <MkServerSetupWizard :token="token!" @finished="onWizardFinished"/>
</template>
<template #fallback>
<MkLoading/>
@@ -124,8 +124,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, ref } from 'vue';
-import * as Misskey from 'misskey-js';
+import { ref } from 'vue';
import { host, version } from '@@/js/config.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
@@ -143,7 +142,7 @@ const accountCreating = ref(false);
const accountCreated = ref(false);
const step = ref(0);
-let token;
+let token: string | null = null;
function createAccount() {
if (accountCreating.value) return;
@@ -191,6 +190,7 @@ function skipSettings() {
}
function finish() {
+ if (token == null) return;
login(token);
}
</script>
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index f32c991828..a41d0bc217 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -26,7 +26,7 @@ export type Plugin = {
version: string;
author?: string;
description?: string;
- permissions?: string[];
+ permissions?: (typeof Misskey.permissions)[number][];
};
export type AiScriptPluginMeta = {
@@ -34,7 +34,7 @@ export type AiScriptPluginMeta = {
version: string;
author: string;
description?: string;
- permissions?: string[];
+ permissions?: (typeof Misskey.permissions)[number][];
config?: Record<string, any>;
};
@@ -49,7 +49,7 @@ async function getParser(): Promise<Parser> {
export function isSupportedAiScriptVersion(version: string): boolean {
try {
return (compareVersions(version, '0.12.0') >= 0);
- } catch (err) {
+ } catch (_) {
return false;
}
}
@@ -72,7 +72,7 @@ export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta>
try {
const parser = await getParser();
ast = parser.parse(code);
- } catch (err) {
+ } catch (_) {
throw new Error('Aiscript syntax error');
}
@@ -97,7 +97,7 @@ export async function parsePluginMeta(code: string): Promise<AiScriptPluginMeta>
version: version as string,
author: author as string,
description: description as string | undefined,
- permissions: permissions as string[] | undefined,
+ permissions: permissions as (typeof Misskey.permissions)[number][] | undefined,
config: config as Record<string, any> | undefined,
};
}
@@ -106,8 +106,9 @@ export async function authorizePlugin(plugin: Plugin) {
if (plugin.permissions == null || plugin.permissions.length === 0) return;
if (Object.hasOwn(store.s.pluginTokens, plugin.installId)) return;
- const token = await new Promise<string>(async (res, rej) => {
- const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkTokenGenerateWindow.vue').then(x => x.default), {
+ const token = await new Promise<string>((res, rej) => {
+ let dispose: () => void;
+ os.popupAsyncWithDialog(import('@/components/MkTokenGenerateWindow.vue').then(x => x.default), {
title: i18n.ts.tokenRequested,
information: i18n.ts.pluginTokenRequestedDescription,
initialName: plugin.name,
@@ -123,7 +124,7 @@ export async function authorizePlugin(plugin: Plugin) {
res(token);
},
closed: () => dispose(),
- });
+ }).then(d => dispose = d.dispose, err => rej(err));
});
store.set('pluginTokens', {
diff --git a/packages/frontend/src/pref-migrate.ts b/packages/frontend/src/pref-migrate.ts
index 8258bbb846..bf86f6954e 100644
--- a/packages/frontend/src/pref-migrate.ts
+++ b/packages/frontend/src/pref-migrate.ts
@@ -5,13 +5,14 @@
import type { DeckProfile } from '@/deck.js';
import { genId } from '@/utility/id.js';
-import { ColdDeviceStorage, store } from '@/store.js';
+import { store } from '@/store.js';
import { prefer } from '@/preferences.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { deckStore } from '@/ui/deck/deck-store.js';
import { unisonReload } from '@/utility/unison-reload.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
+import type { SoundStore } from '@/preferences/def.js';
// TODO: そのうち消す
export function migrateOldSettings() {
@@ -24,16 +25,6 @@ export function migrateOldSettings() {
}
});
- const plugins = ColdDeviceStorage.get('plugins');
- prefer.commit('plugins', plugins.map(p => {
- const { id, ...rest } = p;
- return {
- ...rest,
- config: rest.config ?? {},
- installId: id,
- };
- }));
-
prefer.commit('deck.profile', deckStore.s.profile);
misskeyApi('i/registry/keys', {
scope: ['client', 'deck', 'profiles'],
@@ -54,9 +45,6 @@ export function migrateOldSettings() {
prefer.commit('deck.profiles', profiles);
});
- prefer.commit('lightTheme', ColdDeviceStorage.get('lightTheme'));
- prefer.commit('darkTheme', ColdDeviceStorage.get('darkTheme'));
- prefer.commit('syncDeviceDarkMode', ColdDeviceStorage.get('syncDeviceDarkMode'));
prefer.commit('emojiPalettes', [{
id: 'reactions',
name: '',
@@ -139,10 +127,10 @@ export function migrateOldSettings() {
prefer.commit('sound.masterVolume', store.s.sound_masterVolume);
prefer.commit('sound.notUseSound', store.s.sound_notUseSound);
prefer.commit('sound.useSoundOnlyWhenActive', store.s.sound_useSoundOnlyWhenActive);
- prefer.commit('sound.on.note', store.s.sound_note as any);
- prefer.commit('sound.on.noteMy', store.s.sound_noteMy as any);
- prefer.commit('sound.on.notification', store.s.sound_notification as any);
- prefer.commit('sound.on.reaction', store.s.sound_reaction as any);
+ prefer.commit('sound.on.note', store.s.sound_note as SoundStore);
+ prefer.commit('sound.on.noteMy', store.s.sound_noteMy as SoundStore);
+ prefer.commit('sound.on.notification', store.s.sound_notification as SoundStore);
+ prefer.commit('sound.on.reaction', store.s.sound_reaction as SoundStore);
prefer.commit('defaultNoteVisibility', store.s.defaultNoteVisibility);
prefer.commit('defaultNoteLocalOnly', store.s.defaultNoteLocalOnly);
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index 8d1d33977d..b33b53b83c 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -237,7 +237,7 @@ export const PREF_DEF = definePreferences({
default: false,
},
emojiStyle: {
- default: 'twemoji', // twemoji / fluentEmoji / native
+ default: 'twemoji' as 'native' | 'fluentEmoji' | 'twemoji',
},
menuStyle: {
default: 'auto' as 'auto' | 'popup' | 'drawer',
@@ -320,6 +320,9 @@ export const PREF_DEF = definePreferences({
mediaListWithOneImageAppearance: {
default: 'expand' as 'expand' | '16_9' | '1_1' | '2_3',
},
+ showMediaListByGridInWideArea: {
+ default: false,
+ },
notificationPosition: {
default: 'rightBottom' as 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom',
},
@@ -500,7 +503,7 @@ export const PREF_DEF = definePreferences({
default: true,
},
'deck.columnAlign': {
- default: 'center' as 'left' | 'right' | 'center',
+ default: 'center' as 'left' | 'center',
},
'deck.columnGap': {
default: 6,
diff --git a/packages/frontend/src/preferences/manager.ts b/packages/frontend/src/preferences/manager.ts
index 13ba0000e4..7f3949f81b 100644
--- a/packages/frontend/src/preferences/manager.ts
+++ b/packages/frontend/src/preferences/manager.ts
@@ -3,11 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { computed, onUnmounted, ref, watch } from 'vue';
+import { customRef, ref, watch, onScopeDispose } from 'vue';
import { EventEmitter } from 'eventemitter3';
import { host, version } from '@@/js/config.js';
import { PREF_DEF } from './def.js';
-import type { Ref, WritableComputedRef } from 'vue';
+import type { Ref } from 'vue';
import type { MenuItem } from '@/types/menu.js';
import { genId } from '@/utility/id.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
@@ -81,7 +81,7 @@ export type PreferencesProfile = {
};
export type PossiblyNonNormalizedPreferencesProfile = Omit<PreferencesProfile, 'preferences'> & {
- preferences: Record<string, any>;
+ preferences: Record<string, [scope: Scope, value: any, meta: ValueMeta][]>;
};
export type StorageProvider = {
@@ -112,17 +112,17 @@ type PreferencesManagerEvents = {
export function definePreferences<T extends Record<string, unknown>>(x: {
[K in keyof T]: PreferencesDefinitionRecord<T[K]>
}): {
- [K in keyof T]: PreferencesDefinitionRecord<T[K]>
- } {
+ [K in keyof T]: PreferencesDefinitionRecord<T[K]>
+} {
return x;
}
export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> {
- const _default = PREF_DEF[k as string].default;
+ const _default = PREF_DEF[k].default;
if (typeof _default === 'function') { // factory
- return _default();
+ return _default() as ValueOf<K>;
} else {
- return _default;
+ return _default as unknown as ValueOf<K>;
}
}
@@ -146,7 +146,7 @@ function createEmptyProfile(): PossiblyNonNormalizedPreferencesProfile {
}
function normalizePreferences(preferences: PossiblyNonNormalizedPreferencesProfile['preferences'], account: { id: string } | null): PreferencesProfile['preferences'] {
- const data = {} as PreferencesProfile['preferences'];
+ const data = {} as Record<string, [scope: Scope, value: any, meta: ValueMeta][]>;
for (const key in PREF_DEF) {
const records = preferences[key];
if (records == null || records.length === 0) {
@@ -183,7 +183,7 @@ function normalizePreferences(preferences: PossiblyNonNormalizedPreferencesProfi
}
}
- return data;
+ return data as PreferencesProfile['preferences'];
}
// TODO: PreferencesManagerForGuest のような非ログイン専用のクラスを分離すればthis.currentAccountのnullチェックやaccountがnullであるスコープのレコード挿入などが不要になり綺麗になるかもしれない
@@ -223,9 +223,10 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
const states = this.genStates();
+ // apply states
for (const key in states) {
- this.s[key] = states[key];
- this.r[key] = ref(this.s[key]);
+ (this.s[key as keyof PREF] as any) = states[key as keyof PREF];
+ (this.r[key as keyof PREF] as Ref<any>) = ref(this.s[key as keyof PREF]);
}
// normalizeの結果変わっていたら保存
@@ -299,36 +300,39 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
* 特定のキーの、簡易的なcomputed refを作ります
* 主にvue上で設定コントロールのmodelとして使う用
*/
- public model<K extends keyof PREF, V extends ValueOf<K> = ValueOf<K>>(
+ public model<K extends keyof PREF, V = ValueOf<K>>(
+ key: K,
+ ): Ref<V>;
+ public model<K extends keyof PREF, V extends Exclude<any, ValueOf<K>>>(
+ key: K,
+ getter: (v: ValueOf<K>) => V,
+ setter: (v: V) => ValueOf<K>,
+ ): Ref<V>;
+
+ public model<K extends keyof PREF, V>(
key: K,
getter?: (v: ValueOf<K>) => V,
setter?: (v: V) => ValueOf<K>,
- ): WritableComputedRef<V> {
- const valueRef = ref(this.s[key]);
-
- const stop = watch(this.r[key], val => {
- valueRef.value = val;
- });
+ ): Ref<V> {
+ return customRef<V>((track, trigger) => {
+ const watchStop = watch(this.r[key], () => {
+ trigger();
+ });
- // NOTE: vueコンポーネント内で呼ばれない限りは、onUnmounted は無意味なのでメモリリークする
- onUnmounted(() => {
- stop();
- });
+ onScopeDispose(() => {
+ watchStop();
+ }, true);
- // TODO: VueのcustomRef使うと良い感じになるかも
- return computed({
- get: () => {
- if (getter) {
- return getter(valueRef.value);
- } else {
- return valueRef.value;
- }
- },
- set: (value) => {
- const val = setter ? setter(value) : value;
- this.commit(key, val);
- valueRef.value = val;
- },
+ return {
+ get: () => {
+ track();
+ return (getter != null ? getter(this.s[key]) : this.s[key]) as V;
+ },
+ set: (value) => {
+ const val = setter != null ? setter(value) : value;
+ this.commit(key, val as ValueOf<K>);
+ },
+ };
});
}
@@ -460,7 +464,7 @@ export class PreferencesManager extends EventEmitter<PreferencesManagerEvents> {
let mergedValue: ValueOf<K> | undefined = undefined; // null と区別したいため
try {
if (merge != null) mergedValue = merge(local, remote);
- } catch (err) {
+ } catch (_) {
// nop
}
const { canceled, result: choice } = await os.select({
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index fb9349c42f..e7cdc98415 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -3,16 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { markRaw, ref } from 'vue';
+import { markRaw } from 'vue';
import * as Misskey from 'misskey-js';
-import lightTheme from '@@/themes/l-light.json5';
-import darkTheme from '@@/themes/d-green-lime.json5';
import { prefersReducedMotion } from '@@/js/config.js';
import { hemisphere } from '@@/js/intl-const.js';
import type { DeviceKind } from '@/utility/device-kind.js';
-import type { Plugin } from '@/plugin.js';
import type { TIPS } from '@/tips.js';
-import { miLocalStorage } from '@/local-storage.js';
import { Pizzax } from '@/lib/pizzax.js';
import { DEFAULT_DEVICE_KIND } from '@/utility/device-kind.js';
@@ -83,7 +79,7 @@ export const store = markRaw(new Pizzax('base', {
},
menuDisplay: {
where: 'device',
- default: 'sideFull' as 'sideFull' | 'sideIcon' | 'top',
+ default: 'sideFull' as 'sideFull' | 'sideIcon'/* | 'top' */,
},
postFormWithHashtags: {
where: 'device',
@@ -257,7 +253,7 @@ export const store = markRaw(new Pizzax('base', {
},
emojiStyle: {
where: 'device',
- default: 'twemoji', // twemoji / fluentEmoji / native
+ default: 'twemoji' as 'twemoji' | 'fluentEmoji' | 'native',
},
menuStyle: {
where: 'device',
@@ -478,89 +474,3 @@ interface Watcher {
key: string;
callback: (value: unknown) => void;
}
-
-// TODO: 消す(preferに移行済みのため)
-/**
- * 常にメモリにロードしておく必要がないような設定情報を保管するストレージ(非リアクティブ)
- */
-export class ColdDeviceStorage {
- public static default = {
- lightTheme, // TODO: 消す(preferに移行済みのため)
- darkTheme, // TODO: 消す(preferに移行済みのため)
- syncDeviceDarkMode: true, // TODO: 消す(preferに移行済みのため)
- plugins: [] as (Omit<Plugin, 'installId'> & { id: string })[], // TODO: 消す(preferに移行済みのため)
- };
-
- public static watchers: Watcher[] = [];
-
- public static get<T extends keyof typeof ColdDeviceStorage.default>(key: T): typeof ColdDeviceStorage.default[T] {
- // TODO: indexedDBにする
- // ただしその際はnullチェックではなくキー存在チェックにしないとダメ
- // (indexedDBはnullを保存できるため、ユーザーが意図してnullを格納した可能性がある)
- const value = miLocalStorage.getItem(`${PREFIX}${key}`);
- if (value == null) {
- return ColdDeviceStorage.default[key];
- } else {
- return JSON.parse(value);
- }
- }
-
- public static getAll(): Partial<typeof this.default> {
- return (Object.keys(this.default) as (keyof typeof this.default)[]).reduce<Partial<typeof this.default>>((acc, key) => {
- const value = localStorage.getItem(PREFIX + key);
- if (value != null) {
- acc[key] = JSON.parse(value);
- }
- return acc;
- }, {});
- }
-
- public static set<T extends keyof typeof ColdDeviceStorage.default>(key: T, value: typeof ColdDeviceStorage.default[T]): void {
- // 呼び出し側のバグ等で undefined が来ることがある
- // undefined を文字列として miLocalStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視
-
- if (value === undefined) {
- console.error(`attempt to store undefined value for key '${key}'`);
- return;
- }
-
- miLocalStorage.setItem(`${PREFIX}${key}`, JSON.stringify(value));
-
- for (const watcher of this.watchers) {
- if (watcher.key === key) watcher.callback(value);
- }
- }
-
- public static watch(key, callback) {
- this.watchers.push({ key, callback });
- }
-
- // TODO: VueのcustomRef使うと良い感じになるかも
- public static ref<T extends keyof typeof ColdDeviceStorage.default>(key: T) {
- const v = ColdDeviceStorage.get(key);
- const r = ref(v);
- // TODO: このままではwatcherがリークするので開放する方法を考える
- this.watch(key, v => {
- r.value = v;
- });
- return r;
- }
-
- /**
- * 特定のキーの、簡易的なgetter/setterを作ります
- * 主にvue場で設定コントロールのmodelとして使う用
- */
- public static makeGetterSetter<K extends keyof typeof ColdDeviceStorage.default>(key: K) {
- // TODO: VueのcustomRef使うと良い感じになるかも
- const valueRef = ColdDeviceStorage.ref(key);
- return {
- get: () => {
- return valueRef.value;
- },
- set: (value: typeof ColdDeviceStorage.default[K]) => {
- const val = value;
- ColdDeviceStorage.set(key, val);
- },
- };
- }
-}
diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts
index adbde3fee2..4e6917e0af 100644
--- a/packages/frontend/src/stream.ts
+++ b/packages/frontend/src/stream.ts
@@ -18,7 +18,6 @@ let lastHeartbeatCall = 0;
export function useStream(): Misskey.IStream {
if (stream) return stream;
- // TODO: No Websocketモードもここで判定
stream = markRaw(new Misskey.Stream(wsOrigin, $i ? {
token: $i.token,
} : null));
diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts
index e001bed8f3..af47402bd6 100644
--- a/packages/frontend/src/theme.ts
+++ b/packages/frontend/src/theme.ts
@@ -204,7 +204,7 @@ export function compile(theme: Theme): Record<string, string> {
return tinycolor(val);
}
- const props = {};
+ const props = {} as Record<string, string>;
for (const [k, v] of Object.entries(theme.props)) {
if (k.startsWith('$')) continue; // ignore const
@@ -232,7 +232,7 @@ export function parseThemeCode(code: string): Theme {
try {
theme = JSON5.parse(code);
- } catch (err) {
+ } catch (_) {
throw new Error('Failed to parse theme json');
}
if (!validateTheme(theme)) {
@@ -247,12 +247,12 @@ export function parseThemeCode(code: string): Theme {
export function previewTheme(code: string): void {
const theme = parseThemeCode(code);
- if (theme) applyTheme(theme, false);
+ if (theme != null) applyTheme(theme, false);
}
export async function installTheme(code: string): Promise<void> {
const theme = parseThemeCode(code);
- if (!theme) return;
+ if (theme == null) return;
await addTheme(theme);
}
diff --git a/packages/frontend/src/tips.ts b/packages/frontend/src/tips.ts
index 6ee7130ee9..d5fc047c2b 100644
--- a/packages/frontend/src/tips.ts
+++ b/packages/frontend/src/tips.ts
@@ -32,7 +32,7 @@ export function resetAllTips() {
}
export function hideAllTips() {
- const v = {};
+ const v = {} as Record<typeof TIPS[number], boolean>;
for (const k of TIPS) {
v[k] = true;
}
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index e0bf135ef8..05fb3034f0 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -6,14 +6,15 @@
import * as Misskey from 'misskey-js';
import type { Component, ComputedRef, Ref, MaybeRef } from 'vue';
import type { ComponentProps as CP } from 'vue-component-type-helpers';
+import type { OptionValue } from '@/types/option-value.js';
type ComponentProps<T extends Component> = { [K in keyof CP<T>]: MaybeRef<CP<T>[K]> };
-type MenuRadioOptionsDef = Record<string, any>;
+type MenuRadioOptionsDef = Record<string, OptionValue>;
type Text = string | ComputedRef<string>;
-export type MenuAction = (ev: MouseEvent) => void;
+export type MenuAction = (ev: PointerEvent) => void;
export interface MenuButton {
type?: 'button';
diff --git a/packages/frontend/src/types/option-value.ts b/packages/frontend/src/types/option-value.ts
new file mode 100644
index 0000000000..98d0fb5faa
--- /dev/null
+++ b/packages/frontend/src/types/option-value.ts
@@ -0,0 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export type OptionValue = string | number | null;
diff --git a/packages/frontend/src/types/overload-to-union.ts b/packages/frontend/src/types/overload-to-union.ts
new file mode 100644
index 0000000000..3cf16a5f3c
--- /dev/null
+++ b/packages/frontend/src/types/overload-to-union.ts
@@ -0,0 +1,31 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+type FlattenAndDedup<T> = T extends (...args: infer A) => infer R ? (...args: A) => R : never;
+
+// 10個で足りなかった場合は増やす
+export type OverloadToUnion<T> = FlattenAndDedup<T extends {
+ (...args: infer A1): infer R1;
+ (...args: infer A2): infer R2;
+ (...args: infer A3): infer R3;
+ (...args: infer A4): infer R4;
+ (...args: infer A5): infer R5;
+ (...args: infer A6): infer R6;
+ (...args: infer A7): infer R7;
+ (...args: infer A8): infer R8;
+ (...args: infer A9): infer R9;
+ (...args: infer A10): infer R10;
+} ? (
+ ((...args: A1) => R1) |
+ ((...args: A2) => R2) |
+ ((...args: A3) => R3) |
+ ((...args: A4) => R4) |
+ ((...args: A5) => R5) |
+ ((...args: A6) => R6) |
+ ((...args: A7) => R7) |
+ ((...args: A8) => R8) |
+ ((...args: A9) => R9) |
+ ((...args: A10) => R10)
+) : never>;
diff --git a/packages/frontend/src/types/page-header.ts b/packages/frontend/src/types/page-header.ts
index 7232f17d47..fcfe9d284c 100644
--- a/packages/frontend/src/types/page-header.ts
+++ b/packages/frontend/src/types/page-header.ts
@@ -7,5 +7,6 @@ export type PageHeaderItem = {
text?: string;
icon: string;
highlighted?: boolean;
- handler: (ev: MouseEvent) => void;
+ danger?: boolean;
+ handler: (ev: PointerEvent) => void;
};
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index a9ad36c97a..7ad18fc2a8 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -50,7 +50,7 @@ function toolsMenuItems(): MenuItem[] {
return items;
}
-export function openInstanceMenu(ev: MouseEvent) {
+export function openInstanceMenu(ev: PointerEvent) {
const menuItems: MenuItem[] = [];
menuItems.push({
@@ -175,7 +175,7 @@ export function openInstanceMenu(ev: MouseEvent) {
});
}
-export function openToolsMenu(ev: MouseEvent) {
+export function openToolsMenu(ev: PointerEvent) {
os.popupMenu(toolsMenuItems(), ev.currentTarget ?? ev.target, {
align: 'left',
});
diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue
index 64da4647b6..eb1d99f0aa 100644
--- a/packages/frontend/src/ui/_common_/navbar-h.vue
+++ b/packages/frontend/src/ui/_common_/navbar-h.vue
@@ -6,22 +6,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="[$style.root, acrylic ? $style.acrylic : null]">
<div :class="$style.body">
- <div :class="$style.left">
+ <div>
<button v-click-anime :class="[$style.item, $style.instance]" class="_button" @click="openInstanceMenu">
<img :class="$style.instanceIcon" :src="instance.iconUrl ?? '/favicon.ico'" draggable="false"/>
</button>
- <MkA v-click-anime v-tooltip="i18n.ts.timeline" :class="$style.item" activeClass="active" to="/" exact>
+ <MkA v-click-anime v-tooltip="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact>
<i :class="$style.itemIcon" class="ti ti-home ti-fw"></i>
</MkA>
<template v-for="item in menu">
<div v-if="item === '-'" :class="$style.divider"></div>
- <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="_button" :class="$style.item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
+ <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show == null || navbarItemDef[item].show.value !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="_button" :class="$style.item" :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i :class="[$style.itemIcon, navbarItemDef[item].icon]" class="ti-fw"></i>
<span v-if="navbarItemDef[item].indicated" :class="$style.indicator" class="_blink"><i class="_indicatorCircle"></i></span>
</component>
</template>
<div :class="$style.divider"></div>
- <MkA v-if="$i && ($i.isAdmin || $i.isModerator)" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
+ <MkA v-if="$i && ($i.isAdmin || $i.isModerator)" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" :activeClass="$style.active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
<i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i>
</MkA>
<button v-click-anime :class="$style.item" class="_button" @click="more">
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</button>
</div>
<div :class="$style.right">
- <MkA v-click-anime v-tooltip="i18n.ts.settings" :class="$style.item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
+ <MkA v-click-anime v-tooltip="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
<i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i>
</MkA>
<button v-if="$i" v-click-anime :class="[$style.item, $style.account]" class="_button" @click="openAccountMenu">
@@ -67,7 +67,7 @@ const props = defineProps<{
const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
const menu = ref(prefer.s.menu);
-// const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
+// const menuDisplay = store.model('menuDisplay');
const otherNavItemIndicated = computed<boolean>(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;
@@ -76,7 +76,7 @@ const otherNavItemIndicated = computed<boolean>(() => {
return false;
});
-async function more(ev: MouseEvent) {
+async function more(ev: PointerEvent) {
const target = getHTMLElementOrNull(ev.currentTarget ?? ev.target);
if (!target) return;
@@ -88,7 +88,7 @@ async function more(ev: MouseEvent) {
});
}
-async function openAccountMenu(ev: MouseEvent) {
+async function openAccountMenu(ev: PointerEvent) {
const menuItems = await getAccountMenu({
withExtraOperation: true,
});
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index c679ee7a92..d0c183763a 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.body">
<div :class="$style.top">
<button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu">
- <img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon" style="viewTransitionName: navbar-serverIcon;"/>
+ <img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon" style="view-transition-name: navbar-serverIcon;"/>
</button>
<button v-if="!iconOnly" v-tooltip.noDelay.right="i18n.ts.realtimeMode" class="_button" :class="[$style.realtimeMode, store.r.realtimeMode.value ? $style.on : null]" @click="toggleRealtimeMode">
<i v-if="store.r.realtimeMode.value" class="ti ti-bolt ti-fw"></i>
@@ -20,16 +20,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="$style.middle">
<MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact>
- <i :class="$style.itemIcon" class="ti ti-home ti-fw" style="viewTransitionName: navbar-homeIcon;"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span>
+ <i :class="$style.itemIcon" class="ti ti-home ti-fw" style="view-transition-name: navbar-homeIcon;"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span>
</MkA>
<template v-for="item in prefer.r.menu.value">
<div v-if="item === '-'" :class="$style.divider"></div>
<component
:is="navbarItemDef[item].to ? 'MkA' : 'button'"
- v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)"
+ v-else-if="navbarItemDef[item] && (navbarItemDef[item].show == null || navbarItemDef[item].show.value !== false)"
v-tooltip.noDelay.right="navbarItemDef[item].title"
class="_button"
- :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]"
+ :class="[$style.item]"
:activeClass="$style.active"
:to="navbarItemDef[item].to"
v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"
@@ -43,14 +43,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div :class="$style.divider"></div>
<MkA v-if="$i != null && ($i.isAdmin || $i.isModerator)" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="$style.item" :activeClass="$style.active" to="/admin">
- <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw" style="viewTransitionName: navbar-controlPanel;"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span>
+ <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw" style="view-transition-name: navbar-controlPanel;"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span>
</MkA>
<button class="_button" :class="$style.item" @click="more">
- <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw" style="viewTransitionName: navbar-more;"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span>
+ <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw" style="view-transition-name: navbar-more;"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span>
<span v-if="otherMenuItemIndicated" :class="$style.itemIndicator" class="_blink"><i class="_indicatorCircle"></i></span>
</button>
<MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" to="/settings">
- <i :class="$style.itemIcon" class="ti ti-settings ti-fw" style="viewTransitionName: navbar-settings;"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span>
+ <i :class="$style.itemIcon" class="ti ti-settings ti-fw" style="view-transition-name: navbar-settings;"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span>
</MkA>
</div>
<div :class="$style.bottom">
@@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span>
</button>
<button v-if="$i != null" v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="_button" :class="[$style.account]" @click="openAccountMenu">
- <MkAvatar :user="$i" :class="$style.avatar" style="viewTransitionName: navbar-avatar;"/><MkAcct class="_nowrap" :class="$style.acct" :user="$i"/>
+ <MkAvatar :user="$i" :class="$style.avatar" style="view-transition-name: navbar-avatar;"/><MkAcct class="_nowrap" :class="$style.acct" :user="$i"/>
</button>
</div>
</div>
@@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<div v-if="!forceIconOnly && prefer.r.showNavbarSubButtons.value" :class="$style.subButtons">
- <div :class="[$style.subButton, $style.menuEditButton]">
+ <div :class="$style.subButton">
<svg viewBox="0 0 16 64" :class="$style.subButtonShape">
<g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)">
<path d="M47.488,7.995C47.79,10.11 47.943,12.266 47.943,14.429C47.997,26.989 47.997,84 47.997,84C47.997,84 44.018,118.246 23.997,133.5C-0.374,152.07 -0.003,192 -0.003,192L-0.003,-96C-0.003,-96 0.151,-56.216 23.997,-37.5C40.861,-24.265 46.043,-1.243 47.488,7.995Z" style="fill:var(--MI_THEME-navBg);"/>
@@ -90,7 +90,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-if="!props.asDrawer">
<div :class="$style.subButtonGapFill"></div>
<div :class="$style.subButtonGapFillDivider"></div>
- <div :class="[$style.subButton, $style.toggleButton]">
+ <div :class="$style.subButton">
<svg viewBox="0 0 16 64" :class="$style.subButtonShape">
<g transform="matrix(0.333333,0,0,0.222222,0.000895785,21.3333)">
<path d="M47.488,7.995C47.79,10.11 47.943,12.266 47.943,14.429C47.997,26.989 47.997,84 47.997,84C47.997,84 44.018,118.246 23.997,133.5C-0.374,152.07 -0.003,192 -0.003,192L-0.003,-96C-0.003,-96 0.151,-56.216 23.997,-37.5C40.861,-24.265 46.043,-1.243 47.488,7.995Z" style="fill:var(--MI_THEME-navBg);"/>
@@ -161,7 +161,7 @@ function toggleIconOnly() {
}
}
-function toggleRealtimeMode(ev: MouseEvent) {
+function toggleRealtimeMode(ev: PointerEvent) {
os.popupMenu([{
type: 'label',
text: i18n.ts.realtimeMode,
@@ -175,7 +175,7 @@ function toggleRealtimeMode(ev: MouseEvent) {
}], ev.currentTarget ?? ev.target);
}
-async function openAccountMenu(ev: MouseEvent) {
+async function openAccountMenu(ev: PointerEvent) {
const menuItems = await getAccountMenu({
withExtraOperation: true,
});
@@ -183,7 +183,7 @@ async function openAccountMenu(ev: MouseEvent) {
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
}
-async function more(ev: MouseEvent) {
+async function more(ev: PointerEvent) {
const target = getHTMLElementOrNull(ev.currentTarget ?? ev.target);
if (!target) return;
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkLaunchPad.vue').then(x => x.default), {
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index 079f1f92bb..23093e60d7 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -67,7 +67,7 @@ useInterval(tick, Math.max(5000, props.refreshIntervalSec * 1000), {
afterMounted: true,
});
-function getInstanceIcon(instance): string {
+function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png';
}
</script>
diff --git a/packages/frontend/src/ui/_common_/widgets.vue b/packages/frontend/src/ui/_common_/widgets.vue
index 1a6d62e19b..4087c4d517 100644
--- a/packages/frontend/src/ui/_common_/widgets.vue
+++ b/packages/frontend/src/ui/_common_/widgets.vue
@@ -5,10 +5,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
- <XWidgets :edit="editMode" :widgets="widgets" @addWidget="addWidget" @removeWidget="removeWidget" @updateWidget="updateWidget" @updateWidgets="updateWidgets" @exit="editMode = false"/>
+ <XWidgets
+ :edit="editMode"
+ :widgets="widgets"
+ @addWidget="addWidget"
+ @removeWidget="removeWidget"
+ @updateWidget="updateWidget"
+ @updateWidgets="updateWidgets"
+ @exit="editMode = false"
+ />
<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
- <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
+ <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em; margin-top: 16px;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
</div>
</template>
@@ -16,7 +24,9 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, ref } from 'vue';
const editMode = ref(false);
</script>
+
<script lang="ts" setup>
+import type { DefaultStoredWidget, Widget } from '@/components/MkWidgets.vue';
import XWidgets from '@/components/MkWidgets.vue';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
@@ -36,30 +46,31 @@ const widgets = computed(() => {
return prefer.r.widgets.value.filter(w => w.place !== 'left');
});
-function addWidget(widget) {
+function addWidget(widget: Widget) {
prefer.commit('widgets', [{
...widget,
place: props.place,
}, ...prefer.s.widgets]);
}
-function removeWidget(widget) {
+function removeWidget(widget: Widget) {
prefer.commit('widgets', prefer.s.widgets.filter(w => w.id !== widget.id));
}
-function updateWidget({ id, data }) {
- prefer.commit('widgets', prefer.s.widgets.map(w => w.id === id ? {
+function updateWidget(widget: { id: Widget['id']; data: Widget['data']; }) {
+ prefer.commit('widgets', prefer.s.widgets.map(w => w.id === widget.id ? {
...w,
- data,
+ data: widget.data,
place: props.place,
} : w));
}
-function updateWidgets(thisWidgets) {
+function updateWidgets(thisWidgets: Widget[]) {
if (props.place === null) {
- prefer.commit('widgets', thisWidgets);
+ prefer.commit('widgets', thisWidgets as DefaultStoredWidget[]);
return;
}
+
if (props.place === 'left') {
prefer.commit('widgets', [
...thisWidgets.map(w => ({ ...w, place: 'left' })),
@@ -67,6 +78,7 @@ function updateWidgets(thisWidgets) {
]);
return;
}
+
prefer.commit('widgets', [
...prefer.s.widgets.filter(w => w.place === 'left' && !thisWidgets.some(t => w.id === t.id)),
...thisWidgets.map(w => ({ ...w, place: 'right' })),
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 484b7f277a..0bafa1074c 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-for="id in ids"
:ref="id"
:key="id"
- :class="[$style.column, { '_shadow': withWallpaper }]"
+ :class="{ '_shadow': withWallpaper }"
:column="columns.find(c => c.id === id)!"
:isStacked="ids.length > 1"
@headerWheel="onWheel"
@@ -174,7 +174,7 @@ const addColumnButtonEl = useTemplateRef('addColumnButtonEl');
const settingsButtonEl = useTemplateRef('settingsButtonEl');
const swicthProfileButtonEl = useTemplateRef('swicthProfileButtonEl');
-const addColumn = async (ev) => {
+async function addColumn(ev: PointerEvent) {
const { canceled, result: column } = await os.select({
title: i18n.ts._deck.addColumn,
items: columnTypes.filter(column => column !== 'chat' || $i == null || $i.policies.chatAvailability !== 'unavailable').map(column => ({
@@ -190,14 +190,14 @@ const addColumn = async (ev) => {
width: 330,
soundSetting: { type: null, volume: 1 },
});
-};
+}
-const onContextmenu = (ev) => {
+function onContextmenu(ev: PointerEvent) {
os.contextMenu([{
text: i18n.ts._deck.addColumn,
action: addColumn,
}], ev);
-};
+}
// タッチでスクロールしてるときはスナップスクロールを有効にする
function pointerEvent(ev: PointerEvent) {
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 312ca51c83..410faf11f9 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -247,15 +247,15 @@ function getMenu() {
return menuItems;
}
-function showSettingsMenu(ev: MouseEvent) {
+function showSettingsMenu(ev: PointerEvent) {
os.popupMenu(getMenu(), ev.currentTarget ?? ev.target);
}
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
os.contextMenu(getMenu(), ev);
}
-function goTop(ev: MouseEvent) {
+function goTop(ev: PointerEvent) {
emit('headerClick', ev);
if (!props.handleScrollToTop) return;
@@ -267,7 +267,9 @@ function goTop(ev: MouseEvent) {
}
}
-function onDragstart(ev) {
+function onDragstart(ev: DragEvent) {
+ if (ev.dataTransfer == null) return;
+
ev.dataTransfer.effectAllowed = 'move';
setDragData(ev, 'deckColumn', props.column.id);
@@ -278,11 +280,13 @@ function onDragstart(ev) {
}, 10);
}
-function onDragend(ev) {
+function onDragend(ev: DragEvent) {
dragging.value = false;
}
-function onDragover(ev) {
+function onDragover(ev: DragEvent) {
+ if (ev.dataTransfer == null) return;
+
// 自分自身がドラッグされている場合
if (dragging.value) {
// 自分自身にはドロップさせない
@@ -300,7 +304,7 @@ function onDragleave() {
draghover.value = false;
}
-function onDrop(ev) {
+function onDrop(ev: DragEvent) {
draghover.value = false;
os.deckGlobalEvents.emit('column.dragEnd');
diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue
index 1388cbdc18..8757ec0941 100644
--- a/packages/frontend/src/ui/deck/main-column.vue
+++ b/packages/frontend/src/ui/deck/main-column.vue
@@ -58,11 +58,11 @@ function back() {
history.back();
}
*/
-function onContextmenu(ev: MouseEvent) {
+function onContextmenu(ev: PointerEvent) {
if (!ev.target) return;
if (isLink(ev.target as HTMLElement)) return;
- if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes((ev.target as HTMLElement).tagName) || (ev.target as HTMLElement).attributes['contenteditable']) return;
+ if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes((ev.target as HTMLElement).tagName) || (ev.target as HTMLElement).attributes.getNamedItem('contenteditable') != null) return;
if (window.getSelection()?.toString() !== '') return;
const path = mainRouter.currentRoute.value.path;
os.contextMenu([{
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 0e59913c4c..aab0cde1c8 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<XColumn :menu="menu" :column="column" :isStacked="isStacked" :refresher="async () => { await timeline?.reloadTimeline() }">
<template #header>
- <i v-if="column.tl != null" :class="basicTimelineIconClass(column.tl)"/>
+ <i v-if="column.tl != null" :class="basicTimelineIconClass(column.tl)"></i>
<span style="margin-left: 8px;">{{ column.name || (column.tl ? i18n.ts._timelines[column.tl] : null) || i18n.ts._deck._columns.tl }}</span>
</template>
diff --git a/packages/frontend/src/ui/deck/widgets-column.vue b/packages/frontend/src/ui/deck/widgets-column.vue
index 4e84ef0ba0..0985e95653 100644
--- a/packages/frontend/src/ui/deck/widgets-column.vue
+++ b/packages/frontend/src/ui/deck/widgets-column.vue
@@ -17,8 +17,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
import XColumn from './column.vue';
-import { addColumnWidget, removeColumnWidget, setColumnWidgets, updateColumnWidget } from '@/deck.js';
import type { Column } from '@/deck.js';
+import type { Widget } from '@/components/MkWidgets.vue';
+import { addColumnWidget, removeColumnWidget, setColumnWidgets, updateColumnWidget } from '@/deck.js';
import XWidgets from '@/components/MkWidgets.vue';
import { i18n } from '@/i18n.js';
@@ -29,19 +30,19 @@ const props = defineProps<{
const edit = ref(false);
-function addWidget(widget) {
+function addWidget(widget: Widget) {
addColumnWidget(props.column.id, widget);
}
-function removeWidget(widget) {
+function removeWidget(widget: Widget) {
removeColumnWidget(props.column.id, widget);
}
-function updateWidget({ id, data }) {
- updateColumnWidget(props.column.id, id, data);
+function updateWidget(widget: { id: Widget['id']; data: Widget['data']; }) {
+ updateColumnWidget(props.column.id, widget.id, widget.data);
}
-function updateWidgets(widgets) {
+function updateWidgets(widgets: Widget[]) {
setColumnWidgets(props.column.id, widgets);
}
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index 497ef72d04..95582edea1 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -103,9 +103,9 @@ if (window.innerWidth > 1024) {
}
}
-const onContextmenu = (ev) => {
- if (isLink(ev.target)) return;
- if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return;
+function onContextmenu(ev: PointerEvent) {
+ if (isLink(ev.target as HTMLElement)) return;
+ if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes((ev.target as HTMLElement).tagName) || (ev.target as HTMLElement).attributes.getNamedItem('contenteditable') != null) return;
if (window.getSelection()?.toString() !== '') return;
const path = mainRouter.getCurrentFullPath();
os.contextMenu([{
@@ -118,7 +118,7 @@ const onContextmenu = (ev) => {
os.pageWindow(path);
},
}], ev);
-};
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue
index 800aef8696..b56ed4eb6a 100644
--- a/packages/frontend/src/ui/zen.vue
+++ b/packages/frontend/src/ui/zen.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="$style.root">
+<div>
<div :class="$style.contents">
<!--
デッキUIが設定されている場合はデッキUIに戻れるようにする (ただし?zenが明示された場合は表示しない)
@@ -57,9 +57,6 @@ function goToDeck() {
</script>
<style lang="scss" module>
-.root {
-}
-
.contents {
display: flex;
flex-direction: column;
diff --git a/packages/frontend/src/utility/admin-lookup.ts b/packages/frontend/src/utility/admin-lookup.ts
index 18eebaa8f8..74485a11d7 100644
--- a/packages/frontend/src/utility/admin-lookup.ts
+++ b/packages/frontend/src/utility/admin-lookup.ts
@@ -14,7 +14,7 @@ export async function lookupUser() {
});
if (canceled || result == null) return;
- const show = (user) => {
+ const show = (user: Misskey.entities.UserDetailed) => {
os.pageWindow(`/admin/user/${user.id}`);
};
@@ -36,7 +36,7 @@ export async function lookupUser() {
notFound();
}
});
- idPromise.then(show).catch(err => {
+ idPromise.then(show).catch(_ => {
notFound();
});
}
@@ -71,12 +71,8 @@ export async function lookupFile() {
});
if (canceled) return;
- const show = (file) => {
- os.pageWindow(`/admin/file/${file.id}`);
- };
-
misskeyApi('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => {
- show(file);
+ os.pageWindow(`/admin/file/${file.id}`);
}).catch(err => {
if (err.code === 'NO_SUCH_FILE') {
os.alert({
diff --git a/packages/frontend/src/utility/autocomplete.ts b/packages/frontend/src/utility/autocomplete.ts
index 82109af1a0..a44bf7c1ae 100644
--- a/packages/frontend/src/utility/autocomplete.ts
+++ b/packages/frontend/src/utility/autocomplete.ts
@@ -12,6 +12,15 @@ import { popup } from '@/os.js';
export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag' | 'mfmParam';
+type CompleteProps<T extends keyof CompleteInfo> = {
+ type: T;
+ value: CompleteInfo[T]['payload'];
+};
+
+function isCompleteType<T extends keyof CompleteInfo>(expectedType: T, props: CompleteProps<keyof CompleteInfo>): props is CompleteProps<T> {
+ return props.type === expectedType;
+}
+
export class Autocomplete {
private suggestion: {
x: Ref<number>;
@@ -194,7 +203,7 @@ export class Autocomplete {
this.currentType = type;
//#region サジェストを表示すべき位置を計算
- const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart);
+ const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart ?? 0);
const rect = this.textarea.getBoundingClientRect();
@@ -213,10 +222,11 @@ export class Autocomplete {
const _y = ref(y);
const _q = ref(q);
- const { dispose } = await popup(defineAsyncComponent(() => import('@/components/MkAutocomplete.vue')), {
+ const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAutocomplete.vue')), {
textarea: this.textarea,
close: this.close,
type: type,
+ //@ts-expect-error popupは今のところジェネリック型のコンポーネントに対応していない
q: _q,
x: _x,
y: _y,
@@ -252,19 +262,19 @@ export class Autocomplete {
/**
* オートコンプリートする
*/
- private complete<T extends keyof CompleteInfo>({ type, value }: { type: T; value: CompleteInfo[T]['payload'] }) {
+ private complete<T extends keyof CompleteInfo>(props: CompleteProps<T>) {
this.close();
const caret = Number(this.textarea.selectionStart);
- if (type === 'user') {
+ if (isCompleteType('user', props)) {
const source = this.text;
const before = source.substring(0, caret);
const trimmedBefore = before.substring(0, before.lastIndexOf('@'));
const after = source.substring(caret);
- const acct = value.host === null ? value.username : `${value.username}@${toASCII(value.host)}`;
+ const acct = props.value.host === null ? props.value.username : `${props.value.username}@${toASCII(props.value.host)}`;
// 挿入
this.text = `${trimmedBefore}@${acct} ${after}`;
@@ -275,7 +285,7 @@ export class Autocomplete {
const pos = trimmedBefore.length + (acct.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type === 'hashtag') {
+ } else if (isCompleteType('hashtag', props)) {
const source = this.text;
const before = source.substring(0, caret);
@@ -283,15 +293,15 @@ export class Autocomplete {
const after = source.substring(caret);
// 挿入
- this.text = `${trimmedBefore}#${value} ${after}`;
+ this.text = `${trimmedBefore}#${props.value} ${after}`;
// キャレットを戻す
nextTick(() => {
this.textarea.focus();
- const pos = trimmedBefore.length + (value.length + 2);
+ const pos = trimmedBefore.length + (props.value.length + 2);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type === 'emoji') {
+ } else if (isCompleteType('emoji', props)) {
const source = this.text;
const before = source.substring(0, caret);
@@ -299,15 +309,15 @@ export class Autocomplete {
const after = source.substring(caret);
// 挿入
- this.text = trimmedBefore + value + after;
+ this.text = trimmedBefore + props.value + after;
// キャレットを戻す
nextTick(() => {
this.textarea.focus();
- const pos = trimmedBefore.length + value.length;
+ const pos = trimmedBefore.length + props.value.length;
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type === 'emojiComplete') {
+ } else if (isCompleteType('emojiComplete', props)) {
const source = this.text;
const before = source.substring(0, caret);
@@ -315,15 +325,15 @@ export class Autocomplete {
const after = source.substring(caret);
// 挿入
- this.text = trimmedBefore + value + after;
+ this.text = trimmedBefore + props.value + after;
// キャレットを戻す
nextTick(() => {
this.textarea.focus();
- const pos = trimmedBefore.length + value.length;
+ const pos = trimmedBefore.length + props.value.length;
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type === 'mfmTag') {
+ } else if (isCompleteType('mfmTag', props)) {
const source = this.text;
const before = source.substring(0, caret);
@@ -331,15 +341,15 @@ export class Autocomplete {
const after = source.substring(caret);
// 挿入
- this.text = `${trimmedBefore}$[${value} ]${after}`;
+ this.text = `${trimmedBefore}$[${props.value} ]${after}`;
// キャレットを戻す
nextTick(() => {
this.textarea.focus();
- const pos = trimmedBefore.length + (value.length + 3);
+ const pos = trimmedBefore.length + (props.value.length + 3);
this.textarea.setSelectionRange(pos, pos);
});
- } else if (type === 'mfmParam') {
+ } else if (isCompleteType('mfmParam', props)) {
const source = this.text;
const before = source.substring(0, caret);
@@ -347,12 +357,12 @@ export class Autocomplete {
const after = source.substring(caret);
// 挿入
- this.text = `${trimmedBefore}.${value}${after}`;
+ this.text = `${trimmedBefore}.${props.value}${after}`;
// キャレットを戻す
nextTick(() => {
this.textarea.focus();
- const pos = trimmedBefore.length + (value.length + 1);
+ const pos = trimmedBefore.length + (props.value.length + 1);
this.textarea.setSelectionRange(pos, pos);
});
}
diff --git a/packages/frontend/src/utility/chart-vline.ts b/packages/frontend/src/utility/chart-vline.ts
index 2fe4bdb83b..1097c66d0e 100644
--- a/packages/frontend/src/utility/chart-vline.ts
+++ b/packages/frontend/src/utility/chart-vline.ts
@@ -11,7 +11,7 @@ export const chartVLine = (vLineColor: string) => ({
const tooltip = chart.tooltip as any;
if (tooltip?._active?.length) {
const ctx = chart.ctx;
- const xs = tooltip._active.map(a => a.element.x);
+ const xs = tooltip._active.map((a: any) => a.element.x) as number[];
const x = xs.reduce((a, b) => a + b, 0) / xs.length;
const topY = chart.scales.y.top;
const bottomY = chart.scales.y.bottom;
diff --git a/packages/frontend/src/utility/check-word-mute.ts b/packages/frontend/src/utility/check-word-mute.ts
index 98fea1bced..eafc939c80 100644
--- a/packages/frontend/src/utility/check-word-mute.ts
+++ b/packages/frontend/src/utility/check-word-mute.ts
@@ -29,7 +29,7 @@ export function checkWordMute(note: Misskey.entities.Note, me: Misskey.entities.
try {
return new RegExp(regexp[1], regexp[2]).test(text);
- } catch (err) {
+ } catch (_) {
// This should never happen due to input sanitisation.
return false;
}
diff --git a/packages/frontend/src/utility/collect-page-vars.ts b/packages/frontend/src/utility/collect-page-vars.ts
deleted file mode 100644
index 5096c0669e..0000000000
--- a/packages/frontend/src/utility/collect-page-vars.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-interface StringPageVar {
- name: string,
- type: 'string',
- value: string
-}
-
-interface NumberPageVar {
- name: string,
- type: 'number',
- value: number
-}
-
-interface BooleanPageVar {
- name: string,
- type: 'boolean',
- value: boolean
-}
-
-type PageVar = StringPageVar | NumberPageVar | BooleanPageVar;
-
-export function collectPageVars(content): PageVar[] {
- const pageVars: PageVar[] = [];
- const collect = (xs: any[]): void => {
- for (const x of xs) {
- if (x.type === 'textInput') {
- pageVars.push({
- name: x.name,
- type: 'string',
- value: x.default || '',
- });
- } else if (x.type === 'textareaInput') {
- pageVars.push({
- name: x.name,
- type: 'string',
- value: x.default || '',
- });
- } else if (x.type === 'numberInput') {
- pageVars.push({
- name: x.name,
- type: 'number',
- value: x.default || 0,
- });
- } else if (x.type === 'switch') {
- pageVars.push({
- name: x.name,
- type: 'boolean',
- value: x.default || false,
- });
- } else if (x.type === 'counter') {
- pageVars.push({
- name: x.name,
- type: 'number',
- value: 0,
- });
- } else if (x.type === 'radioButton') {
- pageVars.push({
- name: x.name,
- type: 'string',
- value: x.default || '',
- });
- } else if (x.children) {
- collect(x.children);
- }
- }
- };
- collect(content);
- return pageVars;
-}
diff --git a/packages/frontend/src/utility/deep-equal.ts b/packages/frontend/src/utility/deep-equal.ts
index 2859641dc7..ac2c2e68da 100644
--- a/packages/frontend/src/utility/deep-equal.ts
+++ b/packages/frontend/src/utility/deep-equal.ts
@@ -31,7 +31,7 @@ export function deepEqual(a: JsonLike, b: JsonLike): boolean {
if (aks.length !== bks.length) return false;
for (let i = 0; i < aks.length; i++) {
const k = aks[i];
- if (!deepEqual(a[k], (b as { [key: string]: JsonLike })[k])) return false;
+ if (!deepEqual((a as { [key: string]: JsonLike })[k], (b as { [key: string]: JsonLike })[k])) return false;
}
return true;
}
diff --git a/packages/frontend/src/utility/drive.ts b/packages/frontend/src/utility/drive.ts
index 64079d125a..7578ed36e6 100644
--- a/packages/frontend/src/utility/drive.ts
+++ b/packages/frontend/src/utility/drive.ts
@@ -180,8 +180,9 @@ export function chooseFileFromPcAndUpload(
export function chooseDriveFile(options: {
multiple?: boolean;
} = {}): Promise<Misskey.entities.DriveFile[]> {
- return new Promise(async resolve => {
- const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFileSelectDialog.vue').then(x => x.default), {
+ return new Promise((resolve, rej) => {
+ let dispose: () => void;
+ os.popupAsyncWithDialog(import('@/components/MkDriveFileSelectDialog.vue').then(x => x.default), {
multiple: options.multiple ?? false,
}, {
done: files => {
@@ -190,7 +191,7 @@ export function chooseDriveFile(options: {
}
},
closed: () => dispose(),
- });
+ }).then((d) => dispose = d.dispose, rej);
});
}
@@ -300,15 +301,28 @@ export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFi
});
}
-export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise<(Misskey.entities.DriveFolder | null)[]> {
- return new Promise(async resolve => {
- const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFolderSelectDialog.vue').then(x => x.default), {
+export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise<{
+ canceled: false;
+ folders: (Misskey.entities.DriveFolder | null)[];
+} | {
+ canceled: true;
+ folders: undefined;
+}> {
+ return new Promise((resolve, reject) => {
+ let dispose: () => void;
+ os.popupAsyncWithDialog(import('@/components/MkDriveFolderSelectDialog.vue').then(x => x.default), {
initialFolder,
}, {
done: folders => {
- resolve(folders);
+ resolve(folders == null ? {
+ canceled: true,
+ folders: undefined,
+ } : {
+ canceled: false,
+ folders,
+ });
},
closed: () => dispose(),
- });
+ }).then(d => dispose = d.dispose, reject);
});
}
diff --git a/packages/frontend/src/utility/contains.ts b/packages/frontend/src/utility/element-contains.ts
index 6137c06e85..8389d49278 100644
--- a/packages/frontend/src/utility/contains.ts
+++ b/packages/frontend/src/utility/element-contains.ts
@@ -3,7 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-export default (parent, child, checkSame = true) => {
+export function elementContains(parent: Element | null, child: Element | null, checkSame = true) {
+ if (parent === null || child === null) return false;
if (checkSame && parent === child) return true;
let node = child.parentNode;
while (node) {
@@ -11,4 +12,4 @@ export default (parent, child, checkSame = true) => {
node = node.parentNode;
}
return false;
-};
+}
diff --git a/packages/frontend/src/utility/file-drop.ts b/packages/frontend/src/utility/file-drop.ts
index 4259fe25e9..ffc024e8f3 100644
--- a/packages/frontend/src/utility/file-drop.ts
+++ b/packages/frontend/src/utility/file-drop.ts
@@ -75,20 +75,18 @@ export async function readDataTransferItems(itemList: DataTransferItemList): Pro
});
}
- function readDirectory(fileSystemDirectoryEntry: FileSystemDirectoryEntry): Promise<DroppedItem[]> {
- return new Promise(async (resolve) => {
- const allEntries = Array.of<FileSystemEntry>();
- const reader = fileSystemDirectoryEntry.createReader();
- while (true) {
- const entries = await new Promise<FileSystemEntry[]>((res, rej) => reader.readEntries(res, rej));
- if (entries.length === 0) {
- break;
- }
- allEntries.push(...entries);
+ async function readDirectory(fileSystemDirectoryEntry: FileSystemDirectoryEntry): Promise<DroppedItem[]> {
+ const allEntries = Array.of<FileSystemEntry>();
+ const reader = fileSystemDirectoryEntry.createReader();
+ while (true) {
+ const entries = await new Promise<FileSystemEntry[]>((res, rej) => reader.readEntries(res, rej));
+ if (entries.length === 0) {
+ break;
}
+ allEntries.push(...entries);
+ }
- resolve(await Promise.all(allEntries.map(readEntry)));
- });
+ return await Promise.all(allEntries.map(readEntry));
}
// 扱いにくいので配列に変換
diff --git a/packages/frontend/src/utility/form.ts b/packages/frontend/src/utility/form.ts
index cb4a227f67..43dee37a0e 100644
--- a/packages/frontend/src/utility/form.ts
+++ b/packages/frontend/src/utility/form.ts
@@ -4,7 +4,7 @@
*/
import * as Misskey from 'misskey-js';
-import type { OptionValue } from '@/components/MkSelect.vue';
+import type { OptionValue } from '@/types/option-value.js';
export type EnumItem = string | {
label: string;
@@ -25,6 +25,7 @@ export interface StringFormItem extends FormItemBase {
required?: boolean;
multiline?: boolean;
treatAsMfm?: boolean;
+ manualSave?: boolean;
}
export interface NumberFormItem extends FormItemBase {
@@ -33,6 +34,7 @@ export interface NumberFormItem extends FormItemBase {
description?: string;
required?: boolean;
step?: number;
+ manualSave?: boolean;
}
export interface BooleanFormItem extends FormItemBase {
@@ -43,18 +45,18 @@ export interface BooleanFormItem extends FormItemBase {
export interface EnumFormItem extends FormItemBase {
type: 'enum';
- default?: string | null;
+ default?: OptionValue | null;
required?: boolean;
enum: EnumItem[];
}
export interface RadioFormItem extends FormItemBase {
type: 'radio';
- default?: unknown | null;
+ default?: OptionValue | null;
required?: boolean;
options: {
label: string;
- value: unknown;
+ value: OptionValue;
}[];
}
@@ -82,7 +84,7 @@ export interface ArrayFormItem extends FormItemBase {
export interface ButtonFormItem extends FormItemBase {
type: 'button';
content?: string;
- action: (ev: MouseEvent, v: any) => void;
+ action: (ev: PointerEvent, v: any) => void;
}
export interface DriveFileFormItem extends FormItemBase {
@@ -124,24 +126,32 @@ type NonNullableIfRequired<T, Item extends FormItem> =
type GetItemType<Item extends FormItem> =
Item extends StringFormItem
? NonNullableIfRequired<InferDefault<Item, string>, Item>
- : Item extends NumberFormItem
- ? NonNullableIfRequired<InferDefault<Item, number>, Item>
- : Item extends BooleanFormItem
- ? boolean
- : Item extends RadioFormItem
- ? GetRadioItemType<Item>
- : Item extends RangeFormItem
- ? NonNullableIfRequired<InferDefault<Item, number>, Item>
- : Item extends EnumFormItem
- ? GetEnumItemType<Item>
- : Item extends ArrayFormItem
- ? NonNullableIfRequired<InferDefault<Item, unknown[]>, Item>
- : Item extends ObjectFormItem
- ? NonNullableIfRequired<InferDefault<Item, Record<string, unknown>>, Item>
- : Item extends DriveFileFormItem
- ? Misskey.entities.DriveFile | undefined
- : never;
+ : Item extends NumberFormItem
+ ? NonNullableIfRequired<InferDefault<Item, number>, Item>
+ : Item extends BooleanFormItem
+ ? boolean
+ : Item extends RadioFormItem
+ ? GetRadioItemType<Item>
+ : Item extends RangeFormItem
+ ? NonNullableIfRequired<InferDefault<Item, number>, Item>
+ : Item extends EnumFormItem
+ ? GetEnumItemType<Item>
+ : Item extends ArrayFormItem
+ ? NonNullableIfRequired<InferDefault<Item, unknown[]>, Item>
+ : Item extends ObjectFormItem
+ ? NonNullableIfRequired<InferDefault<Item, Record<string, unknown>>, Item>
+ : Item extends DriveFileFormItem
+ ? Misskey.entities.DriveFile | undefined
+ : never;
export type GetFormResultType<F extends Form> = {
[P in keyof F]: GetItemType<F[P]>;
};
+
+export function getDefaultFormValues<F extends FormWithDefault>(form: F): GetFormResultType<F> {
+ const result = {} as GetFormResultType<F>;
+ for (const key of Object.keys(form) as (keyof F)[]) {
+ result[key] = form[key].default as GetItemType<F[typeof key]>;
+ }
+ return result;
+}
diff --git a/packages/frontend/src/utility/get-drive-file-menu.ts b/packages/frontend/src/utility/get-drive-file-menu.ts
index 040cf8f976..fea7f8f1d3 100644
--- a/packages/frontend/src/utility/get-drive-file-menu.ts
+++ b/packages/frontend/src/utility/get-drive-file-menu.ts
@@ -44,10 +44,11 @@ async function describe(file: Misskey.entities.DriveFile) {
}
function move(file: Misskey.entities.DriveFile) {
- selectDriveFolder(null).then(folder => {
+ selectDriveFolder(null).then(({ canceled, folders }) => {
+ if (canceled) return;
misskeyApi('drive/files/update', {
fileId: file.id,
- folderId: folder[0] ? folder[0].id : null,
+ folderId: folders[0] ? folders[0].id : null,
});
});
}
@@ -89,7 +90,7 @@ async function deleteFile(file: Misskey.entities.DriveFile) {
}
export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Misskey.entities.DriveFolder | null): MenuItem[] {
- const isImage = file.type.startsWith('image/');
+ const _isImage = file.type.startsWith('image/');
const menuItems: MenuItem[] = [];
diff --git a/packages/frontend/src/utility/get-embed-code.ts b/packages/frontend/src/utility/get-embed-code.ts
index 5ccd46cfe2..5817d7ece8 100644
--- a/packages/frontend/src/utility/get-embed-code.ts
+++ b/packages/frontend/src/utility/get-embed-code.ts
@@ -3,10 +3,10 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { defineAsyncComponent } from 'vue';
-import { genId } from '@/utility/id.js';
import { url } from '@@/js/config.js';
import { defaultEmbedParams, embedRouteWithScrollbar } from '@@/js/embed-page.js';
import type { EmbedParams, EmbeddableEntity } from '@@/js/embed-page.js';
+import { genId } from '@/utility/id.js';
import * as os from '@/os.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
@@ -21,19 +21,20 @@ export function normalizeEmbedParams(params: EmbedParams): Record<string, string
// paramsのvalueをすべてstringに変換。undefinedやnullはプロパティごと消す
const normalizedParams: Record<string, string> = {};
for (const key in params) {
+ const k = key as keyof EmbedParams;
// デフォルトの値と同じならparamsに含めない
- if (params[key] == null || params[key] === defaultEmbedParams[key]) {
+ if (params[k] == null || params[k] === defaultEmbedParams[k]) {
continue;
}
- switch (typeof params[key]) {
+ switch (typeof params[k]) {
case 'number':
- normalizedParams[key] = params[key].toString();
+ normalizedParams[k] = params[k].toString();
break;
case 'boolean':
- normalizedParams[key] = params[key] ? 'true' : 'false';
+ normalizedParams[k] = params[k] ? 'true' : 'false';
break;
default:
- normalizedParams[key] = params[key];
+ normalizedParams[k] = params[k];
break;
}
}
diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts
index fc165ea898..78176970f1 100644
--- a/packages/frontend/src/utility/get-note-menu.ts
+++ b/packages/frontend/src/utility/get-note-menu.ts
@@ -262,7 +262,7 @@ export function getNoteMenu(props: {
os.apiWithDialog('clips/remove-note', { clipId: props.currentClip.id, noteId: appearNote.id });
}
- async function promote(): Promise<void> {
+ async function _promote(): Promise<void> {
const { canceled, result: days } = await os.inputNumber({
title: i18n.ts.numberOfDays,
});
diff --git a/packages/frontend/src/utility/get-user-environment.ts b/packages/frontend/src/utility/get-user-environment.ts
index 3b8d43fb2c..ebae0492b1 100644
--- a/packages/frontend/src/utility/get-user-environment.ts
+++ b/packages/frontend/src/utility/get-user-environment.ts
@@ -39,7 +39,7 @@ export async function getUserEnvironment(): Promise<UserEnvironment> {
}
}
- const browserData = uaData.fullVersionList.find((item) => !/^\s*not.+a.+brand\s*$/i.test(item.brand));
+ const browserData = uaData.fullVersionList.find((item: any) => !/^\s*not.+a.+brand\s*$/i.test(item.brand));
return {
os: `${uaData.platform} ${osVersion}`,
browser: browserData ? `${browserData.brand} v${browserData.version}` : 'Unknown',
diff --git a/packages/frontend/src/utility/image-compositor-functions/blur.glsl b/packages/frontend/src/utility/image-compositor-functions/blur.glsl
index e591267887..dc48c2ae94 100644
--- a/packages/frontend/src/utility/image-compositor-functions/blur.glsl
+++ b/packages/frontend/src/utility/image-compositor-functions/blur.glsl
@@ -21,13 +21,20 @@ uniform float u_radius;
uniform int u_samples;
out vec4 out_color;
+float rand(vec2 value) {
+ return fract(sin(dot(value, vec2(12.9898, 78.233))) * 43758.5453);
+}
+
void main() {
float angle = -(u_angle * PI);
+ float aspect = in_resolution.x / max(in_resolution.y, 1.0);
vec2 centeredUv = in_uv - vec2(0.5, 0.5) - u_offset;
- vec2 rotatedUV = vec2(
- centeredUv.x * cos(angle) - centeredUv.y * sin(angle),
- centeredUv.x * sin(angle) + centeredUv.y * cos(angle)
- ) + u_offset;
+ vec2 scaledUv = vec2(centeredUv.x * aspect, centeredUv.y);
+ vec2 rotatedScaledUv = vec2(
+ scaledUv.x * cos(angle) - scaledUv.y * sin(angle),
+ scaledUv.x * sin(angle) + scaledUv.y * cos(angle)
+ );
+ vec2 rotatedUV = vec2(rotatedScaledUv.x / aspect, rotatedScaledUv.y) + u_offset;
bool isInside = false;
if (u_ellipse) {
@@ -46,31 +53,29 @@ void main() {
float totalSamples = 0.0;
// Make blur radius resolution-independent by using a percentage of image size
- // This ensures consistent visual blur regardless of image resolution
float referenceSize = min(in_resolution.x, in_resolution.y);
- float normalizedRadius = u_radius / 100.0; // Convert radius to percentage (0-15 -> 0-0.15)
- vec2 blurOffset = vec2(normalizedRadius) / in_resolution * referenceSize;
-
- // Calculate how many samples to take in each direction
- // This determines the grid density, not the blur extent
- int sampleRadius = int(sqrt(float(u_samples)) / 2.0);
+ float normalizedRadius = u_radius / 100.0;
+ float radiusPx = normalizedRadius * referenceSize;
+ vec2 texelSize = 1.0 / in_resolution;
- // Sample in a grid pattern within the specified radius
- for (int x = -sampleRadius; x <= sampleRadius; x++) {
- for (int y = -sampleRadius; y <= sampleRadius; y++) {
- // Normalize the grid position to [-1, 1] range
- float normalizedX = float(x) / float(sampleRadius);
- float normalizedY = float(y) / float(sampleRadius);
+ int sampleCount = max(u_samples, 1);
+ float sampleCountF = float(sampleCount);
+ float jitter = rand(in_uv * in_resolution);
+ float goldenAngle = 2.39996323;
- // Scale by radius to get the actual sampling offset
- vec2 offset = vec2(normalizedX, normalizedY) * blurOffset;
- vec2 sampleUV = in_uv + offset;
+ // Sample in a circular pattern to avoid axis-aligned artifacts
+ for (int i = 0; i < sampleCount; i++) {
+ float fi = float(i);
+ float radius = sqrt((fi + 0.5) / sampleCountF);
+ float theta = (fi + jitter) * goldenAngle;
+ vec2 direction = vec2(cos(theta), sin(theta));
+ vec2 offset = direction * (radiusPx * radius) * texelSize;
+ vec2 sampleUV = in_uv + offset;
- // Only sample if within texture bounds
- if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0) {
- result += texture(in_texture, sampleUV);
- totalSamples += 1.0;
- }
+ if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0) {
+ float weight = exp(-radius * radius * 4.0);
+ result += texture(in_texture, sampleUV) * weight;
+ totalSamples += weight;
}
}
diff --git a/packages/frontend/src/utility/image-compositor-functions/blur.ts b/packages/frontend/src/utility/image-compositor-functions/blur.ts
index 1ab8eee6ba..72711445cc 100644
--- a/packages/frontend/src/utility/image-compositor-functions/blur.ts
+++ b/packages/frontend/src/utility/image-compositor-functions/blur.ts
@@ -84,9 +84,9 @@ export const uiDefinition = {
radius: {
label: i18n.ts._imageEffector._fxProps.strength,
type: 'number',
- default: 3.0,
+ default: 10.0,
min: 0.0,
- max: 10.0,
+ max: 20.0,
step: 0.5,
},
},
diff --git a/packages/frontend/src/utility/image-compositor-functions/fill.glsl b/packages/frontend/src/utility/image-compositor-functions/fill.glsl
index f04dc5545a..02e5e3a071 100644
--- a/packages/frontend/src/utility/image-compositor-functions/fill.glsl
+++ b/packages/frontend/src/utility/image-compositor-functions/fill.glsl
@@ -27,11 +27,14 @@ void main() {
//float y_ratio = max(in_resolution.y / in_resolution.x, 1.0);
float angle = -(u_angle * PI);
+ float aspect = in_resolution.x / max(in_resolution.y, 1.0);
vec2 centeredUv = in_uv - vec2(0.5, 0.5) - u_offset;
- vec2 rotatedUV = vec2(
- centeredUv.x * cos(angle) - centeredUv.y * sin(angle),
- centeredUv.x * sin(angle) + centeredUv.y * cos(angle)
- ) + u_offset;
+ vec2 scaledUv = vec2(centeredUv.x * aspect, centeredUv.y);
+ vec2 rotatedScaledUv = vec2(
+ scaledUv.x * cos(angle) - scaledUv.y * sin(angle),
+ scaledUv.x * sin(angle) + scaledUv.y * cos(angle)
+ );
+ vec2 rotatedUV = vec2(rotatedScaledUv.x / aspect, rotatedScaledUv.y) + u_offset;
bool isInside = false;
if (u_ellipse) {
diff --git a/packages/frontend/src/utility/image-compositor-functions/pixelate.glsl b/packages/frontend/src/utility/image-compositor-functions/pixelate.glsl
index 4de3f27397..b08a3d798f 100644
--- a/packages/frontend/src/utility/image-compositor-functions/pixelate.glsl
+++ b/packages/frontend/src/utility/image-compositor-functions/pixelate.glsl
@@ -21,8 +21,6 @@ uniform int u_samples;
uniform float u_strength;
out vec4 out_color;
-// TODO: pixelateの中心を画像中心ではなく範囲の中心にする
-// TODO: 画像のアスペクト比に関わらず各画素は正方形にする
void main() {
if (u_strength <= 0.0) {
@@ -31,11 +29,14 @@ void main() {
}
float angle = -(u_angle * PI);
+ float aspect = in_resolution.x / max(in_resolution.y, 1.0);
vec2 centeredUv = in_uv - vec2(0.5, 0.5) - u_offset;
- vec2 rotatedUV = vec2(
- centeredUv.x * cos(angle) - centeredUv.y * sin(angle),
- centeredUv.x * sin(angle) + centeredUv.y * cos(angle)
- ) + u_offset;
+ vec2 scaledUv = vec2(centeredUv.x * aspect, centeredUv.y);
+ vec2 rotatedScaledUv = vec2(
+ scaledUv.x * cos(angle) - scaledUv.y * sin(angle),
+ scaledUv.x * sin(angle) + scaledUv.y * cos(angle)
+ );
+ vec2 rotatedUV = vec2(rotatedScaledUv.x / aspect, rotatedScaledUv.y) + u_offset;
bool isInside = false;
if (u_ellipse) {
@@ -50,19 +51,24 @@ void main() {
return;
}
- float dx = u_strength / 1.0;
- float dy = u_strength / 1.0;
+ float baseResolution = (in_resolution.x + in_resolution.y) * 0.5;
+ float dx = (u_strength * baseResolution) / max(in_resolution.x, 1.0);
+ float dy = (u_strength * baseResolution) / max(in_resolution.y, 1.0);
+ vec2 centerUv = vec2(0.5, 0.5) + u_offset;
vec2 new_uv = vec2(
- (dx * (floor((in_uv.x - 0.5 - (dx / 2.0)) / dx) + 0.5)),
- (dy * (floor((in_uv.y - 0.5 - (dy / 2.0)) / dy) + 0.5))
- ) + vec2(0.5 + (dx / 2.0), 0.5 + (dy / 2.0));
+ (dx * (floor((in_uv.x - centerUv.x - (dx / 2.0)) / dx) + 0.5)),
+ (dy * (floor((in_uv.y - centerUv.y - (dy / 2.0)) / dy) + 0.5))
+ ) + vec2(centerUv.x + (dx / 2.0), centerUv.y + (dy / 2.0));
vec4 result = vec4(0.0);
float totalSamples = 0.0;
- // TODO: より多くのサンプリング
- result += texture(in_texture, new_uv);
- totalSamples += 1.0;
+ vec2 halfStep = vec2(dx, dy) * 0.25;
+ result += texture(in_texture, new_uv + vec2(-halfStep.x, -halfStep.y));
+ result += texture(in_texture, new_uv + vec2(halfStep.x, -halfStep.y));
+ result += texture(in_texture, new_uv + vec2(-halfStep.x, halfStep.y));
+ result += texture(in_texture, new_uv + vec2(halfStep.x, halfStep.y));
+ totalSamples += 4.0;
out_color = totalSamples > 0.0 ? result / totalSamples : texture(in_texture, in_uv);
}
diff --git a/packages/frontend/src/utility/image-frame-renderer/ImageFrameRenderer.ts b/packages/frontend/src/utility/image-frame-renderer/ImageFrameRenderer.ts
index 9e97728785..591a94b855 100644
--- a/packages/frontend/src/utility/image-frame-renderer/ImageFrameRenderer.ts
+++ b/packages/frontend/src/utility/image-frame-renderer/ImageFrameRenderer.ts
@@ -201,7 +201,7 @@ export class ImageFrameRenderer {
qrSize,
);
qrImageBitmap.close();
- } catch (err) {
+ } catch (_) {
// nop
}
}
diff --git a/packages/frontend/src/utility/is-birthday.ts b/packages/frontend/src/utility/is-birthday.ts
new file mode 100644
index 0000000000..ff875281a2
--- /dev/null
+++ b/packages/frontend/src/utility/is-birthday.ts
@@ -0,0 +1,28 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+
+export function isBirthday(user: Misskey.entities.UserDetailed, now = new Date()): boolean {
+ if (user.birthday == null) return false;
+
+ const [_, bm, bd] = user.birthday.split('-').map((v) => parseInt(v, 10));
+ if (isNaN(bm) || isNaN(bd)) return false;
+
+ const y = now.getFullYear();
+ const m = now.getMonth() + 1;
+ const d = now.getDate();
+
+ // 閏日生まれで平年の場合は3月1日を誕生日として扱う
+ if (bm === 2 && bd === 29 && m === 3 && d === 1 && !isLeapYear(y)) {
+ return true;
+ }
+
+ return m === bm && d === bd;
+}
+
+function isLeapYear(year: number): boolean {
+ return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
+}
diff --git a/packages/frontend/src/utility/mfm-function-picker.ts b/packages/frontend/src/utility/mfm-function-picker.ts
index 09802d580b..5580435db1 100644
--- a/packages/frontend/src/utility/mfm-function-picker.ts
+++ b/packages/frontend/src/utility/mfm-function-picker.ts
@@ -3,55 +3,27 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { nextTick } from 'vue';
import { MFM_TAGS } from '@@/js/const.js';
-import type { Ref } from 'vue';
-import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
/**
* MFMの装飾のリストを表示する
*/
-export function mfmFunctionPicker(anchorElement: HTMLElement | EventTarget | null, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
+export function mfmFunctionPicker(anchorElement: HTMLElement | EventTarget | null, onChosen: (tag: string) => void, onClosed?: () => void) {
os.popupMenu([{
text: i18n.ts.addMfmFunction,
type: 'label',
- }, ...getFunctionList(textArea, textRef)], anchorElement);
-}
-
-function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>): MenuItem[] {
- return MFM_TAGS.map(tag => ({
+ }, ...MFM_TAGS.map(tag => ({
text: tag,
icon: 'ti ti-icons',
- action: () => add(textArea, textRef, tag),
- }));
-}
-
-function add(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, type: string) {
- const caretStart: number = textArea.selectionStart as number;
- const caretEnd: number = textArea.selectionEnd as number;
-
- MFM_TAGS.forEach(tag => {
- if (type === tag) {
- if (caretStart === caretEnd) {
- // 単純にFunctionを追加
- const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ]${textRef.value.substring(caretEnd)}`;
- textRef.value = trimmedText;
- } else {
- // 選択範囲を囲むようにFunctionを追加
- const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ${textRef.value.substring(caretStart, caretEnd)}]${textRef.value.substring(caretEnd)}`;
- textRef.value = trimmedText;
- }
- }
- });
-
- const nextCaretStart: number = caretStart + 3 + type.length;
- const nextCaretEnd: number = caretEnd + 3 + type.length;
-
- // キャレットを戻す
- nextTick(() => {
- textArea.focus();
- textArea.setSelectionRange(nextCaretStart, nextCaretEnd);
+ action: () => {
+ onChosen(tag);
+ },
+ }))], anchorElement, {
+ onClosed: () => {
+ if (onClosed) onClosed();
+ },
});
}
+
diff --git a/packages/frontend/src/utility/paginator.ts b/packages/frontend/src/utility/paginator.ts
index 59ae1e431a..45054acfd0 100644
--- a/packages/frontend/src/utility/paginator.ts
+++ b/packages/frontend/src/utility/paginator.ts
@@ -213,7 +213,7 @@ export class Paginator<
} : {}),
};
- const apiRes = (await misskeyApi(this.endpoint, data).catch(err => {
+ const apiRes = (await misskeyApi(this.endpoint, data).catch(_ => {
this.error.value = true;
this.fetching.value = false;
return null;
@@ -273,7 +273,7 @@ export class Paginator<
}),
};
- const apiRes = (await misskeyApi<T[]>(this.endpoint, data).catch(err => {
+ const apiRes = (await misskeyApi<T[]>(this.endpoint, data).catch(_ => {
return null;
})) as T[] | null;
@@ -326,7 +326,7 @@ export class Paginator<
}),
};
- const apiRes = (await misskeyApi<T[]>(this.endpoint, data).catch(err => {
+ const apiRes = (await misskeyApi<T[]>(this.endpoint, data).catch(_ => {
return null;
})) as T[] | null;
diff --git a/packages/frontend/src/utility/please-login.ts b/packages/frontend/src/utility/please-login.ts
index 737e7d7c6e..8120a8d1af 100644
--- a/packages/frontend/src/utility/please-login.ts
+++ b/packages/frontend/src/utility/please-login.ts
@@ -48,8 +48,8 @@ export async function pleaseLogin(opts: {
path?: string;
message?: string;
openOnRemote?: OpenOnRemoteOptions;
-} = {}) {
- if ($i) return;
+} = {}): Promise<boolean> {
+ if ($i != null) return true;
let _openOnRemote: OpenOnRemoteOptions | undefined = undefined;
@@ -71,5 +71,5 @@ export async function pleaseLogin(opts: {
closed: () => dispose(),
});
- throw new Error('signin required');
+ return false;
}
diff --git a/packages/frontend/src/utility/sensitive-file.ts b/packages/frontend/src/utility/sensitive-file.ts
new file mode 100644
index 0000000000..f1fc909e4a
--- /dev/null
+++ b/packages/frontend/src/utility/sensitive-file.ts
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import * as os from '@/os.js';
+import { prefer } from '@/preferences.js';
+import { i18n } from '@/i18n.js';
+
+export function shouldHideFileByDefault(file: Misskey.entities.DriveFile): boolean {
+ if (prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) {
+ return true;
+ }
+
+ if (file.isSensitive && prefer.s.nsfw !== 'ignore') {
+ return true;
+ }
+
+ return false;
+}
+
+export async function canRevealFile(file: Misskey.entities.DriveFile): Promise<boolean> {
+ if (file.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
+ const { canceled } = await os.confirm({
+ type: 'question',
+ text: i18n.ts.sensitiveMediaRevealConfirm,
+ });
+ if (canceled) return false;
+ }
+
+ return true;
+}
diff --git a/packages/frontend/src/utility/snowfall-effect.ts b/packages/frontend/src/utility/snowfall-effect.ts
index cefa720ebf..aa86db6bd1 100644
--- a/packages/frontend/src/utility/snowfall-effect.ts
+++ b/packages/frontend/src/utility/snowfall-effect.ts
@@ -21,7 +21,7 @@ export class SnowfallEffect {
}>;
private uniforms: Record<string, {
type: string;
- value: number[] | Float32Array;
+ value: number | number[] | Float32Array;
location: WebGLUniformLocation;
}>;
private texture: WebGLTexture;
@@ -44,9 +44,9 @@ export class SnowfallEffect {
start: number;
previous: number;
} = {
- start: 0,
- previous: 0,
- };
+ start: 0,
+ previous: 0,
+ };
private raf = 0;
private density: number = 1 / 90;
@@ -90,7 +90,7 @@ export class SnowfallEffect {
mat2: 'uniformMatrix2fv',
mat3: 'uniformMatrix3fv',
mat4: 'uniformMatrix4fv',
- };
+ } as const;
private CAMERA = {
fov: 60,
@@ -167,7 +167,7 @@ export class SnowfallEffect {
return { ...this.WIND };
}
- private initShader(type, source): WebGLShader {
+ private initShader(type: number, source: string): WebGLShader {
const { gl } = this;
const shader = gl.createShader(type);
if (shader == null) throw new Error('Failed to create shader');
@@ -224,7 +224,7 @@ export class SnowfallEffect {
}
}
- private setBuffer(name: string, value?) {
+ private setBuffer(name: string, value?: number[] | undefined) {
const { gl, buffers } = this;
const buffer = buffers[name];
@@ -253,18 +253,18 @@ export class SnowfallEffect {
}
}
- private setUniform(name: string, value?) {
+ private setUniform(name: string, value?: number | number[] | Float32Array<ArrayBufferLike> | undefined) {
const { gl, uniforms } = this;
const uniform = uniforms[name];
- const setter = this.UNIFORM_SETTERS[uniform.type];
+ const setter = this.UNIFORM_SETTERS[uniform.type as keyof typeof this.UNIFORM_SETTERS];
const isMatrix = /^mat[2-4]$/i.test(uniform.type);
uniform.value = value ?? uniform.value;
if (isMatrix) {
- gl[setter](uniform.location, false, uniform.value);
+ (gl as any)[setter](uniform.location, false, uniform.value);
} else {
- gl[setter](uniform.location, uniform.value);
+ (gl as any)[setter](uniform.location, uniform.value);
}
}
diff --git a/packages/frontend/src/utility/sound.ts b/packages/frontend/src/utility/sound.ts
index 8e79841647..303244d126 100644
--- a/packages/frontend/src/utility/sound.ts
+++ b/packages/frontend/src/utility/sound.ts
@@ -111,7 +111,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
try {
response = await window.fetch(url);
- } catch (err) {
+ } catch (_) {
return;
}
diff --git a/packages/frontend/src/utility/storage.ts b/packages/frontend/src/utility/storage.ts
index 9df3a251e6..42743f78ea 100644
--- a/packages/frontend/src/utility/storage.ts
+++ b/packages/frontend/src/utility/storage.ts
@@ -3,14 +3,24 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { computed, ref, shallowRef, watch, defineAsyncComponent } from 'vue';
+import { readonly, ref } from 'vue';
import * as os from '@/os.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
-export const storagePersisted = ref(await navigator.storage.persisted());
+export const storagePersistenceSupported = window.isSecureContext && 'storage' in navigator;
+const storagePersisted = ref(false);
+
+export async function getStoragePersistenceStatusRef() {
+ if (storagePersistenceSupported) {
+ storagePersisted.value = await navigator.storage.persisted().catch(() => false);
+ }
+
+ return readonly(storagePersisted);
+}
export async function enableStoragePersistence() {
+ if (!storagePersistenceSupported) return;
try {
const persisted = await navigator.storage.persist();
if (persisted) {
diff --git a/packages/frontend/src/utility/timeline-date-separate.ts b/packages/frontend/src/utility/timeline-date-separate.ts
index 33ddea048b..de71b8ce11 100644
--- a/packages/frontend/src/utility/timeline-date-separate.ts
+++ b/packages/frontend/src/utility/timeline-date-separate.ts
@@ -104,7 +104,7 @@ export function makeDateGroupedTimelineComputedRef<T extends { id: string; creat
for (let i = 0; i < items.value.length; i++) {
const item = items.value[i];
const date = new Date(item.createdAt);
- const nextDate = items.value[i + 1] ? new Date(items.value[i + 1].createdAt) : null;
+ const _nextDate = items.value[i + 1] ? new Date(items.value[i + 1].createdAt) : null;
if (tl.length === 0 || (
span === 'day' && tl[tl.length - 1].date.getTime() !== date.getTime()
diff --git a/packages/frontend/src/utility/tour.ts b/packages/frontend/src/utility/tour.ts
index c6bfa35a66..b14486e953 100644
--- a/packages/frontend/src/utility/tour.ts
+++ b/packages/frontend/src/utility/tour.ts
@@ -13,7 +13,7 @@ type TourStep = {
};
export function startTour(steps: TourStep[]) {
- return new Promise<void>(async (resolve) => {
+ return new Promise<void>((resolve) => {
const currentStepIndex = ref(0);
const titleRef = ref(steps[0].title);
const descriptionRef = ref(steps[0].description);
diff --git a/packages/frontend/src/widgets/WidgetActivity.chart.vue b/packages/frontend/src/widgets/WidgetActivity.chart.vue
index e708343b3a..bab688f851 100644
--- a/packages/frontend/src/widgets/WidgetActivity.chart.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.chart.vue
@@ -53,19 +53,27 @@ const pointsReply = ref<string>();
const pointsRenote = ref<string>();
const pointsTotal = ref<string>();
-function dragListen(fn) {
+function dragListen(fn: (ev: MouseEvent | TouchEvent) => void) {
window.addEventListener('mousemove', fn);
window.addEventListener('mouseleave', dragClear.bind(null, fn));
window.addEventListener('mouseup', dragClear.bind(null, fn));
}
-function dragClear(fn) {
+function dragClear(fn: (ev: MouseEvent | TouchEvent) => void) {
window.removeEventListener('mousemove', fn);
- window.removeEventListener('mouseleave', dragClear);
- window.removeEventListener('mouseup', dragClear);
+ window.removeEventListener('mouseleave', dragClear as any);
+ window.removeEventListener('mouseup', dragClear as any);
}
-function onMousedown(ev) {
+function getPositionX(event: MouseEvent | TouchEvent) {
+ return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientX : 'clientX' in event ? event.clientX : 0;
+}
+
+function getPositionY(event: MouseEvent | TouchEvent) {
+ return 'touches' in event && event.touches.length > 0 ? event.touches[0].clientY : 'clientY' in event ? event.clientY : 0;
+}
+
+function onMousedown(ev: MouseEvent) {
const clickX = ev.clientX;
const clickY = ev.clientY;
const baseZoom = zoom.value;
@@ -73,8 +81,11 @@ function onMousedown(ev) {
// 動かした時
dragListen(me => {
- let moveLeft = me.clientX - clickX;
- let moveTop = me.clientY - clickY;
+ const x = getPositionX(me);
+ const y = getPositionY(me);
+
+ let moveLeft = x - clickX;
+ let moveTop = y - clickY;
zoom.value = Math.max(1, baseZoom + (-moveTop / 20));
pos.value = Math.min(0, basePos + moveLeft);
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index 9625abb4d1..3d0f4657b1 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -38,10 +38,12 @@ const name = 'activity';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
view: {
diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue
index 3951de1d84..c2a41b6257 100644
--- a/packages/frontend/src/widgets/WidgetAichan.vue
+++ b/packages/frontend/src/widgets/WidgetAichan.vue
@@ -12,14 +12,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, onUnmounted, useTemplateRef } from 'vue';
import { useWidgetPropsManager } from './widget.js';
+import { i18n } from '@/i18n.js';
import type { WidgetComponentProps, WidgetComponentEmits, WidgetComponentExpose } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
-const name = 'ai';
+const name = 'aichan';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index 795c5a2cfa..c6cafb270f 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -37,10 +37,12 @@ const name = 'aiscript';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
script: {
type: 'string',
+ label: i18n.ts.script,
multiline: true,
default: '(1 + 1)',
hidden: true,
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index 18acd966fd..9ed441b77c 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -24,6 +24,7 @@ import type { AsUiComponent, AsUiRoot } from '@/aiscript/ui.js';
import * as os from '@/os.js';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
import { $i } from '@/i.js';
+import { i18n } from '@/i18n.js';
import MkAsUi from '@/components/MkAsUi.vue';
import MkContainer from '@/components/MkContainer.vue';
import { registerAsUiLib } from '@/aiscript/ui.js';
@@ -33,11 +34,14 @@ const name = 'aiscriptApp';
const widgetPropsDef = {
script: {
type: 'string',
+ label: i18n.ts.script,
multiline: true,
+ manualSave: true,
default: '',
},
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.user.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.user.vue
new file mode 100644
index 0000000000..2b714c2f6c
--- /dev/null
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.user.vue
@@ -0,0 +1,86 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.root">
+ <MkA :to="userPage(item.user)" style="overflow: clip;">
+ <MkUserCardMini :user="item.user" :withChart="false" style="text-overflow: ellipsis; background: inherit; border-radius: unset;">
+ <template #sub>
+ <span>{{ countdownDate }}</span>
+ <span> / </span>
+ <span class="_monospace">@{{ acct(item.user) }}</span>
+ </template>
+ </MkUserCardMini>
+ </MkA>
+ <button v-tooltip.noDelay="i18n.ts.note" class="_button" :class="$style.post" @click="os.post({initialText: `@${item.user.username}${item.user.host ? `@${item.user.host}` : ''} `})">
+ <i class="ti-fw ti ti-confetti" :class="$style.postIcon"></i>
+ </button>
+</div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkUserCardMini from '@/components/MkUserCardMini.vue';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { useLowresTime } from '@/composables/use-lowres-time.js';
+import { userPage, acct } from '@/filters/user.js';
+
+const props = defineProps<{
+ item: Misskey.entities.UsersGetFollowingUsersByBirthdayResponse[number];
+}>();
+
+const now = useLowresTime();
+const nowDate = computed(() => {
+ const date = new Date(now.value);
+ date.setHours(0, 0, 0, 0);
+ return date;
+});
+const birthdayDate = computed(() => {
+ const [year, month, day] = props.item.birthday.split('-').map((v) => parseInt(v, 10));
+ return new Date(year, month - 1, day, 0, 0, 0, 0);
+});
+
+const countdownDate = computed(() => {
+ const days = Math.floor((birthdayDate.value.getTime() - nowDate.value.getTime()) / (1000 * 60 * 60 * 24));
+ if (days === 0) {
+ return i18n.ts.today;
+ } else if (days > 0) {
+ return i18n.tsx._timeIn.days({ n: days });
+ } else {
+ return i18n.tsx._ago.daysAgo({ n: Math.abs(days) });
+ }
+});
+</script>
+
+<style lang="scss" module>
+.root {
+ box-sizing: border-box;
+ display: grid;
+ align-items: center;
+ grid-template-columns: auto 56px;
+}
+
+.post {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 40px;
+ width: 40px;
+ margin-right: 16px;
+ aspect-ratio: 1/1;
+ border-radius: 100%;
+ background: linear-gradient(90deg, var(--MI_THEME-buttonGradateA), var(--MI_THEME-buttonGradateB));
+
+ &:hover {
+ background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
+ }
+}
+
+.postIcon {
+ color: var(--MI_THEME-fgOnAccent);
+}
+</style>
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
index d1991cd70a..cf9c5a3d35 100644
--- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
@@ -4,42 +4,75 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkContainer :showHeader="widgetProps.showHeader" class="mkw-bdayfollowings">
+<MkContainer :style="`height: ${widgetProps.height}px;`" :showHeader="widgetProps.showHeader" :scrollable="true" class="mkw-bdayfollowings">
<template #icon><i class="ti ti-cake"></i></template>
<template #header>{{ i18n.ts._widgets.birthdayFollowings }}</template>
- <template #func="{ buttonStyleClass }"><button class="_button" :class="buttonStyleClass" @click="actualFetch()"><i class="ti ti-refresh"></i></button></template>
+ <template #func="{ buttonStyleClass }"><button class="_button" :class="buttonStyleClass" @click="fetch"><i class="ti ti-refresh"></i></button></template>
- <div :class="$style.bdayFRoot">
- <MkLoading v-if="fetching"/>
- <div v-else-if="users.length > 0" :class="$style.bdayFGrid">
- <MkAvatar v-for="user in users" :key="user.id" :user="user.followee!" link preview></MkAvatar>
+ <MkPagination v-slot="{ items }" :paginator="birthdayUsersPaginator">
+ <div>
+ <template v-for="(user, i) in items" :key="user.id">
+ <div
+ v-if="i > 0 && isSeparatorNeeded(birthdayUsersPaginator.items.value[i - 1].birthday, user.birthday)"
+ >
+ <div :class="$style.date">
+ <span><i class="ti ti-chevron-up"></i> {{ getSeparatorInfo(birthdayUsersPaginator.items.value[i - 1].birthday, user.birthday)?.prevText }}</span>
+ <span style="height: 1em; width: 1px; background: var(--MI_THEME-divider);"></span>
+ <span>{{ getSeparatorInfo(birthdayUsersPaginator.items.value[i - 1].birthday, user.birthday)?.nextText }} <i class="ti ti-chevron-down"></i></span>
+ </div>
+ <XUser :class="$style.user" :item="user" />
+ </div>
+ <XUser v-else :class="$style.user" :item="user" />
+ </template>
</div>
- <div v-else :class="$style.bdayFFallback">
- <MkResult type="empty"/>
- </div>
- </div>
+ </MkPagination>
</MkContainer>
</template>
<script lang="ts" setup>
-import { ref } from 'vue';
-import * as Misskey from 'misskey-js';
-import { useInterval } from '@@/js/use-interval.js';
+import { computed, markRaw, ref, watch } from 'vue';
+import { useLowresTime } from '@/composables/use-lowres-time.js';
+import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-separate.js';
import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import MkContainer from '@/components/MkContainer.vue';
-import { misskeyApi } from '@/utility/misskey-api.js';
+import MkPagination from '@/components/MkPagination.vue';
+import XUser from './WidgetBirthdayFollowings.user.vue';
import { i18n } from '@/i18n.js';
-import { $i } from '@/i.js';
+import { Paginator } from '@/utility/paginator.js';
-const name = i18n.ts._widgets.birthdayFollowings;
+const name = 'birthdayFollowings';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
+ height: {
+ type: 'number' as const,
+ label: i18n.ts._widgetOptions.height,
+ default: 300,
+ },
+ period: {
+ type: 'radio' as const,
+ label: i18n.ts._widgetOptions._birthdayFollowings.period,
+ default: '3day',
+ options: [{
+ value: 'today' as const,
+ label: i18n.ts.today,
+ }, {
+ value: '3day' as const,
+ label: i18n.tsx.dayX({ day: 3 }),
+ }, {
+ value: 'week' as const,
+ label: i18n.ts.oneWeek,
+ }, {
+ value: 'month' as const,
+ label: i18n.ts.oneMonth,
+ }],
+ },
} satisfies FormWithDefault;
type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
@@ -47,62 +80,84 @@ type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
const props = defineProps<WidgetComponentProps<WidgetProps>>();
const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
-const { widgetProps, configure } = useWidgetPropsManager(name,
+const { widgetProps, configure } = useWidgetPropsManager(
+ name,
widgetPropsDef,
props,
emit,
);
-const users = ref<Misskey.Endpoints['users/following']['res']>([]);
-const fetching = ref(true);
-let lastFetchedAt = '1970-01-01';
+const now = useLowresTime();
+const nextDay = new Date();
+nextDay.setHours(24, 0, 0, 0);
+let nextDayMidnightTime = nextDay.getTime();
-const fetch = () => {
- if (!$i) {
- users.value = [];
- fetching.value = false;
- return;
+const begin = ref<Date>(new Date());
+const end = computed(() => {
+ switch (widgetProps.period) {
+ case '3day':
+ return new Date(begin.value.getTime() + 1000 * 60 * 60 * 24 * 3);
+ case 'week':
+ return new Date(begin.value.getTime() + 1000 * 60 * 60 * 24 * 7);
+ case 'month':
+ return new Date(begin.value.getTime() + 1000 * 60 * 60 * 24 * 30);
+ default:
+ return begin.value;
}
+});
+
+const birthdayUsersPaginator = markRaw(new Paginator('users/get-following-users-by-birthday', {
+ limit: 18,
+ offsetMode: true,
+ computedParams: computed(() => {
+ if (widgetProps.period === 'today') {
+ return {
+ birthday: {
+ month: begin.value.getMonth() + 1,
+ day: begin.value.getDate(),
+ },
+ };
+ } else {
+ return {
+ birthday: {
+ begin: {
+ month: begin.value.getMonth() + 1,
+ day: begin.value.getDate(),
+ },
+ end: {
+ month: end.value.getMonth() + 1,
+ day: end.value.getDate(),
+ },
+ },
+ };
+ }
+ }),
+}));
- const lfAtD = new Date(lastFetchedAt);
- lfAtD.setHours(0, 0, 0, 0);
+function fetch() {
const now = new Date();
- now.setHours(0, 0, 0, 0);
+ begin.value = now;
+}
- if (now > lfAtD) {
- actualFetch();
+const UPDATE_INTERVAL = 1000 * 60;
+let nextDayTimer: number | null = null;
- lastFetchedAt = now.toISOString();
- }
-};
+watch(now, (to) => {
+ // 次回更新までに日付が変わる場合、日付が変わった直後に強制的に更新するタイマーをセットする
+ if (nextDayMidnightTime - to <= UPDATE_INTERVAL) {
+ if (nextDayTimer != null) {
+ window.clearTimeout(nextDayTimer);
+ nextDayTimer = null;
+ }
-function actualFetch() {
- if ($i == null) {
- users.value = [];
- fetching.value = false;
- return;
+ nextDayTimer = window.setTimeout(() => {
+ fetch();
+ nextDay.setHours(24, 0, 0, 0);
+ nextDayMidnightTime = nextDay.getTime();
+ nextDayTimer = null;
+ }, nextDayMidnightTime - to);
}
-
- const now = new Date();
- now.setHours(0, 0, 0, 0);
- fetching.value = true;
- misskeyApi('users/following', {
- limit: 18,
- birthday: `${now.getFullYear().toString().padStart(4, '0')}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`,
- userId: $i.id,
- }).then(res => {
- users.value = res;
- window.setTimeout(() => {
- // 早すぎるとチカチカする
- fetching.value = false;
- }, 100);
- });
-}
-
-useInterval(fetch, 1000 * 60, {
- immediate: true,
- afterMounted: true,
-});
+}, { immediate: true });
defineExpose<WidgetComponentExpose>({
name,
@@ -112,24 +167,24 @@ defineExpose<WidgetComponentExpose>({
</script>
<style lang="scss" module>
-.bdayFRoot {
- overflow: hidden;
- min-height: calc(calc(calc(50px * 3) - 8px) + calc(var(--MI-margin) * 2));
+.root {
+ container-type: inline-size;
+ background: var(--MI_THEME-panel);
}
-.bdayFGrid {
- display: grid;
- grid-template-columns: repeat(6, 42px);
- grid-template-rows: repeat(3, 42px);
- place-content: center;
- gap: 8px;
- margin: var(--MI-margin) auto;
+
+.user {
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
}
-.bdayFFallback {
- height: 100%;
+.date {
display: flex;
- flex-direction: column;
- justify-content: center;
+ font-size: 85%;
align-items: center;
+ justify-content: center;
+ gap: 1em;
+ opacity: 0.75;
+ padding: 8px 8px;
+ margin: 0 auto;
+ border-bottom: solid 0.5px var(--MI_THEME-divider);
}
</style>
diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue
index f8ae03c5fd..223901390e 100644
--- a/packages/frontend/src/widgets/WidgetButton.vue
+++ b/packages/frontend/src/widgets/WidgetButton.vue
@@ -20,22 +20,26 @@ import * as os from '@/os.js';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
import { $i } from '@/i.js';
import MkButton from '@/components/MkButton.vue';
+import { i18n } from '@/i18n.js';
const name = 'button';
const widgetPropsDef = {
label: {
type: 'string',
+ label: i18n.ts.label,
default: 'BUTTON',
},
colored: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._button.colored,
default: true,
},
script: {
type: 'string',
+ label: i18n.ts.script,
multiline: true,
- default: 'Mk:dialog("hello" "world")',
+ default: 'Mk:dialog("hello", "world")',
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index f2321ca9fa..28336cbe09 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -50,6 +50,7 @@ const name = 'calendar';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetChat.vue b/packages/frontend/src/widgets/WidgetChat.vue
index 8fee7f00f6..06d8f741f4 100644
--- a/packages/frontend/src/widgets/WidgetChat.vue
+++ b/packages/frontend/src/widgets/WidgetChat.vue
@@ -29,6 +29,7 @@ const name = 'chat';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue
index 282a1a6d93..614e7c7fe5 100644
--- a/packages/frontend/src/widgets/WidgetClicker.vue
+++ b/packages/frontend/src/widgets/WidgetClicker.vue
@@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
+import { i18n } from '@/i18n.js';
import MkContainer from '@/components/MkContainer.vue';
import MkClickerGame from '@/components/MkClickerGame.vue';
@@ -23,6 +24,7 @@ const name = 'clicker';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: false,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue
index 7aa69a39b5..80f312e7c4 100644
--- a/packages/frontend/src/widgets/WidgetClock.vue
+++ b/packages/frontend/src/widgets/WidgetClock.vue
@@ -44,10 +44,12 @@ const name = 'clock';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
size: {
type: 'radio',
+ label: i18n.ts._widgetOptions._clock.size,
default: 'medium',
options: [{
value: 'small' as const,
@@ -62,79 +64,89 @@ const widgetPropsDef = {
},
thickness: {
type: 'radio',
+ label: i18n.ts._widgetOptions._clock.thickness,
default: 0.2,
options: [{
value: 0.1 as const,
- label: 'thin',
+ label: i18n.ts._widgetOptions._clock.thicknessThin,
}, {
value: 0.2 as const,
- label: 'medium',
+ label: i18n.ts._widgetOptions._clock.thicknessMedium,
}, {
value: 0.3 as const,
- label: 'thick',
+ label: i18n.ts._widgetOptions._clock.thicknessThick,
}],
},
graduations: {
type: 'radio',
+ label: i18n.ts._widgetOptions._clock.graduations,
default: 'numbers',
options: [{
value: 'none' as const,
- label: 'None',
+ label: i18n.ts.none,
}, {
value: 'dots' as const,
- label: 'Dots',
+ label: i18n.ts._widgetOptions._clock.graduationDots,
}, {
value: 'numbers' as const,
- label: 'Numbers',
- }],
+ label: i18n.ts._widgetOptions._clock.graduationArabic,
+ }, /*, {
+ value: 'roman' as const,
+ label: i18n.ts._widgetOptions._clock.graduationRoman,
+ }*/],
},
fadeGraduations: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._clock.fadeGraduations,
default: true,
},
sAnimation: {
type: 'radio',
+ label: i18n.ts._widgetOptions._clock.sAnimation,
default: 'elastic',
options: [{
value: 'none' as const,
- label: 'None',
+ label: i18n.ts.none,
}, {
value: 'elastic' as const,
- label: 'Elastic',
+ label: i18n.ts._widgetOptions._clock.sAnimationElastic,
}, {
value: 'easeOut' as const,
- label: 'Ease out',
+ label: i18n.ts._widgetOptions._clock.sAnimationEaseOut,
}],
},
twentyFour: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._clock.twentyFour,
default: false,
},
label: {
type: 'radio',
+ label: i18n.ts.label,
default: 'none',
options: [{
value: 'none' as const,
- label: 'None',
+ label: i18n.ts.none,
}, {
value: 'time' as const,
- label: 'Time',
+ label: i18n.ts._widgetOptions._clock.labelTime,
}, {
value: 'tz' as const,
- label: 'TZ',
+ label: i18n.ts._widgetOptions._clock.labelTz,
}, {
value: 'timeAndTz' as const,
- label: 'Time + TZ',
+ label: i18n.ts._widgetOptions._clock.labelTimeAndTz,
}],
},
timezone: {
type: 'enum',
+ label: i18n.ts._widgetOptions._clock.timezone,
default: null,
enum: [...timezones.map((tz) => ({
label: tz.name,
value: tz.name.toLowerCase(),
})), {
- label: '(auto)',
+ label: i18n.ts.auto,
value: null,
}],
},
diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue
index b8cbc6429c..d50d4aef62 100644
--- a/packages/frontend/src/widgets/WidgetDigitalClock.vue
+++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue
@@ -19,6 +19,7 @@ import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import { timezones } from '@/utility/timezones.js';
+import { i18n } from '@/i18n.js';
import MkDigitalClock from '@/components/MkDigitalClock.vue';
const name = 'digitalClock';
@@ -26,29 +27,34 @@ const name = 'digitalClock';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
fontSize: {
type: 'number',
+ label: i18n.ts.fontSize,
default: 1.5,
step: 0.1,
},
showMs: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._clock.showMs,
default: true,
},
showLabel: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._clock.showLabel,
default: true,
},
timezone: {
type: 'enum',
+ label: i18n.ts._widgetOptions._clock.timezone,
default: null,
enum: [...timezones.map((tz) => ({
label: tz.name,
value: tz.name.toLowerCase(),
})), {
- label: '(auto)',
+ label: i18n.ts.auto,
value: null,
}],
},
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index 3e880af03b..cf2c2f0ab7 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -43,6 +43,7 @@ const name = 'federation';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
} satisfies FormWithDefault;
@@ -62,7 +63,7 @@ const instances = ref<Misskey.entities.FederationInstance[]>([]);
const charts = ref<Misskey.entities.ChartsInstanceResponse[]>([]);
const fetching = ref(true);
-const fetch = async () => {
+async function fetchInstances() {
const fetchedInstances = await misskeyApi('federation/instances', {
sort: '+latestRequestReceivedAt',
limit: 5,
@@ -71,14 +72,14 @@ const fetch = async () => {
instances.value = fetchedInstances;
charts.value = fetchedCharts;
fetching.value = false;
-};
+}
-useInterval(fetch, 1000 * 60, {
+useInterval(fetchInstances, 1000 * 60, {
immediate: true,
afterMounted: true,
});
-function getInstanceIcon(instance): string {
+function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png';
}
diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
index 8053dd43cf..c1e864bdb3 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -29,12 +29,14 @@ import MkTagCloud from '@/components/MkTagCloud.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js';
+import { i18n } from '@/i18n.js';
const name = 'instanceCloud';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
} satisfies FormWithDefault;
@@ -53,7 +55,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
const cloud = useTemplateRef('cloud');
const activeInstances = shallowRef<Misskey.entities.FederationInstance[] | null>(null);
-function onInstanceClick(i) {
+function onInstanceClick(i: Misskey.entities.FederationInstance) {
os.pageWindow(`/instance-info/${i.host}`);
}
@@ -70,7 +72,7 @@ useInterval(() => {
afterMounted: true,
});
-function getInstanceIcon(instance): string {
+function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png';
}
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index fba7d82062..1727ea9b74 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onUnmounted, reactive, ref } from 'vue';
+import * as Misskey from 'misskey-js';
import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
@@ -61,16 +62,19 @@ import * as sound from '@/utility/sound.js';
import { deepClone } from '@/utility/clone.js';
import { prefer } from '@/preferences.js';
import { genId } from '@/utility/id.js';
+import { i18n } from '@/i18n.js';
const name = 'jobQueue';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
sound: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._jobQueue.sound,
default: false,
},
} satisfies FormWithDefault;
@@ -113,20 +117,22 @@ if (prefer.s['sound.masterVolume']) {
}
for (const domain of ['inbox', 'deliver']) {
- prev[domain] = deepClone(current[domain]);
+ const d = domain as 'inbox' | 'deliver';
+ prev[d] = deepClone(current[d]);
}
-const onStats = (stats) => {
+const onStats = (stats: Misskey.entities.QueueStats) => {
for (const domain of ['inbox', 'deliver']) {
- prev[domain] = deepClone(current[domain]);
- current[domain].activeSincePrevTick = stats[domain].activeSincePrevTick;
- current[domain].active = stats[domain].active;
- current[domain].waiting = stats[domain].waiting;
- current[domain].delayed = stats[domain].delayed;
+ const d = domain as 'inbox' | 'deliver';
+ prev[d] = deepClone(current[d]);
+ current[d].activeSincePrevTick = stats[d].activeSincePrevTick;
+ current[d].active = stats[d].active;
+ current[d].waiting = stats[d].waiting;
+ current[d].delayed = stats[d].delayed;
- if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) {
+ if (current[d].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) {
const soundNode = sound.createSourceNode(jammedAudioBuffer.value, {}).soundSource;
- if (soundNode) {
+ if (soundNode != null) {
jammedSoundNodePlaying.value = true;
soundNode.onended = () => jammedSoundNodePlaying.value = false;
soundNode.start();
@@ -135,7 +141,7 @@ const onStats = (stats) => {
}
};
-const onStatsLog = (statsLog) => {
+const onStatsLog = (statsLog: Misskey.entities.QueueStatsLog) => {
for (const stats of [...statsLog].reverse()) {
onStats(stats);
}
diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue
index 2beca8c43a..fd5b56991e 100644
--- a/packages/frontend/src/widgets/WidgetMemo.vue
+++ b/packages/frontend/src/widgets/WidgetMemo.vue
@@ -29,10 +29,12 @@ const name = 'memo';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
height: {
type: 'number',
+ label: i18n.ts.height,
default: 100,
},
} satisfies FormWithDefault;
@@ -50,7 +52,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
const text = ref<string | null>(store.s.memo);
const changed = ref(false);
-let timeoutId;
+let timeoutId: number | null = null;
const saveMemo = () => {
store.set('memo', text.value);
@@ -59,7 +61,7 @@ const saveMemo = () => {
const onChange = () => {
changed.value = true;
- window.clearTimeout(timeoutId);
+ if (timeoutId != null) window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(saveMemo, 1000);
};
diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue
index b588bcb029..48a29d6145 100644
--- a/packages/frontend/src/widgets/WidgetNotifications.vue
+++ b/packages/frontend/src/widgets/WidgetNotifications.vue
@@ -31,10 +31,12 @@ const name = 'notifications';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
height: {
type: 'number',
+ label: i18n.ts.height,
default: 300,
},
excludeTypes: {
diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
index 9fd8c013d1..b0bb4b47b1 100644
--- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue
+++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
@@ -28,6 +28,7 @@ const name = 'onlineUsers';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: true,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue
index e89a642b99..670e764c8c 100644
--- a/packages/frontend/src/widgets/WidgetPhotos.vue
+++ b/packages/frontend/src/widgets/WidgetPhotos.vue
@@ -39,10 +39,12 @@ const name = 'photos';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
} satisfies FormWithDefault;
@@ -62,12 +64,12 @@ const connection = useStream().useChannel('main');
const images = ref<Misskey.entities.DriveFile[]>([]);
const fetching = ref(true);
-const onDriveFileCreated = (file) => {
+function onDriveFileCreated(file: Misskey.entities.DriveFile) {
if (/^image\/.+$/.test(file.type)) {
images.value.unshift(file);
if (images.value.length > 9) images.value.pop();
}
-};
+}
const thumbnail = (image: Misskey.entities.DriveFile): string => {
return prefer.s.disableShowingAnimatedImages
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index e5499aa0da..2495c5a6e9 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -25,28 +25,33 @@ import * as Misskey from 'misskey-js';
import { url as base } from '@@/js/config.js';
import { useInterval } from '@@/js/use-interval.js';
import { useWidgetPropsManager } from './widget.js';
+import { i18n } from '@/i18n.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import MkContainer from '@/components/MkContainer.vue';
-import { i18n } from '@/i18n.js';
const name = 'rss';
const widgetPropsDef = {
url: {
type: 'string',
+ label: i18n.ts._widgetOptions._rss.url,
default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews',
+ manualSave: true,
},
refreshIntervalSec: {
type: 'number',
+ label: i18n.ts._widgetOptions._rss.refreshIntervalSec,
default: 60,
},
maxEntries: {
type: 'number',
+ label: i18n.ts._widgetOptions._rss.maxEntries,
default: 15,
},
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
} satisfies FormWithDefault;
@@ -68,7 +73,7 @@ const fetching = ref(true);
const fetchEndpoint = computed(() => {
const url = new URL('/api/fetch-rss', base);
url.searchParams.set('url', widgetProps.url);
- return url;
+ return url.toString();
});
const intervalClear = ref<(() => void) | undefined>();
@@ -83,7 +88,7 @@ const tick = () => {
});
};
-watch(() => fetchEndpoint, tick);
+watch(fetchEndpoint, tick);
watch(() => widgetProps.refreshIntervalSec, () => {
if (intervalClear.value) {
intervalClear.value();
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index 95f82f7d7b..9ed21e6d00 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -35,6 +35,7 @@ import MkMarqueeText from '@/components/MkMarqueeText.vue';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
import MkContainer from '@/components/MkContainer.vue';
import { shuffle } from '@/utility/shuffle.js';
+import { i18n } from '@/i18n.js';
import { url as base } from '@@/js/config.js';
import { useInterval } from '@@/js/use-interval.js';
@@ -43,22 +44,28 @@ const name = 'rssTicker';
const widgetPropsDef = {
url: {
type: 'string',
+ label: i18n.ts._widgetOptions._rss.url,
default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews',
+ manualSave: true,
},
shuffle: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._rssTicker.shuffle,
default: true,
},
refreshIntervalSec: {
type: 'number',
+ label: i18n.ts._widgetOptions._rss.refreshIntervalSec,
default: 60,
},
maxEntries: {
type: 'number',
+ label: i18n.ts._widgetOptions._rss.maxEntries,
default: 15,
},
duration: {
type: 'range',
+ label: i18n.ts._widgetOptions._rssTicker.duration,
default: 70,
step: 1,
min: 5,
@@ -66,14 +73,17 @@ const widgetPropsDef = {
},
reverse: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._rssTicker.reverse,
default: false,
},
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: false,
},
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
} satisfies FormWithDefault;
@@ -119,7 +129,7 @@ const tick = () => {
});
};
-watch(() => fetchEndpoint, tick);
+watch(fetchEndpoint, tick);
watch(() => widgetProps.refreshIntervalSec, () => {
if (intervalClear.value) {
intervalClear.value();
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index 240210c1fb..b812c89e08 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -33,6 +33,7 @@ const name = 'slideshow';
const widgetPropsDef = {
height: {
type: 'number',
+ label: i18n.ts._widgetOptions.height,
default: 300,
},
folderId: {
@@ -95,11 +96,11 @@ const fetch = () => {
};
const choose = () => {
- selectDriveFolder(null).then(folder => {
- if (folder[0] == null) {
+ selectDriveFolder(null).then(({ folders, canceled }) => {
+ if (canceled || folders[0] == null) {
return;
}
- widgetProps.folderId = folder[0].id;
+ widgetProps.folderId = folders[0].id;
save();
fetch();
});
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index 6c775fd98c..83b8e7ccbc 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -41,13 +41,13 @@ import * as Misskey from 'misskey-js';
import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
+import type { MenuItem } from '@/types/menu.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import MkContainer from '@/components/MkContainer.vue';
import MkStreamingNotesTimeline from '@/components/MkStreamingNotesTimeline.vue';
import { i18n } from '@/i18n.js';
import { availableBasicTimelines, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass, basicTimelineTypes } from '@/timelines.js';
-import type { MenuItem } from '@/types/menu.js';
const name = 'timeline';
@@ -93,12 +93,12 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
const menuOpened = ref(false);
const headerTitle = computed<string>(() => {
- if (widgetProps.src === 'list' && widgetProps.list != null) {
- return widgetProps.list.name;
- } else if (widgetProps.src === 'antenna' && widgetProps.antenna != null) {
- return widgetProps.antenna.name;
+ if (widgetProps.src === 'list') {
+ return widgetProps.list != null ? widgetProps.list.name : '?';
+ } else if (widgetProps.src === 'antenna') {
+ return widgetProps.antenna != null ? widgetProps.antenna.name : '?';
} else {
- return i18n.ts._timelines[widgetProps.src];
+ return i18n.ts._timelines[widgetProps.src] ?? '?';
}
});
@@ -107,7 +107,7 @@ const setSrc = (src: TlSrc) => {
save();
};
-const choose = async (ev: MouseEvent) => {
+const choose = async (ev: PointerEvent) => {
menuOpened.value = true;
const [antennas, lists] = await Promise.all([
misskeyApi('antennas/list'),
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index dcb900b0c9..498129305b 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -36,11 +36,12 @@ import { misskeyApiGet } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
-const name = 'hashtags';
+const name = 'trends';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue
index 226a4c73aa..1bb361664f 100644
--- a/packages/frontend/src/widgets/WidgetUnixClock.vue
+++ b/packages/frontend/src/widgets/WidgetUnixClock.vue
@@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onUnmounted, ref, watch } from 'vue';
import { useWidgetPropsManager } from './widget.js';
+import { i18n } from '@/i18n.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
@@ -26,19 +27,23 @@ const name = 'unixClock';
const widgetPropsDef = {
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
fontSize: {
type: 'number',
+ label: i18n.ts.fontSize,
default: 1.5,
step: 0.1,
},
showMs: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._clock.showMs,
default: true,
},
showLabel: {
type: 'boolean',
+ label: i18n.ts._widgetOptions._clock.showLabel,
default: true,
},
} satisfies FormWithDefault;
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index 9e914fa648..3fc46f303f 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -41,6 +41,7 @@ const name = 'userList';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
listId: {
diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts
index aea810d1ea..79bae68d71 100644
--- a/packages/frontend/src/widgets/index.ts
+++ b/packages/frontend/src/widgets/index.ts
@@ -42,7 +42,7 @@ export default function(app: App) {
export const federationWidgets = [
'federation',
'instanceCloud',
-];
+] as const;
export const widgets = [
'profile',
@@ -74,4 +74,6 @@ export const widgets = [
'chat',
...federationWidgets,
-];
+] as const;
+
+export type WidgetName = typeof widgets[number];
diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue
index f52b6fd12e..5d93c6a982 100644
--- a/packages/frontend/src/widgets/server-metric/index.vue
+++ b/packages/frontend/src/widgets/server-metric/index.vue
@@ -40,10 +40,12 @@ const name = 'serverMetric';
const widgetPropsDef = {
showHeader: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.showHeader,
default: true,
},
transparent: {
type: 'boolean',
+ label: i18n.ts._widgetOptions.transparent,
default: false,
},
view: {
diff --git a/packages/frontend/src/widgets/widget.ts b/packages/frontend/src/widgets/widget.ts
index c5ca7ac26c..bfb724ff72 100644
--- a/packages/frontend/src/widgets/widget.ts
+++ b/packages/frontend/src/widgets/widget.ts
@@ -3,12 +3,14 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { reactive, watch } from 'vue';
-import type { Reactive } from 'vue';
+import { defineAsyncComponent, reactive, watch } from 'vue';
import { throttle } from 'throttle-debounce';
+import type { Reactive } from 'vue';
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
+import { getDefaultFormValues } from '@/utility/form.js';
import * as os from '@/os.js';
import { deepClone } from '@/utility/clone.js';
+import type { WidgetName } from './index.js';
export type Widget<P extends Record<string, unknown>> = {
id: string;
@@ -20,7 +22,7 @@ export type WidgetComponentProps<P extends Record<string, unknown>> = {
};
export type WidgetComponentEmits<P extends Record<string, unknown>> = {
- (ev: 'updateProps', props: P);
+ (ev: 'updateProps', props: P): void;
};
export type WidgetComponentExpose = {
@@ -30,7 +32,7 @@ export type WidgetComponentExpose = {
};
export const useWidgetPropsManager = <F extends FormWithDefault>(
- name: string,
+ name: WidgetName,
propsDef: F,
props: Readonly<WidgetComponentProps<GetFormResultType<F>>>,
emit: WidgetComponentEmits<GetFormResultType<F>>,
@@ -39,19 +41,23 @@ export const useWidgetPropsManager = <F extends FormWithDefault>(
save: () => void;
configure: () => void;
} => {
- const widgetProps = reactive<GetFormResultType<F>>((props.widget ? deepClone(props.widget.data) : {}) as GetFormResultType<F>);
-
- const mergeProps = () => {
- for (const prop of Object.keys(propsDef)) {
- if (typeof widgetProps[prop] === 'undefined') {
- widgetProps[prop] = propsDef[prop].default;
+ const widgetProps = reactive((() => {
+ const np = getDefaultFormValues(propsDef);
+ if (props.widget?.data != null) {
+ for (const key of Object.keys(props.widget.data) as (keyof F)[]) {
+ np[key] = props.widget.data[key] as GetFormResultType<F>[typeof key];
}
}
- };
+ return np;
+ })());
- watch(widgetProps, () => {
- mergeProps();
- }, { deep: true, immediate: true });
+ watch(() => props.widget?.data, (to) => {
+ if (to != null) {
+ for (const key of Object.keys(propsDef)) {
+ (widgetProps as any)[key] = to[key];
+ }
+ }
+ }, { deep: true });
const save = throttle(3000, () => {
emit('updateProps', widgetProps as GetFormResultType<F>);
@@ -60,13 +66,38 @@ export const useWidgetPropsManager = <F extends FormWithDefault>(
const configure = async () => {
const form = deepClone(propsDef);
for (const item of Object.keys(form)) {
- form[item].default = widgetProps[item];
+ form[item].default = (widgetProps as any)[item];
+ }
+
+ const res = await new Promise<{
+ canceled: false;
+ result: GetFormResultType<F>;
+ } | {
+ canceled: true;
+ }>((resolve) => {
+ const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkWidgetSettingsDialog.vue')), {
+ widgetName: name,
+ form: form,
+ currentSettings: widgetProps,
+ }, {
+ saved: (newProps) => {
+ resolve({ canceled: false, result: newProps as GetFormResultType<F> });
+ },
+ canceled: () => {
+ resolve({ canceled: true });
+ },
+ closed: () => {
+ dispose();
+ },
+ });
+ });
+
+ if (res.canceled) {
+ return;
}
- const { canceled, result } = await os.form(name, form);
- if (canceled) return;
- for (const key of Object.keys(result)) {
- widgetProps[key] = result[key];
+ for (const key of Object.keys(res.result)) {
+ (widgetProps as any)[key] = res.result[key];
}
save();
diff --git a/packages/frontend/test/is-birthday.test.ts b/packages/frontend/test/is-birthday.test.ts
new file mode 100644
index 0000000000..a072db4416
--- /dev/null
+++ b/packages/frontend/test/is-birthday.test.ts
@@ -0,0 +1,55 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import { describe, test, expect } from 'vitest';
+import { isBirthday } from '@/utility/is-birthday.js';
+
+describe('isBirthday', () => {
+ test('通常の誕生日', () => {
+ const currentDate = new Date('2024-05-15');
+ const result = isBirthday({
+ birthday: '2000-05-15',
+ } as Misskey.entities.UserDetailed, currentDate);
+
+ expect(result).toBe(true);
+ });
+
+ test('誕生日ではない場合', () => {
+ const currentDate = new Date('2024-05-15');
+ const result = isBirthday({
+ birthday: '2000-06-20',
+ } as Misskey.entities.UserDetailed, currentDate);
+
+ expect(result).toBe(false);
+ });
+
+ test('平年に閏日生まれを見た際に3月1日を誕生日とする', () => {
+ const currentDate = new Date('2023-03-01');
+ const result = isBirthday({
+ birthday: '2000-02-29',
+ } as Misskey.entities.UserDetailed, currentDate);
+
+ expect(result).toBe(true);
+ });
+
+ test('閏年に閏日生まれを見た際に2月29日を誕生日とする', () => {
+ const currentDate = new Date('2024-02-29');
+ const result = isBirthday({
+ birthday: '2000-02-29',
+ } as Misskey.entities.UserDetailed, currentDate);
+
+ expect(result).toBe(true);
+ });
+
+ test('閏年に閏日生まれを見た際に3月1日を誕生日としない', () => {
+ const currentDate = new Date('2024-03-01');
+ const result = isBirthday({
+ birthday: '2000-02-29',
+ } as Misskey.entities.UserDetailed, currentDate);
+
+ expect(result).toBe(false);
+ });
+});
diff --git a/packages/frontend/test/scroll.test.ts b/packages/frontend/test/scroll.test.ts
index 34e7e64313..11029c71ee 100644
--- a/packages/frontend/test/scroll.test.ts
+++ b/packages/frontend/test/scroll.test.ts
@@ -25,7 +25,7 @@ describe('Scroll', () => {
*/
test('No onScrollTop callback for disconnected elements', () => {
- const { document } = new Window();
+ const { document: _ } = new Window();
const div = window.document.createElement('div');
assert.strictEqual(div.scrollTop, 0);
@@ -53,7 +53,7 @@ describe('Scroll', () => {
*/
test('No onScrollBottom callback for disconnected elements', () => {
- const { document } = new Window();
+ const { document: _ } = new Window();
const div = window.document.createElement('div');
assert.strictEqual(div.scrollTop, 0);
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 125a393417..e80d981bd2 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"allowJs": true,
"noEmitOnError": false,
- "noImplicitAny": false,
+ "noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedParameters": false,
"noUnusedLocals": false,
@@ -23,7 +23,6 @@
"useDefineForClassFields": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true,
- "baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@@/*": ["../frontend-shared/*"]
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index c9c20b23ea..260d1215df 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -167,7 +167,7 @@ export function getConfig(): UserConfig {
_ENV_: JSON.stringify(process.env.NODE_ENV),
_DEV_: process.env.NODE_ENV !== 'production',
_PERF_PREFIX_: JSON.stringify('Misskey:'),
- __VUE_OPTIONS_API__: true,
+ __VUE_OPTIONS_API__: false,
__VUE_PROD_DEVTOOLS__: false,
},
diff --git a/packages/i18n/build.ts b/packages/i18n/build.ts
index 21bf2996b4..3caddd0054 100644
--- a/packages/i18n/build.ts
+++ b/packages/i18n/build.ts
@@ -100,7 +100,7 @@ async function buildSrc(): Promise<void> {
function buildDts(): Promise<unknown> {
return execa(
- 'tsc',
+ 'tsgo',
[
'--project', 'tsconfig.json',
'--rootDir', 'src',
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index ac6c386995..20abb21cbc 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -20,7 +20,7 @@
"build": "tsx ./build.ts",
"watch": "nodemon -w package.json -e json --exec \"tsx ./build.ts --watch\"",
"tsd": "tsd",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint",
"lint:fix": "pnpm eslint --fix"
},
@@ -29,15 +29,14 @@
],
"devDependencies": {
"@types/js-yaml": "4.0.9",
- "@types/node": "24.10.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
+ "@types/node": "24.10.13",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
"chokidar": "5.0.0",
- "esbuild": "0.27.1",
+ "esbuild": "0.27.3",
"execa": "9.6.1",
- "nodemon": "3.1.11",
- "tsx": "4.21.0",
- "typescript": "5.9.3"
+ "nodemon": "3.1.14",
+ "tsx": "4.21.0"
},
"dependencies": {
"js-yaml": "4.1.1"
diff --git a/packages/i18n/src/autogen/locale.ts b/packages/i18n/src/autogen/locale.ts
index 96a728da63..f814f3ecc2 100644
--- a/packages/i18n/src/autogen/locale.ts
+++ b/packages/i18n/src/autogen/locale.ts
@@ -2185,6 +2185,10 @@ export interface Locale extends ILocale {
*/
"limitTo": ParameterizedString<"x">;
/**
+ * 画面幅が広いときはメディアリストを横並びで表示する
+ */
+ "showMediaListByGridInWideArea": string;
+ /**
* フォロー申請はありません
*/
"noFollowRequests": string;
@@ -5639,6 +5643,10 @@ export interface Locale extends ILocale {
* ゼロ埋め
*/
"zeroPadding": string;
+ /**
+ * 設定項目はありません
+ */
+ "nothingToConfigure": string;
"_imageEditing": {
"_vars": {
/**
@@ -9881,7 +9889,7 @@ export interface Locale extends ILocale {
*/
"clicker": string;
/**
- * 今日誕生日のユーザー
+ * もうすぐ誕生日のユーザー
*/
"birthdayFollowings": string;
/**
@@ -9889,6 +9897,144 @@ export interface Locale extends ILocale {
*/
"chat": string;
};
+ "_widgetOptions": {
+ /**
+ * ヘッダーを表示
+ */
+ "showHeader": string;
+ /**
+ * 背景を透明にする
+ */
+ "transparent": string;
+ /**
+ * 高さ
+ */
+ "height": string;
+ "_button": {
+ /**
+ * 色付き
+ */
+ "colored": string;
+ };
+ "_clock": {
+ /**
+ * サイズ
+ */
+ "size": string;
+ /**
+ * 針の太さ
+ */
+ "thickness": string;
+ /**
+ * 細い
+ */
+ "thicknessThin": string;
+ /**
+ * 普通
+ */
+ "thicknessMedium": string;
+ /**
+ * 太い
+ */
+ "thicknessThick": string;
+ /**
+ * 文字盤の目盛り
+ */
+ "graduations": string;
+ /**
+ * ドット
+ */
+ "graduationDots": string;
+ /**
+ * アラビア数字
+ */
+ "graduationArabic": string;
+ /**
+ * 目盛りをフェード
+ */
+ "fadeGraduations": string;
+ /**
+ * 秒針のアニメーション
+ */
+ "sAnimation": string;
+ /**
+ * リアル
+ */
+ "sAnimationElastic": string;
+ /**
+ * 滑らか
+ */
+ "sAnimationEaseOut": string;
+ /**
+ * 24時間表示
+ */
+ "twentyFour": string;
+ /**
+ * 時刻
+ */
+ "labelTime": string;
+ /**
+ * タイムゾーン
+ */
+ "labelTz": string;
+ /**
+ * 時刻とタイムゾーン
+ */
+ "labelTimeAndTz": string;
+ /**
+ * タイムゾーン
+ */
+ "timezone": string;
+ /**
+ * ミリ秒を表示
+ */
+ "showMs": string;
+ /**
+ * ラベルを表示
+ */
+ "showLabel": string;
+ };
+ "_jobQueue": {
+ /**
+ * 音を鳴らす
+ */
+ "sound": string;
+ };
+ "_rss": {
+ /**
+ * RSSフィードのURL
+ */
+ "url": string;
+ /**
+ * 更新間隔(秒)
+ */
+ "refreshIntervalSec": string;
+ /**
+ * 最大表示件数
+ */
+ "maxEntries": string;
+ };
+ "_rssTicker": {
+ /**
+ * 表示順をシャッフル
+ */
+ "shuffle": string;
+ /**
+ * ティッカーのスクロール速度(秒)
+ */
+ "duration": string;
+ /**
+ * 逆方向にスクロール
+ */
+ "reverse": string;
+ };
+ "_birthdayFollowings": {
+ /**
+ * 期間
+ */
+ "period": string;
+ };
+ };
"_cw": {
/**
* 隠す
@@ -12764,10 +12910,6 @@ export interface Locale extends ILocale {
*/
"discardChangesConfirm": string;
/**
- * 設定項目はありません
- */
- "nothingToConfigure": string;
- /**
* 画像の読み込みに失敗しました
*/
"failedToLoadImage": string;
diff --git a/packages/icons-subsetter/package.json b/packages/icons-subsetter/package.json
index 8d52555288..cbad48c26e 100644
--- a/packages/icons-subsetter/package.json
+++ b/packages/icons-subsetter/package.json
@@ -7,20 +7,19 @@
"scripts": {
"build": "tsx src/generator.ts",
"eslint": "eslint src/**/*.ts",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "24.10.2",
+ "@types/node": "24.10.13",
"@types/wawoff2": "1.0.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0"
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0"
},
"dependencies": {
"@tabler/icons-webfont": "3.35.0",
- "harfbuzzjs": "0.4.13",
+ "harfbuzzjs": "0.8.0",
"tsx": "4.21.0",
- "typescript": "5.9.3",
"wawoff2": "2.0.1"
},
"files": [
diff --git a/packages/misskey-bubble-game/build.js b/packages/misskey-bubble-game/build.js
index 1a6f87a8e8..2e606f276f 100644
--- a/packages/misskey-bubble-game/build.js
+++ b/packages/misskey-bubble-game/build.js
@@ -58,7 +58,7 @@ async function buildSrc() {
function buildDts() {
return execa(
- 'tsc',
+ 'tsgo',
[
'--project', 'tsconfig.json',
'--outDir', 'built',
diff --git a/packages/misskey-bubble-game/package.json b/packages/misskey-bubble-game/package.json
index 3844740bf2..f707beaee8 100644
--- a/packages/misskey-bubble-game/package.json
+++ b/packages/misskey-bubble-game/package.json
@@ -20,25 +20,24 @@
"build": "node ./build.js",
"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
"@types/matter-js": "0.20.2",
- "@types/node": "24.10.2",
+ "@types/node": "24.10.13",
"@types/seedrandom": "3.0.8",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
- "esbuild": "0.27.1",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "esbuild": "0.27.3",
"execa": "9.6.1",
- "nodemon": "3.1.11",
- "typescript": "5.9.3"
+ "nodemon": "3.1.14"
},
"files": [
"built"
],
"dependencies": {
- "eventemitter3": "5.0.1",
+ "eventemitter3": "5.0.4",
"matter-js": "0.20.0",
"seedrandom": "3.0.5"
}
diff --git a/packages/misskey-js/LICENSE b/packages/misskey-js/LICENSE
index 16352625db..d666d5ef7f 100644
--- a/packages/misskey-js/LICENSE
+++ b/packages/misskey-js/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2021-2025 syuilo and other contributors
+Copyright (c) 2021-2026 syuilo and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/packages/misskey-js/build.js b/packages/misskey-js/build.js
index 68535556d3..befc605a08 100644
--- a/packages/misskey-js/build.js
+++ b/packages/misskey-js/build.js
@@ -59,7 +59,7 @@ async function buildSrc() {
function buildDts() {
return execa(
- 'tsc',
+ 'tsgo',
[
'--project', 'tsconfig.json',
'--outDir', 'built',
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index fe19c00a80..760d5c319b 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -854,7 +854,14 @@ export type Channels = {
key: K;
value: ReversiGameDetailed[K];
}) => void;
- log: (payload: Record<string, unknown>) => void;
+ log: (payload: {
+ time: number;
+ player: boolean;
+ operation: 'put';
+ pos: number;
+ } & {
+ id: string | null;
+ }) => void;
};
receives: {
putStone: {
@@ -2121,6 +2128,8 @@ declare namespace entities {
UsersFollowingResponse,
UsersGalleryPostsRequest,
UsersGalleryPostsResponse,
+ UsersGetFollowingUsersByBirthdayRequest,
+ UsersGetFollowingUsersByBirthdayResponse,
UsersGetFrequentlyRepliedUsersRequest,
UsersGetFrequentlyRepliedUsersResponse,
UsersListsCreateRequest,
@@ -2221,6 +2230,7 @@ declare namespace entities {
MetaLite,
MetaDetailedOnly,
MetaDetailed,
+ MetaClientOptions,
UserWebhook,
SystemWebhook,
AbuseReportNotificationRecipient,
@@ -2812,6 +2822,9 @@ type MeDetailed = components['schemas']['MeDetailed'];
type MeDetailedOnly = components['schemas']['MeDetailedOnly'];
// @public (undocumented)
+type MetaClientOptions = components['schemas']['MetaClientOptions'];
+
+// @public (undocumented)
type MetaDetailed = components['schemas']['MetaDetailed'];
// @public (undocumented)
@@ -3728,6 +3741,12 @@ type UsersGalleryPostsRequest = operations['users___gallery___posts']['requestBo
type UsersGalleryPostsResponse = operations['users___gallery___posts']['responses']['200']['content']['application/json'];
// @public (undocumented)
+type UsersGetFollowingUsersByBirthdayRequest = operations['users___get-following-users-by-birthday']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersGetFollowingUsersByBirthdayResponse = operations['users___get-following-users-by-birthday']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
type UsersGetFrequentlyRepliedUsersRequest = operations['users___get-frequently-replied-users']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -3864,7 +3883,7 @@ type VerifyEmailRequest = operations['verify-email']['requestBody']['content']['
// src/entities.ts:55:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
// src/streaming.ts:57:3 - (ae-forgotten-export) The symbol "ReconnectingWebSocket" needs to be exported by the entry point index.d.ts
// src/streaming.types.ts:226:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts
-// src/streaming.types.ts:236:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts
+// src/streaming.types.ts:241:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json
index e9721911cc..bdce22942f 100644
--- a/packages/misskey-js/generator/package.json
+++ b/packages/misskey-js/generator/package.json
@@ -7,16 +7,15 @@
"generate": "tsx src/generator.ts && eslint ./built/**/*.ts --fix"
},
"devDependencies": {
- "@readme/openapi-parser": "5.2.1",
- "@types/node": "24.10.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
+ "@readme/openapi-parser": "5.5.0",
+ "@types/node": "24.10.13",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
"openapi-types": "12.1.3",
- "openapi-typescript": "7.10.1",
+ "openapi-typescript": "7.13.0",
"ts-case-convert": "2.1.0",
"tsx": "4.21.0",
- "typescript": "5.9.3",
- "eslint": "9.39.1"
+ "eslint": "9.39.3"
},
"files": [
"built"
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 226428af13..b031e2ef61 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
- "version": "2025.12.2",
+ "version": "2026.3.0",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",
@@ -25,7 +25,7 @@
"api": "pnpm api-extractor run --local --verbose",
"api-prod": "pnpm api-extractor run --verbose",
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint",
"vitest": "vitest run --coverage",
"test": "pnpm vitest && pnpm tsd",
@@ -37,18 +37,17 @@
"directory": "packages/misskey-js"
},
"devDependencies": {
- "@microsoft/api-extractor": "7.55.2",
- "@types/node": "24.10.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
- "@vitest/coverage-v8": "4.0.15",
- "esbuild": "0.27.1",
+ "@microsoft/api-extractor": "7.57.2",
+ "@types/node": "24.10.13",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "@vitest/coverage-v8": "4.0.18",
+ "esbuild": "0.27.3",
"execa": "9.6.1",
"ncp": "2.0.0",
- "nodemon": "3.1.11",
+ "nodemon": "3.1.14",
"tsd": "0.33.0",
- "typescript": "5.9.3",
- "vitest": "4.0.15",
+ "vitest": "4.0.18",
"vitest-websocket-mock": "0.5.0"
},
"files": [
@@ -56,7 +55,7 @@
],
"dependencies": {
"@simplewebauthn/types": "12.0.0",
- "eventemitter3": "5.0.1",
+ "eventemitter3": "5.0.4",
"reconnecting-websocket": "4.4.0"
}
}
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index af3a09a16e..c9cb9147f1 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -4533,6 +4533,17 @@ declare module '../api.js' {
): Promise<SwitchCaseResponseType<E, P>>;
/**
+ * Retrieve users who have a birthday on the specified range.
+ *
+ * **Credential required**: *Yes* / **Permission**: *read:account*
+ */
+ request<E extends 'users/get-following-users-by-birthday', P extends Endpoints[E]['req']>(
+ endpoint: E,
+ params: P,
+ credential?: string | null,
+ ): Promise<SwitchCaseResponseType<E, P>>;
+
+ /**
* Get a list of other users that the specified user frequently replies to.
*
* **Credential required**: *No*
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index c3ef3de4e6..2d4e0fe35e 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -616,6 +616,8 @@ import type {
UsersFollowingResponse,
UsersGalleryPostsRequest,
UsersGalleryPostsResponse,
+ UsersGetFollowingUsersByBirthdayRequest,
+ UsersGetFollowingUsersByBirthdayResponse,
UsersGetFrequentlyRepliedUsersRequest,
UsersGetFrequentlyRepliedUsersResponse,
UsersListsCreateRequest,
@@ -1067,6 +1069,7 @@ export type Endpoints = {
'users/followers': { req: UsersFollowersRequest; res: UsersFollowersResponse };
'users/following': { req: UsersFollowingRequest; res: UsersFollowingResponse };
'users/gallery/posts': { req: UsersGalleryPostsRequest; res: UsersGalleryPostsResponse };
+ 'users/get-following-users-by-birthday': { req: UsersGetFollowingUsersByBirthdayRequest; res: UsersGetFollowingUsersByBirthdayResponse };
'users/get-frequently-replied-users': { req: UsersGetFrequentlyRepliedUsersRequest; res: UsersGetFrequentlyRepliedUsersResponse };
'users/lists/create': { req: UsersListsCreateRequest; res: UsersListsCreateResponse };
'users/lists/create-from-public': { req: UsersListsCreateFromPublicRequest; res: UsersListsCreateFromPublicResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 0d57b065dc..a49dd729e6 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -619,6 +619,8 @@ export type UsersFollowingRequest = operations['users___following']['requestBody
export type UsersFollowingResponse = operations['users___following']['responses']['200']['content']['application/json'];
export type UsersGalleryPostsRequest = operations['users___gallery___posts']['requestBody']['content']['application/json'];
export type UsersGalleryPostsResponse = operations['users___gallery___posts']['responses']['200']['content']['application/json'];
+export type UsersGetFollowingUsersByBirthdayRequest = operations['users___get-following-users-by-birthday']['requestBody']['content']['application/json'];
+export type UsersGetFollowingUsersByBirthdayResponse = operations['users___get-following-users-by-birthday']['responses']['200']['content']['application/json'];
export type UsersGetFrequentlyRepliedUsersRequest = operations['users___get-frequently-replied-users']['requestBody']['content']['application/json'];
export type UsersGetFrequentlyRepliedUsersResponse = operations['users___get-frequently-replied-users']['responses']['200']['content']['application/json'];
export type UsersListsCreateRequest = operations['users___lists___create']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 01b48442d6..f45ae39da4 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -58,6 +58,7 @@ export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
export type MetaLite = components['schemas']['MetaLite'];
export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
export type MetaDetailed = components['schemas']['MetaDetailed'];
+export type MetaClientOptions = components['schemas']['MetaClientOptions'];
export type UserWebhook = components['schemas']['UserWebhook'];
export type SystemWebhook = components['schemas']['SystemWebhook'];
export type AbuseReportNotificationRecipient = components['schemas']['AbuseReportNotificationRecipient'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 2650869590..46d04ac2dc 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3717,6 +3717,15 @@ export type paths = {
*/
post: operations['users___gallery___posts'];
};
+ '/users/get-following-users-by-birthday': {
+ /**
+ * users/get-following-users-by-birthday
+ * @description Retrieve users who have a birthday on the specified range.
+ *
+ * **Credential required**: *Yes* / **Permission**: *read:account*
+ */
+ post: operations['users___get-following-users-by-birthday'];
+ };
'/users/get-frequently-replied-users': {
/**
* users/get-frequently-replied-users
@@ -4267,6 +4276,33 @@ export type components = {
/** Format: misskey:id */
userListId: string;
};
+ login?: {
+ /** @enum {string} */
+ type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
+ } | {
+ /** @enum {string} */
+ type: 'list';
+ /** Format: misskey:id */
+ userListId: string;
+ };
+ createToken?: {
+ /** @enum {string} */
+ type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
+ } | {
+ /** @enum {string} */
+ type: 'list';
+ /** Format: misskey:id */
+ userListId: string;
+ };
+ exportCompleted?: {
+ /** @enum {string} */
+ type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'followingOrFollower' | 'never';
+ } | {
+ /** @enum {string} */
+ type: 'list';
+ /** Format: misskey:id */
+ userListId: string;
+ };
};
emailNotificationTypes: string[];
achievements: components['schemas']['Achievement'][];
@@ -5337,7 +5373,8 @@ export type components = {
/** Format: id */
timeoutUserId: string | null;
black: number | null;
- bw: string;
+ /** @enum {string} */
+ bw: 'random' | '1' | '2';
noIrregularRules: boolean;
isLlotheo: boolean;
canPutEverywhere: boolean;
@@ -5373,7 +5410,8 @@ export type components = {
/** Format: id */
timeoutUserId: string | null;
black: number | null;
- bw: string;
+ /** @enum {string} */
+ bw: 'random' | '1' | '2';
noIrregularRules: boolean;
isLlotheo: boolean;
canPutEverywhere: boolean;
@@ -5403,7 +5441,7 @@ export type components = {
feedbackUrl: string | null;
defaultDarkTheme: string | null;
defaultLightTheme: string | null;
- clientOptions: Record<string, never>;
+ clientOptions: components['schemas']['MetaClientOptions'];
disableRegistration: boolean;
emailRequiredForSignup: boolean;
enableHcaptcha: boolean;
@@ -5502,6 +5540,12 @@ export type components = {
cacheRemoteSensitiveFiles: boolean;
};
MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly'];
+ MetaClientOptions: {
+ /** @enum {string} */
+ entrancePageStyle: 'classic' | 'simple';
+ showTimelineForVisitor: boolean;
+ showActivitiesForVisitor: boolean;
+ };
UserWebhook: {
/** Format: id */
id: string;
@@ -6777,8 +6821,10 @@ export interface operations {
updatedAt: string | null;
text: string;
title: string;
- icon: string | null;
- display: string;
+ /** @enum {string} */
+ icon: 'info' | 'warning' | 'error' | 'success';
+ /** @enum {string} */
+ display: 'normal' | 'banner' | 'dialog';
isActive: boolean;
forExistingUsers: boolean;
silence: boolean;
@@ -8217,16 +8263,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- 'application/json': {
- /** Format: id */
- id: string;
- aliases: string[];
- name: string;
- category: string | null;
- /** @description The local host is represented with `null`. The field exists for compatibility with other API endpoints that return files. */
- host: string | null;
- url: string;
- }[];
+ 'application/json': components['schemas']['EmojiDetailed'][];
};
};
/** @description Client error */
@@ -8305,16 +8342,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
- 'application/json': {
- /** Format: id */
- id: string;
- aliases: string[];
- name: string;
- category: string | null;
- /** @description The local host is represented with `null`. */
- host: string | null;
- url: string;
- }[];
+ 'application/json': components['schemas']['EmojiDetailed'][];
};
};
/** @description Client error */
@@ -9446,7 +9474,7 @@ export interface operations {
deeplIsPro: boolean;
defaultDarkTheme: string | null;
defaultLightTheme: string | null;
- clientOptions: Record<string, never>;
+ clientOptions: components['schemas']['MetaClientOptions'];
description: string | null;
disableRegistration: boolean;
impressumUrl: string | null;
@@ -12702,7 +12730,12 @@ export interface operations {
description?: string | null;
defaultLightTheme?: string | null;
defaultDarkTheme?: string | null;
- clientOptions?: Record<string, never>;
+ clientOptions?: {
+ /** @enum {string} */
+ entrancePageStyle?: 'classic' | 'simple';
+ showTimelineForVisitor?: boolean;
+ showActivitiesForVisitor?: boolean;
+ };
cacheRemoteFiles?: boolean;
cacheRemoteSensitiveFiles?: boolean;
emailRequiredForSignup?: boolean;
@@ -23987,6 +24020,8 @@ export interface operations {
tag: string;
/** @default 10 */
limit?: number;
+ /** @default 0 */
+ offset?: number;
/** @enum {string} */
sort: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt';
/**
@@ -34847,6 +34882,7 @@ export interface operations {
untilDate?: number;
/** @default 10 */
limit?: number;
+ /** @description @deprecated use get-following-users-by-birthday instead. */
birthday?: string | null;
};
};
@@ -34982,6 +35018,92 @@ export interface operations {
};
};
};
+ 'users___get-following-users-by-birthday': {
+ requestBody: {
+ content: {
+ 'application/json': {
+ /** @default 10 */
+ limit?: number;
+ /** @default 0 */
+ offset?: number;
+ birthday: {
+ month: number;
+ day: number;
+ } | {
+ begin: {
+ month: number;
+ day: number;
+ };
+ end: {
+ month: number;
+ day: number;
+ };
+ };
+ };
+ };
+ };
+ responses: {
+ /** @description OK (with results) */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': {
+ /** Format: misskey:id */
+ id: string;
+ birthday: string;
+ user: components['schemas']['UserLite'];
+ }[];
+ };
+ };
+ /** @description Client error */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Authentication error */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Forbidden error */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description I'm Ai */
+ 418: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Internal server error */
+ 500: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ };
+ };
'users___get-frequently-replied-users': {
requestBody: {
content: {
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index eb6c5cd1b0..8789ee056d 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -13,7 +13,6 @@ import type {
Role,
ReversiGameDetailed,
SystemWebhook,
- UserLite,
ChatRoom,
} from './autogen/models.js';
diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts
index 93838ceed5..63e1f947a5 100644
--- a/packages/misskey-js/src/streaming.types.ts
+++ b/packages/misskey-js/src/streaming.types.ts
@@ -224,7 +224,12 @@ export type Channels = {
canceled: (payload: { userId: User['id']; }) => void;
changeReadyStates: (payload: { user1: boolean; user2: boolean; }) => void;
updateSettings: <K extends ReversiUpdateKey>(payload: { userId: User['id']; key: K; value: ReversiGameDetailed[K]; }) => void;
- log: (payload: Record<string, unknown>) => void;
+ log: (payload: {
+ time: number;
+ player: boolean;
+ operation: 'put';
+ pos: number;
+ } & { id: string | null }) => void;
};
receives: {
putStone: {
@@ -291,7 +296,10 @@ export type NoteUpdatedEvent = { id: Note['id'] } & ({
type: 'reacted';
body: {
reaction: string;
- emoji: string | null;
+ emoji?: {
+ name: string;
+ url: string;
+ } | null;
userId: User['id'];
};
} | {
diff --git a/packages/misskey-reversi/build.js b/packages/misskey-reversi/build.js
index 1a6f87a8e8..2e606f276f 100644
--- a/packages/misskey-reversi/build.js
+++ b/packages/misskey-reversi/build.js
@@ -58,7 +58,7 @@ async function buildSrc() {
function buildDts() {
return execa(
- 'tsc',
+ 'tsgo',
[
'--project', 'tsconfig.json',
'--outDir', 'built',
diff --git a/packages/misskey-reversi/package.json b/packages/misskey-reversi/package.json
index e22ccd1e02..d1913bc9dc 100644
--- a/packages/misskey-reversi/package.json
+++ b/packages/misskey-reversi/package.json
@@ -20,17 +20,16 @@
"build": "node ./build.js",
"watch": "nodemon -w package.json -e json --exec \"node ./build.js --watch\"",
"eslint": "eslint './**/*.{js,jsx,ts,tsx}'",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"lint": "pnpm typecheck && pnpm eslint"
},
"devDependencies": {
- "@types/node": "24.10.2",
- "@typescript-eslint/eslint-plugin": "8.49.0",
- "@typescript-eslint/parser": "8.49.0",
- "esbuild": "0.27.1",
+ "@types/node": "24.10.13",
+ "@typescript-eslint/eslint-plugin": "8.56.0",
+ "@typescript-eslint/parser": "8.56.0",
+ "esbuild": "0.27.3",
"execa": "9.6.1",
- "nodemon": "3.1.11",
- "typescript": "5.9.3"
+ "nodemon": "3.1.14"
},
"files": [
"built"
diff --git a/packages/shared/eslint.config.js b/packages/shared/eslint.config.js
index ae9fb3cd37..afd14001d9 100644
--- a/packages/shared/eslint.config.js
+++ b/packages/shared/eslint.config.js
@@ -40,6 +40,22 @@ export default [
// 型の情報を利用してlintする必要があるため無効化
// TODO: 有効化検討
'@typescript-eslint/no-misused-promises': 'off',
+ 'no-async-promise-executor': 'error',
+ },
+ },
+ {
+ // typescript
+ files: ['**/*.ts', '**/*.tsx'],
+ rules: {
+ '@typescript-eslint/no-unused-vars': ['warn', {
+ 'args': 'all',
+ 'argsIgnorePattern': '^_',
+ 'caughtErrors': 'all',
+ 'caughtErrorsIgnorePattern': '^_',
+ 'destructuredArrayIgnorePattern': '^_',
+ 'varsIgnorePattern': '^_',
+ 'ignoreRestSiblings': true,
+ }],
},
},
];
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 1911524b8f..28d7c30f7b 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -4,22 +4,21 @@
"scripts": {
"watch": "nodemon -w ../../package.json -e json --exec \"node build.js watch\"",
"build": "node build.js",
- "typecheck": "tsc --noEmit",
+ "typecheck": "tsgo --noEmit",
"eslint": "eslint --quiet src/**/*.ts",
"lint": "pnpm typecheck && pnpm eslint"
},
"dependencies": {
"i18n": "workspace:*",
- "esbuild": "0.27.1",
+ "esbuild": "0.27.3",
"idb-keyval": "6.2.2",
"misskey-js": "workspace:*"
},
"devDependencies": {
- "@typescript-eslint/parser": "8.49.0",
+ "@typescript-eslint/parser": "8.56.0",
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.74",
"eslint-plugin-import": "2.32.0",
- "nodemon": "3.1.11",
- "typescript": "5.9.3"
+ "nodemon": "3.1.14"
},
"type": "module"
}
diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
index 783c78f7dc..6dfea12aa8 100644
--- a/packages/sw/src/scripts/create-notification.ts
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -292,30 +292,28 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif
}
export async function createEmptyNotification(): Promise<void> {
- return new Promise<void>(async res => {
- const i18n = await (swLang.i18n ?? swLang.fetchLocale());
-
- await globalThis.registration.showNotification(
- (new URL(origin)).host,
- {
- body: `Misskey v${_VERSION_}`,
- silent: true,
- badge: iconUrl('null'),
- tag: 'read_notification',
- actions: [
- {
- action: 'markAllAsRead',
- title: i18n.ts.markAllAsRead,
- },
- {
- action: 'settings',
- title: i18n.ts.notificationSettings,
- },
- ],
- data: {},
- },
- );
-
+ const i18n = await (swLang.i18n ?? swLang.fetchLocale());
+ await globalThis.registration.showNotification(
+ (new URL(origin)).host,
+ {
+ body: `Misskey v${_VERSION_}`,
+ silent: true,
+ badge: iconUrl('null'),
+ tag: 'read_notification',
+ actions: [
+ {
+ action: 'markAllAsRead',
+ title: i18n.ts.markAllAsRead,
+ },
+ {
+ action: 'settings',
+ title: i18n.ts.notificationSettings,
+ },
+ ],
+ data: {},
+ },
+ );
+ return new Promise<void>(res => {
setTimeout(async () => {
try {
await closeNotificationsByTags(['user_visible_auto_notification']);
diff --git a/packages/sw/tsconfig.json b/packages/sw/tsconfig.json
index 2712475a37..9732a438ce 100644
--- a/packages/sw/tsconfig.json
+++ b/packages/sw/tsconfig.json
@@ -19,7 +19,6 @@
"experimentalDecorators": true,
"resolveJsonModule": true,
"isolatedModules": true,
- "baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@@/*": ["../frontend-shared/*"]
@@ -28,6 +27,7 @@
"./node_modules/@types",
"./src/@types"
],
+ "libReplacement": true,
"lib": [
"esnext",
"webworker"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2b58b2e106..e62857051e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -5,9 +5,9 @@ settings:
excludeLinksFromLockfile: false
overrides:
- chokidar: 5.0.0
- lodash: 4.17.21
'@aiscript-dev/aiscript-languageserver': '-'
+ chokidar: 5.0.0
+ lodash: 4.17.23
importers:
@@ -17,8 +17,8 @@ importers:
specifier: 7.1.2
version: 7.1.2(postcss@8.5.6)
esbuild:
- specifier: 0.27.1
- version: 0.27.1
+ specifier: 0.27.3
+ version: 0.27.3
execa:
specifier: 9.6.1
version: 9.6.1
@@ -32,54 +32,57 @@ importers:
specifier: 8.5.6
version: 8.5.6
tar:
- specifier: 7.5.2
- version: 7.5.2
+ specifier: 7.5.9
+ version: 7.5.9
terser:
- specifier: 5.44.1
- version: 5.44.1
- typescript:
- specifier: 5.9.3
- version: 5.9.3
+ specifier: 5.46.0
+ version: 5.46.0
devDependencies:
'@eslint/js':
- specifier: 9.39.1
- version: 9.39.1
+ specifier: 9.39.3
+ version: 9.39.3
'@misskey-dev/eslint-plugin':
- specifier: 2.2.0
- version: 2.2.0(@eslint/compat@1.4.0(eslint@9.39.1))(@eslint/js@9.39.1)(@stylistic/eslint-plugin@5.5.0(eslint@9.39.1))(@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1))(eslint@9.39.1)(globals@16.5.0)
+ specifier: 2.1.0
+ version: 2.1.0(@eslint/compat@1.4.0(eslint@9.39.3))(@stylistic/eslint-plugin@5.5.0(eslint@9.39.3))(@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3))(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3))(eslint@9.39.3)(globals@17.3.0)
'@types/js-yaml':
specifier: 4.0.9
version: 4.0.9
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
+ '@typescript/native-preview':
+ specifier: 7.0.0-dev.20260116.1
+ version: 7.0.0-dev.20260116.1
cross-env:
specifier: 10.1.0
version: 10.1.0
cypress:
- specifier: 15.7.1
- version: 15.7.1
+ specifier: 15.10.0
+ version: 15.10.0
eslint:
- specifier: 9.39.1
- version: 9.39.1
+ specifier: 9.39.3
+ version: 9.39.3
globals:
- specifier: 16.5.0
- version: 16.5.0
+ specifier: 17.3.0
+ version: 17.3.0
ncp:
specifier: 2.0.0
version: 2.0.0
pnpm:
- specifier: 10.25.0
- version: 10.25.0
+ specifier: 10.30.1
+ version: 10.30.1
start-server-and-test:
specifier: 2.1.3
version: 2.1.3
+ typescript:
+ specifier: 5.9.3
+ version: 5.9.3
optionalDependencies:
'@tensorflow/tfjs-core':
specifier: 4.22.0
@@ -88,11 +91,11 @@ importers:
packages/backend:
dependencies:
'@aws-sdk/client-s3':
- specifier: 3.948.0
- version: 3.948.0
+ specifier: 3.995.0
+ version: 3.995.0
'@aws-sdk/lib-storage':
- specifier: 3.948.0
- version: 3.948.0(@aws-sdk/client-s3@3.948.0)
+ specifier: 3.995.0
+ version: 3.995.0(@aws-sdk/client-s3@3.995.0)
'@discordapp/twemoji':
specifier: 16.0.1
version: 16.0.1
@@ -103,20 +106,20 @@ importers:
specifier: 11.2.0
version: 11.2.0
'@fastify/express':
- specifier: 4.0.2
- version: 4.0.2
+ specifier: 4.0.4
+ version: 4.0.4
'@fastify/http-proxy':
specifier: 11.4.1
- version: 11.4.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ version: 11.4.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)
'@fastify/multipart':
- specifier: 9.3.0
- version: 9.3.0
+ specifier: 9.4.0
+ version: 9.4.0
'@fastify/static':
- specifier: 8.3.0
- version: 8.3.0
+ specifier: 9.0.0
+ version: 9.0.0
'@kitajs/html':
- specifier: 4.2.11
- version: 4.2.11
+ specifier: 4.2.13
+ version: 4.2.13
'@misskey-dev/sharp-read-bmp':
specifier: 1.2.0
version: 1.2.0
@@ -124,53 +127,50 @@ importers:
specifier: 5.2.5
version: 5.2.5
'@napi-rs/canvas':
- specifier: 0.1.84
- version: 0.1.84
+ specifier: 0.1.94
+ version: 0.1.94
'@nestjs/common':
- specifier: 11.1.9
- version: 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ specifier: 11.1.14
+ version: 11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/core':
- specifier: 11.1.9
- version: 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ specifier: 11.1.14
+ version: 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/testing':
- specifier: 11.1.9
- version: 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/platform-express@11.1.9)
+ specifier: 11.1.14
+ version: 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-express@11.1.14)
'@peertube/http-signature':
specifier: 1.7.0
version: 1.7.0
'@sentry/node':
- specifier: 10.29.0
- version: 10.29.0
+ specifier: 10.39.0
+ version: 10.39.0
'@sentry/profiling-node':
- specifier: 10.29.0
- version: 10.29.0
+ specifier: 10.39.0
+ version: 10.39.0
'@simplewebauthn/server':
specifier: 13.2.2
version: 13.2.2
'@sinonjs/fake-timers':
- specifier: 15.0.0
- version: 15.0.0
+ specifier: 15.1.0
+ version: 15.1.0
'@smithy/node-http-handler':
- specifier: 4.4.5
- version: 4.4.5
+ specifier: 4.4.10
+ version: 4.4.10
'@swc/cli':
- specifier: 0.7.9
- version: 0.7.9(@swc/core@1.15.3)(chokidar@5.0.0)
+ specifier: 0.8.0
+ version: 0.8.0(@swc/core@1.15.11)(chokidar@5.0.0)
'@swc/core':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@twemoji/parser':
specifier: 16.0.0
version: 16.0.0
- '@types/redis-info':
- specifier: 3.0.3
- version: 3.0.3
accepts:
specifier: 1.3.8
version: 1.3.8
ajv:
- specifier: 8.17.1
- version: 8.17.1
+ specifier: 8.18.0
+ version: 8.18.0
archiver:
specifier: 7.0.1
version: 7.0.1
@@ -184,11 +184,11 @@ importers:
specifier: 2.0.5
version: 2.0.5
body-parser:
- specifier: 2.2.1
- version: 2.2.1
+ specifier: 2.2.2
+ version: 2.2.2
bullmq:
- specifier: 5.65.1
- version: 5.65.1
+ specifier: 5.69.4
+ version: 5.69.4
cacheable-lookup:
specifier: 7.0.0
version: 7.0.0
@@ -214,17 +214,17 @@ importers:
specifier: 0.1.21
version: 0.1.21
fastify:
- specifier: 5.6.2
- version: 5.6.2
+ specifier: 5.7.4
+ version: 5.7.4
fastify-raw-body:
specifier: 5.0.0
version: 5.0.0
feed:
- specifier: 5.1.0
- version: 5.1.0
+ specifier: 5.2.0
+ version: 5.2.0
file-type:
- specifier: 21.1.1
- version: 21.1.1
+ specifier: 21.3.0
+ version: 21.3.0
fluent-ffmpeg:
specifier: 2.1.3
version: 2.1.3
@@ -232,8 +232,8 @@ importers:
specifier: 4.0.5
version: 4.0.5
got:
- specifier: 14.6.5
- version: 14.6.5
+ specifier: 14.6.6
+ version: 14.6.6
hpagent:
specifier: 1.2.0
version: 1.2.0
@@ -244,8 +244,8 @@ importers:
specifier: workspace:*
version: link:../i18n
ioredis:
- specifier: 5.8.2
- version: 5.8.2
+ specifier: 5.9.3
+ version: 5.9.3
ip-cidr:
specifier: 4.0.2
version: 4.0.2
@@ -262,11 +262,11 @@ importers:
specifier: 9.0.0
version: 9.0.0
juice:
- specifier: 11.0.3
- version: 11.0.3
+ specifier: 11.1.1
+ version: 11.1.1
meilisearch:
- specifier: 0.54.0
- version: 0.54.0
+ specifier: 0.55.0
+ version: 0.55.0
mfm-js:
specifier: 0.25.0
version: 0.25.0
@@ -292,11 +292,11 @@ importers:
specifier: 3.3.2
version: 3.3.2
node-html-parser:
- specifier: 7.0.1
- version: 7.0.1
+ specifier: 7.0.2
+ version: 7.0.2
nodemailer:
- specifier: 7.0.11
- version: 7.0.11
+ specifier: 8.0.1
+ version: 8.0.1
nsfwjs:
specifier: 4.2.0
version: 4.2.0(@tensorflow/tfjs@4.22.0(encoding@0.1.13)(seedrandom@3.0.5))(buffer@6.0.3)
@@ -310,14 +310,14 @@ importers:
specifier: 0.0.14
version: 0.0.14
otpauth:
- specifier: 9.4.1
- version: 9.4.1
+ specifier: 9.5.0
+ version: 9.5.0
pg:
- specifier: 8.16.3
- version: 8.16.3
+ specifier: 8.18.0
+ version: 8.18.0
pkce-challenge:
- specifier: 5.0.1
- version: 5.0.1
+ specifier: 6.0.0
+ version: 6.0.0
probe-image-size:
specifier: 7.2.3
version: 7.2.3
@@ -334,11 +334,8 @@ importers:
specifier: 3.4.1
version: 3.4.1
re2:
- specifier: 1.22.3
- version: 1.22.3
- redis-info:
- specifier: 3.1.0
- version: 3.1.0
+ specifier: 1.23.3
+ version: 1.23.3
reflect-metadata:
specifier: 0.2.2
version: 0.2.2
@@ -352,14 +349,14 @@ importers:
specifier: 7.8.2
version: 7.8.2
sanitize-html:
- specifier: 2.17.0
- version: 2.17.0
+ specifier: 2.17.1
+ version: 2.17.1
secure-json-parse:
specifier: 4.1.0
version: 4.1.0
semver:
- specifier: 7.7.3
- version: 7.7.3
+ specifier: 7.7.4
+ version: 7.7.4
sharp:
specifier: 0.33.5
version: 0.33.5
@@ -373,8 +370,8 @@ importers:
specifier: 2.1.0
version: 2.1.0
systeminformation:
- specifier: 5.27.14
- version: 5.27.14
+ specifier: 5.31.1
+ version: 5.31.1
tinycolor2:
specifier: 1.6.0
version: 1.6.0
@@ -386,10 +383,7 @@ importers:
version: 1.8.16
typeorm:
specifier: 0.3.28
- version: 0.3.28(ioredis@5.8.2)(pg@8.16.3)
- typescript:
- specifier: 5.9.3
- version: 5.9.3
+ version: 0.3.28(ioredis@5.9.3)(pg@8.18.0)
ulid:
specifier: 3.0.2
version: 3.0.2
@@ -400,8 +394,8 @@ importers:
specifier: 3.6.7
version: 3.6.7
ws:
- specifier: 8.18.3
- version: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ specifier: 8.19.0
+ version: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
xev:
specifier: 3.0.2
version: 3.0.2
@@ -410,20 +404,20 @@ importers:
specifier: 29.7.0
version: 29.7.0
'@kitajs/ts-html-plugin':
- specifier: 4.1.3
- version: 4.1.3(@kitajs/html@4.2.11)(typescript@5.9.3)
+ specifier: 4.1.4
+ version: 4.1.4(@kitajs/html@4.2.13)(typescript@5.9.3)
'@nestjs/platform-express':
- specifier: 11.1.9
- version: 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)
+ specifier: 11.1.14
+ version: 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)
'@sentry/vue':
- specifier: 10.29.0
- version: 10.29.0(vue@3.5.25(typescript@5.9.3))
+ specifier: 10.39.0
+ version: 10.39.0(vue@3.5.28(typescript@5.9.3))
'@simplewebauthn/types':
specifier: 12.0.0
version: 12.0.0
'@swc/jest':
specifier: 0.2.39
- version: 0.2.39(@swc/core@1.15.3)
+ version: 0.2.39(@swc/core@1.15.11)
'@types/accepts':
specifier: 1.3.7
version: 1.3.7
@@ -458,11 +452,11 @@ importers:
specifier: 2.1.0
version: 2.1.0
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@types/nodemailer':
- specifier: 7.0.4
- version: 7.0.4
+ specifier: 7.0.11
+ version: 7.0.11
'@types/oauth2orize':
specifier: 1.11.5
version: 1.11.5
@@ -470,8 +464,8 @@ importers:
specifier: 0.1.2
version: 0.1.2
'@types/pg':
- specifier: 8.15.6
- version: 8.15.6
+ specifier: 8.16.0
+ version: 8.16.0
'@types/qrcode':
specifier: 1.5.6
version: 1.5.6
@@ -515,11 +509,11 @@ importers:
specifier: 8.18.1
version: 8.18.1
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
aws-sdk-client-mock:
specifier: 4.1.0
version: 4.1.0
@@ -529,18 +523,21 @@ importers:
cross-env:
specifier: 10.1.0
version: 10.1.0
+ esbuild-plugin-swc:
+ specifier: 1.0.1
+ version: 1.0.1
eslint-plugin-import:
specifier: 2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)
+ version: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)
execa:
specifier: 9.6.1
version: 9.6.1
fkill:
- specifier: 10.0.1
- version: 10.0.1
+ specifier: 10.0.3
+ version: 10.0.3
jest:
specifier: 29.7.0
- version: 29.7.0(@types/node@24.10.2)
+ version: 29.7.0(@types/node@24.10.13)
jest-mock:
specifier: 29.7.0
version: 29.7.0
@@ -548,57 +545,57 @@ importers:
specifier: 4.1.1
version: 4.1.1
nodemon:
- specifier: 3.1.11
- version: 3.1.11
+ specifier: 3.1.14
+ version: 3.1.14
pid-port:
- specifier: 2.0.0
- version: 2.0.0
+ specifier: 2.0.1
+ version: 2.0.1
simple-oauth2:
specifier: 5.1.0
version: 5.1.0
supertest:
- specifier: 7.1.4
- version: 7.1.4
+ specifier: 7.2.2
+ version: 7.2.2
vite:
- specifier: 7.2.7
- version: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ specifier: 7.3.1
+ version: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
optionalDependencies:
'@swc/core-android-arm64':
specifier: 1.3.11
version: 1.3.11
'@swc/core-darwin-arm64':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-darwin-x64':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-freebsd-x64':
specifier: 1.3.11
version: 1.3.11
'@swc/core-linux-arm-gnueabihf':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-linux-arm64-gnu':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-linux-arm64-musl':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-linux-x64-gnu':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-linux-x64-musl':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-win32-arm64-msvc':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-win32-ia32-msvc':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@swc/core-win32-x64-msvc':
- specifier: 1.15.3
- version: 1.15.3
+ specifier: 1.15.11
+ version: 1.15.11
'@tensorflow/tfjs':
specifier: 4.22.0
version: 4.22.0(encoding@0.1.13)(seedrandom@3.0.5)
@@ -606,8 +603,8 @@ importers:
specifier: 4.22.0
version: 4.22.0(encoding@0.1.13)(seedrandom@3.0.5)
bufferutil:
- specifier: 4.0.9
- version: 4.0.9
+ specifier: 4.1.0
+ version: 4.1.0
slacc-android-arm-eabi:
specifier: 0.0.10
version: 0.0.10
@@ -648,8 +645,8 @@ importers:
specifier: 0.0.10
version: 0.0.10
utf-8-validate:
- specifier: 6.0.5
- version: 6.0.5
+ specifier: 6.0.6
+ version: 6.0.6
packages/frontend:
dependencies:
@@ -662,27 +659,27 @@ importers:
'@github/webauthn-json':
specifier: 2.1.1
version: 2.1.1
- '@mcaptcha/vanilla-glue':
- specifier: 0.1.0-rc2
- version: 0.1.0-rc2(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ '@mcaptcha/core-glue':
+ specifier: 0.1.0-alpha-5
+ version: 0.1.0-alpha-5
'@misskey-dev/browser-image-resizer':
specifier: 2024.1.0
version: 2024.1.0
'@rollup/plugin-json':
specifier: 6.1.0
- version: 6.1.0(rollup@4.53.3)
+ version: 6.1.0(rollup@4.59.0)
'@rollup/plugin-replace':
specifier: 6.0.3
- version: 6.0.3(rollup@4.53.3)
+ version: 6.0.3(rollup@4.59.0)
'@rollup/pluginutils':
specifier: 5.3.0
- version: 5.3.0(rollup@4.53.3)
+ version: 5.3.0(rollup@4.59.0)
'@sentry/vue':
- specifier: 10.29.0
- version: 10.29.0(vue@3.5.25(typescript@5.9.3))
+ specifier: 10.39.0
+ version: 10.39.0(vue@3.5.28(typescript@5.9.3))
'@syuilo/aiscript':
- specifier: 1.2.0
- version: 1.2.0
+ specifier: 1.2.1
+ version: 1.2.1
'@syuilo/aiscript-0-19-0':
specifier: npm:@syuilo/aiscript@^0.19.0
version: '@syuilo/aiscript@0.19.0'
@@ -690,8 +687,8 @@ importers:
specifier: 16.0.0
version: 16.0.0
'@vitejs/plugin-vue':
- specifier: 6.0.2
- version: 6.0.2(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))(vue@3.5.25(typescript@5.9.3))
+ specifier: 6.0.4
+ version: 6.0.4(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))(vue@3.5.28(typescript@5.9.3))
aiscript-vscode:
specifier: github:aiscript-dev/aiscript-vscode#v0.1.16
version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/1dc7f60cda78d030dadfc518a33c472202b2ef67
@@ -699,8 +696,8 @@ importers:
specifier: 0.8.19
version: 0.8.19(@types/dlv@1.1.5)
broadcast-channel:
- specifier: 7.2.0
- version: 7.2.0
+ specifier: 7.3.0
+ version: 7.3.0
buraha:
specifier: 0.0.1
version: 0.0.1
@@ -723,8 +720,8 @@ importers:
specifier: 2.2.0
version: 2.2.0(chart.js@4.5.1)
chromatic:
- specifier: 13.3.4
- version: 13.3.4
+ specifier: 15.1.1
+ version: 15.1.1
compare-versions:
specifier: 6.1.1
version: 6.1.1
@@ -735,14 +732,14 @@ importers:
specifier: 4.1.0
version: 4.1.0
eventemitter3:
- specifier: 5.0.1
- version: 5.0.1
+ specifier: 5.0.4
+ version: 5.0.4
execa:
specifier: 9.6.1
version: 9.6.1
exifreader:
- specifier: 4.33.1
- version: 4.33.1
+ specifier: 4.36.2
+ version: 4.36.2
frontend-shared:
specifier: workspace:*
version: link:../frontend-shared
@@ -771,8 +768,8 @@ importers:
specifier: 0.20.0
version: 0.20.0
mediabunny:
- specifier: 1.25.8
- version: 1.25.8
+ specifier: 1.34.4
+ version: 1.34.4
mfm-js:
specifier: 0.25.0
version: 0.25.0
@@ -798,23 +795,23 @@ importers:
specifier: 1.4.2
version: 1.4.2
rollup:
- specifier: 4.53.3
- version: 4.53.3
+ specifier: 4.59.0
+ version: 4.59.0
sanitize-html:
- specifier: 2.17.0
- version: 2.17.0
+ specifier: 2.17.1
+ version: 2.17.1
sass:
- specifier: 1.95.1
- version: 1.95.1
+ specifier: 1.97.3
+ version: 1.97.3
shiki:
- specifier: 3.19.0
- version: 3.19.0
+ specifier: 3.22.0
+ version: 3.22.0
textarea-caret:
specifier: 3.1.0
version: 3.1.0
three:
- specifier: 0.181.2
- version: 0.181.2
+ specifier: 0.183.1
+ version: 0.183.1
throttle-debounce:
specifier: 5.0.2
version: 5.0.2
@@ -823,16 +820,13 @@ importers:
version: 1.6.0
v-code-diff:
specifier: 1.13.1
- version: 1.13.1(vue@3.5.25(typescript@5.9.3))
+ version: 1.13.1(vue@3.5.28(typescript@5.9.3))
vite:
- specifier: 7.2.7
- version: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ specifier: 7.3.1
+ version: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
vue:
- specifier: 3.5.25
- version: 3.5.25(typescript@5.9.3)
- vuedraggable:
- specifier: next
- version: 4.1.0(vue@3.5.25(typescript@5.9.3))
+ specifier: 3.5.28
+ version: 3.5.28(typescript@5.9.3)
wanakana:
specifier: 5.3.1
version: 5.3.1
@@ -841,68 +835,71 @@ importers:
specifier: 5.2.5
version: 5.2.5
'@storybook/addon-essentials':
- specifier: 8.6.14
- version: 8.6.14(@types/react@19.2.2)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(@types/react@19.2.2)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/addon-interactions':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/addon-links':
- specifier: 10.1.5
- version: 10.1.5(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 10.2.10
+ version: 10.2.10(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/addon-mdx-gfm':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/addon-storysource':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/blocks':
- specifier: 8.6.14
- version: 8.6.14(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/components':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/core-events':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/manager-api':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/preview-api':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/react':
- specifier: 10.1.5
- version: 10.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(typescript@5.9.3)
+ specifier: 10.2.10
+ version: 10.2.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(typescript@5.9.3)
'@storybook/react-vite':
- specifier: 10.1.5
- version: 10.1.5(esbuild@0.27.1)(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
+ specifier: 10.2.10
+ version: 10.2.10(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
'@storybook/test':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/theming':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/types':
- specifier: 8.6.14
- version: 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ specifier: 8.6.17
+ version: 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@storybook/vue3':
- specifier: 10.1.5
- version: 10.1.5(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vue@3.5.25(typescript@5.9.3))
+ specifier: 10.2.10
+ version: 10.2.10(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vue@3.5.28(typescript@5.9.3))
'@storybook/vue3-vite':
- specifier: 10.1.5
- version: 10.1.5(esbuild@0.27.1)(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))(vue@3.5.25(typescript@5.9.3))
+ specifier: 10.2.10
+ version: 10.2.10(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))(vue@3.5.28(typescript@5.9.3))
'@tabler/icons-webfont':
specifier: 3.35.0
version: 3.35.0
'@testing-library/vue':
specifier: 8.1.0
- version: 8.1.0(@vue/compiler-sfc@3.5.25)(vue@3.5.25(typescript@5.9.3))
+ version: 8.1.0(@vue/compiler-sfc@3.5.28)(vue@3.5.28(typescript@5.9.3))
'@types/canvas-confetti':
specifier: 1.9.0
version: 1.9.0
'@types/estree':
specifier: 1.0.8
version: 1.0.8
+ '@types/insert-text-at-cursor':
+ specifier: 0.3.2
+ version: 0.3.2
'@types/matter-js':
specifier: 0.20.2
version: 0.20.2
@@ -910,8 +907,8 @@ importers:
specifier: 4.0.10
version: 4.0.10
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@types/punycode.js':
specifier: npm:@types/punycode@2.1.4
version: '@types/punycode@2.1.4'
@@ -921,6 +918,9 @@ importers:
'@types/seedrandom':
specifier: 3.0.8
version: 3.0.8
+ '@types/textarea-caret':
+ specifier: 3.0.4
+ version: 3.0.4
'@types/throttle-debounce':
specifier: 5.0.2
version: 5.0.2
@@ -928,20 +928,20 @@ importers:
specifier: 1.4.6
version: 1.4.6
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
'@vitest/coverage-v8':
- specifier: 4.0.15
- version: 4.0.15(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
+ specifier: 4.0.18
+ version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
'@vue/compiler-core':
- specifier: 3.5.25
- version: 3.5.25
+ specifier: 3.5.28
+ version: 3.5.28
acorn:
- specifier: 8.15.0
- version: 8.15.0
+ specifier: 8.16.0
+ version: 8.16.0
astring:
specifier: 1.9.0
version: 1.9.0
@@ -949,20 +949,20 @@ importers:
specifier: 10.1.0
version: 10.1.0
cypress:
- specifier: 15.7.1
- version: 15.7.1
+ specifier: 15.10.0
+ version: 15.10.0
eslint-plugin-import:
specifier: 2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)
+ version: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)
eslint-plugin-vue:
- specifier: 10.6.2
- version: 10.6.2(@stylistic/eslint-plugin@5.5.0(eslint@9.39.1))(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1))
+ specifier: 10.8.0
+ version: 10.8.0(@stylistic/eslint-plugin@5.5.0(eslint@9.39.3))(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(vue-eslint-parser@10.4.0(eslint@9.39.3))
estree-walker:
specifier: 3.0.3
version: 3.0.3
happy-dom:
- specifier: 20.0.11
- version: 20.0.11
+ specifier: 20.7.0
+ version: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
intersection-observer:
specifier: 0.12.2
version: 0.12.2
@@ -973,26 +973,26 @@ importers:
specifier: 4.0.8
version: 4.0.8
minimatch:
- specifier: 10.1.1
- version: 10.1.1
+ specifier: 10.2.4
+ version: 10.2.4
msw:
- specifier: 2.12.4
- version: 2.12.4(@types/node@24.10.2)(typescript@5.9.3)
+ specifier: 2.12.10
+ version: 2.12.10(@types/node@24.10.13)(typescript@5.9.3)
msw-storybook-addon:
specifier: 2.0.6
- version: 2.0.6(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))
+ version: 2.0.6(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))
nodemon:
- specifier: 3.1.11
- version: 3.1.11
+ specifier: 3.1.14
+ version: 3.1.14
prettier:
- specifier: 3.7.4
- version: 3.7.4
+ specifier: 3.8.1
+ version: 3.8.1
react:
- specifier: 19.2.1
- version: 19.2.1
+ specifier: 19.2.4
+ version: 19.2.4
react-dom:
- specifier: 19.2.1
- version: 19.2.1(react@19.2.1)
+ specifier: 19.2.4
+ version: 19.2.4(react@19.2.4)
seedrandom:
specifier: 3.0.5
version: 3.0.5
@@ -1000,38 +1000,35 @@ importers:
specifier: 2.1.3
version: 2.1.3
storybook:
- specifier: 10.1.5
- version: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ specifier: 10.2.10
+ version: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
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(62d43c4a6ba7695d11b4f3f5e39354d0)
+ version: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(4a35f3b5099288de1d7ef0cf5b586d03)
tsx:
specifier: 4.21.0
version: 4.21.0
- typescript:
- specifier: 5.9.3
- version: 5.9.3
vite-plugin-glsl:
specifier: 1.5.5
- version: 1.5.5(@rollup/pluginutils@5.3.0(rollup@4.53.3))(esbuild@0.27.1)(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
+ version: 1.5.5(@rollup/pluginutils@5.3.0(rollup@4.59.0))(esbuild@0.27.3)(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
vite-plugin-turbosnap:
specifier: 1.0.3
version: 1.0.3
vitest:
- specifier: 4.0.15
- version: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ specifier: 4.0.18
+ version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
vitest-fetch-mock:
specifier: 0.4.5
- version: 0.4.5(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
+ version: 0.4.5(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
vue-component-type-helpers:
- specifier: 3.1.8
- version: 3.1.8
+ specifier: 3.2.4
+ version: 3.2.4
vue-eslint-parser:
- specifier: 10.2.0
- version: 10.2.0(eslint@9.39.1)
+ specifier: 10.4.0
+ version: 10.4.0(eslint@9.39.3)
vue-tsc:
- specifier: 3.1.8
- version: 3.1.8(typescript@5.9.3)
+ specifier: 3.2.4
+ version: 3.2.4(typescript@5.9.3)
packages/frontend-builder:
dependencies:
@@ -1045,27 +1042,24 @@ importers:
specifier: 0.30.21
version: 0.30.21
vite:
- specifier: 7.2.7
- version: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ specifier: 7.3.1
+ version: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
devDependencies:
'@types/estree':
specifier: 1.0.8
version: 1.0.8
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
rollup:
- specifier: 4.53.3
- version: 4.53.3
- typescript:
- specifier: 5.9.3
- version: 5.9.3
+ specifier: 4.59.0
+ version: 4.59.0
packages/frontend-embed:
dependencies:
@@ -1074,19 +1068,19 @@ importers:
version: 16.0.1
'@rollup/plugin-json':
specifier: 6.1.0
- version: 6.1.0(rollup@4.53.3)
+ version: 6.1.0(rollup@4.59.0)
'@rollup/plugin-replace':
specifier: 6.0.3
- version: 6.0.3(rollup@4.53.3)
+ version: 6.0.3(rollup@4.59.0)
'@rollup/pluginutils':
specifier: 5.3.0
- version: 5.3.0(rollup@4.53.3)
+ version: 5.3.0(rollup@4.59.0)
'@twemoji/parser':
specifier: 16.0.0
version: 16.0.0
'@vitejs/plugin-vue':
- specifier: 6.0.2
- version: 6.0.2(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))(vue@3.5.25(typescript@5.9.3))
+ specifier: 6.0.4
+ version: 6.0.4(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))(vue@3.5.28(typescript@5.9.3))
buraha:
specifier: 0.0.1
version: 0.0.1
@@ -1115,14 +1109,14 @@ importers:
specifier: 2.3.1
version: 2.3.1
rollup:
- specifier: 4.53.3
- version: 4.53.3
+ specifier: 4.59.0
+ version: 4.59.0
sass:
- specifier: 1.95.1
- version: 1.95.1
+ specifier: 1.97.3
+ version: 1.97.3
shiki:
- specifier: 3.19.0
- version: 3.19.0
+ specifier: 3.22.0
+ version: 3.22.0
tinycolor2:
specifier: 1.6.0
version: 1.6.0
@@ -1130,11 +1124,11 @@ importers:
specifier: 13.0.0
version: 13.0.0
vite:
- specifier: 7.2.7
- version: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ specifier: 7.3.1
+ version: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
vue:
- specifier: 3.5.25
- version: 3.5.25(typescript@5.9.3)
+ specifier: 3.5.28
+ version: 3.5.28(typescript@5.9.3)
devDependencies:
'@misskey-dev/summaly':
specifier: 5.2.5
@@ -1144,7 +1138,7 @@ importers:
version: 3.35.0
'@testing-library/vue':
specifier: 8.1.0
- version: 8.1.0(@vue/compiler-sfc@3.5.25)(vue@3.5.25(typescript@5.9.3))
+ version: 8.1.0(@vue/compiler-sfc@3.5.28)(vue@3.5.28(typescript@5.9.3))
'@types/estree':
specifier: 1.0.8
version: 1.0.8
@@ -1152,8 +1146,8 @@ importers:
specifier: 4.0.10
version: 4.0.10
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@types/punycode.js':
specifier: npm:@types/punycode@2.1.4
version: '@types/punycode@2.1.4'
@@ -1164,32 +1158,32 @@ importers:
specifier: 8.18.1
version: 8.18.1
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
'@vitest/coverage-v8':
- specifier: 4.0.15
- version: 4.0.15(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
+ specifier: 4.0.18
+ version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
'@vue/runtime-core':
- specifier: 3.5.25
- version: 3.5.25
+ specifier: 3.5.28
+ version: 3.5.28
acorn:
- specifier: 8.15.0
- version: 8.15.0
+ specifier: 8.16.0
+ version: 8.16.0
cross-env:
specifier: 10.1.0
version: 10.1.0
eslint-plugin-import:
specifier: 2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)
+ version: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)
eslint-plugin-vue:
- specifier: 10.6.2
- version: 10.6.2(@stylistic/eslint-plugin@5.5.0(eslint@9.39.1))(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1))
+ specifier: 10.8.0
+ version: 10.8.0(@stylistic/eslint-plugin@5.5.0(eslint@9.39.3))(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(vue-eslint-parser@10.4.0(eslint@9.39.3))
happy-dom:
- specifier: 20.0.11
- version: 20.0.11
+ specifier: 20.7.0
+ version: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
intersection-observer:
specifier: 0.12.2
version: 0.12.2
@@ -1197,35 +1191,32 @@ importers:
specifier: 4.0.8
version: 4.0.8
msw:
- specifier: 2.12.4
- version: 2.12.4(@types/node@24.10.2)(typescript@5.9.3)
+ specifier: 2.12.10
+ version: 2.12.10(@types/node@24.10.13)(typescript@5.9.3)
nodemon:
- specifier: 3.1.11
- version: 3.1.11
+ specifier: 3.1.14
+ version: 3.1.14
prettier:
- specifier: 3.7.4
- version: 3.7.4
+ specifier: 3.8.1
+ version: 3.8.1
start-server-and-test:
specifier: 2.1.3
version: 2.1.3
tsx:
specifier: 4.21.0
version: 4.21.0
- typescript:
- specifier: 5.9.3
- version: 5.9.3
vite-plugin-turbosnap:
specifier: 1.0.3
version: 1.0.3
vue-component-type-helpers:
- specifier: 3.1.8
- version: 3.1.8
+ specifier: 3.2.4
+ version: 3.2.4
vue-eslint-parser:
- specifier: 10.2.0
- version: 10.2.0(eslint@9.39.1)
+ specifier: 10.4.0
+ version: 10.4.0(eslint@9.39.3)
vue-tsc:
- specifier: 3.1.8
- version: 3.1.8(typescript@5.9.3)
+ specifier: 3.2.4
+ version: 3.2.4(typescript@5.9.3)
packages/frontend-shared:
dependencies:
@@ -1236,33 +1227,30 @@ importers:
specifier: workspace:*
version: link:../misskey-js
vue:
- specifier: 3.5.25
- version: 3.5.25(typescript@5.9.3)
+ specifier: 3.5.28
+ version: 3.5.28(typescript@5.9.3)
devDependencies:
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
esbuild:
- specifier: 0.27.1
- version: 0.27.1
+ specifier: 0.27.3
+ version: 0.27.3
eslint-plugin-vue:
- specifier: 10.6.2
- version: 10.6.2(@stylistic/eslint-plugin@5.5.0(eslint@9.39.1))(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1))
+ specifier: 10.8.0
+ version: 10.8.0(@stylistic/eslint-plugin@5.5.0(eslint@9.39.3))(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(vue-eslint-parser@10.4.0(eslint@9.39.3))
nodemon:
- specifier: 3.1.11
- version: 3.1.11
- typescript:
- specifier: 5.9.3
- version: 5.9.3
+ specifier: 3.1.14
+ version: 3.1.14
vue-eslint-parser:
- specifier: 10.2.0
- version: 10.2.0(eslint@9.39.1)
+ specifier: 10.4.0
+ version: 10.4.0(eslint@9.39.3)
packages/i18n:
dependencies:
@@ -1274,32 +1262,29 @@ importers:
specifier: 4.0.9
version: 4.0.9
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
chokidar:
specifier: 5.0.0
version: 5.0.0
esbuild:
- specifier: 0.27.1
- version: 0.27.1
+ specifier: 0.27.3
+ version: 0.27.3
execa:
specifier: 9.6.1
version: 9.6.1
nodemon:
- specifier: 3.1.11
- version: 3.1.11
+ specifier: 3.1.14
+ version: 3.1.14
tsx:
specifier: 4.21.0
version: 4.21.0
- typescript:
- specifier: 5.9.3
- version: 5.9.3
packages/icons-subsetter:
dependencies:
@@ -1307,36 +1292,33 @@ importers:
specifier: 3.35.0
version: 3.35.0
harfbuzzjs:
- specifier: 0.4.13
- version: 0.4.13
+ specifier: 0.8.0
+ version: 0.8.0
tsx:
specifier: 4.21.0
version: 4.21.0
- typescript:
- specifier: 5.9.3
- version: 5.9.3
wawoff2:
specifier: 2.0.1
version: 2.0.1
devDependencies:
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@types/wawoff2':
specifier: 1.0.2
version: 1.0.2
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
packages/misskey-bubble-game:
dependencies:
eventemitter3:
- specifier: 5.0.1
- version: 5.0.1
+ specifier: 5.0.4
+ version: 5.0.4
matter-js:
specifier: 0.20.0
version: 0.20.0
@@ -1348,29 +1330,26 @@ importers:
specifier: 0.20.2
version: 0.20.2
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@types/seedrandom':
specifier: 3.0.8
version: 3.0.8
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
esbuild:
- specifier: 0.27.1
- version: 0.27.1
+ specifier: 0.27.3
+ version: 0.27.3
execa:
specifier: 9.6.1
version: 9.6.1
nodemon:
- specifier: 3.1.11
- version: 3.1.11
- typescript:
- specifier: 5.9.3
- version: 5.9.3
+ specifier: 3.1.14
+ version: 3.1.14
packages/misskey-js:
dependencies:
@@ -1378,30 +1357,30 @@ importers:
specifier: 12.0.0
version: 12.0.0
eventemitter3:
- specifier: 5.0.1
- version: 5.0.1
+ specifier: 5.0.4
+ version: 5.0.4
reconnecting-websocket:
specifier: 4.4.0
version: 4.4.0
devDependencies:
'@microsoft/api-extractor':
- specifier: 7.55.2
- version: 7.55.2(@types/node@24.10.2)
+ specifier: 7.57.2
+ version: 7.57.2(@types/node@24.10.13)
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
'@vitest/coverage-v8':
- specifier: 4.0.15
- version: 4.0.15(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
+ specifier: 4.0.18
+ version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
esbuild:
- specifier: 0.27.1
- version: 0.27.1
+ specifier: 0.27.3
+ version: 0.27.3
execa:
specifier: 9.6.1
version: 9.6.1
@@ -1409,53 +1388,47 @@ importers:
specifier: 2.0.0
version: 2.0.0
nodemon:
- specifier: 3.1.11
- version: 3.1.11
+ specifier: 3.1.14
+ version: 3.1.14
tsd:
specifier: 0.33.0
version: 0.33.0
- typescript:
- specifier: 5.9.3
- version: 5.9.3
vitest:
- specifier: 4.0.15
- version: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ specifier: 4.0.18
+ version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
vitest-websocket-mock:
specifier: 0.5.0
- version: 0.5.0(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
+ version: 0.5.0(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
packages/misskey-js/generator:
devDependencies:
'@readme/openapi-parser':
- specifier: 5.2.1
- version: 5.2.1(openapi-types@12.1.3)
+ specifier: 5.5.0
+ version: 5.5.0(openapi-types@12.1.3)
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
eslint:
- specifier: 9.39.1
- version: 9.39.1
+ specifier: 9.39.3
+ version: 9.39.3
openapi-types:
specifier: 12.1.3
version: 12.1.3
openapi-typescript:
- specifier: 7.10.1
- version: 7.10.1(typescript@5.9.3)
+ specifier: 7.13.0
+ version: 7.13.0(typescript@5.9.3)
ts-case-convert:
specifier: 2.1.0
version: 2.1.0
tsx:
specifier: 4.21.0
version: 4.21.0
- typescript:
- specifier: 5.9.3
- version: 5.9.3
packages/misskey-reversi:
dependencies:
@@ -1464,32 +1437,29 @@ importers:
version: 1.2.2
devDependencies:
'@types/node':
- specifier: 24.10.2
- version: 24.10.2
+ specifier: 24.10.13
+ version: 24.10.13
'@typescript-eslint/eslint-plugin':
- specifier: 8.49.0
- version: 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
esbuild:
- specifier: 0.27.1
- version: 0.27.1
+ specifier: 0.27.3
+ version: 0.27.3
execa:
specifier: 9.6.1
version: 9.6.1
nodemon:
- specifier: 3.1.11
- version: 3.1.11
- typescript:
- specifier: 5.9.3
- version: 5.9.3
+ specifier: 3.1.14
+ version: 3.1.14
packages/sw:
dependencies:
esbuild:
- specifier: 0.27.1
- version: 0.27.1
+ specifier: 0.27.3
+ version: 0.27.3
i18n:
specifier: workspace:*
version: link:../i18n
@@ -1501,25 +1471,22 @@ importers:
version: link:../misskey-js
devDependencies:
'@typescript-eslint/parser':
- specifier: 8.49.0
- version: 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ specifier: 8.56.0
+ version: 8.56.0(eslint@9.39.3)(typescript@5.9.3)
'@typescript/lib-webworker':
specifier: npm:@types/serviceworker@0.0.74
version: '@types/serviceworker@0.0.74'
eslint-plugin-import:
specifier: 2.32.0
- version: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)
+ version: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)
nodemon:
- specifier: 3.1.11
- version: 3.1.11
- typescript:
- specifier: 5.9.3
- version: 5.9.3
+ specifier: 3.1.14
+ version: 3.1.14
packages:
- '@acemir/cssom@0.9.23':
- resolution: {integrity: sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA==}
+ '@acemir/cssom@0.9.31':
+ resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==}
'@adobe/css-tools@4.4.4':
resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
@@ -1560,11 +1527,11 @@ packages:
'@apm-js-collab/tracing-hooks@0.3.1':
resolution: {integrity: sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==}
- '@asamuzakjp/css-color@4.0.5':
- resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==}
+ '@asamuzakjp/css-color@4.1.2':
+ resolution: {integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==}
- '@asamuzakjp/dom-selector@6.7.4':
- resolution: {integrity: sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==}
+ '@asamuzakjp/dom-selector@6.7.8':
+ resolution: {integrity: sha512-stisC1nULNc9oH5lakAj8MH88ZxeGxzyWNDfbdCxvJSJIvDsHNZqYvscGTgy/ysgXWLJPt6K/4t0/GjvtKcFJQ==}
'@asamuzakjp/nwsapi@2.3.9':
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
@@ -1592,220 +1559,151 @@ packages:
'@aws-crypto/util@5.2.0':
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
- '@aws-sdk/client-s3@3.948.0':
- resolution: {integrity: sha512-uvEjds8aYA9SzhBS8RKDtsDUhNV9VhqKiHTcmvhM7gJO92q0WTn8/QeFTdNyLc6RxpiDyz+uBxS7PcdNiZzqfA==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/client-sesv2@3.938.0':
- resolution: {integrity: sha512-GW07FQuZkW5ASm0WP+CWLetcortqup9l3+p1OlvuUN3rLBIzlWRqYd5Nf2GTS72sPbaNowE3dYJXCtwu1IlLuQ==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/client-sso@3.936.0':
- resolution: {integrity: sha512-0G73S2cDqYwJVvqL08eakj79MZG2QRaB56Ul8/Ps9oQxllr7DMI1IQ/N3j3xjxgpq/U36pkoFZ8aK1n7Sbr3IQ==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/client-sso@3.948.0':
- resolution: {integrity: sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/core@3.936.0':
- resolution: {integrity: sha512-eGJ2ySUMvgtOziHhDRDLCrj473RJoL4J1vPjVM3NrKC/fF3/LoHjkut8AAnKmrW6a2uTzNKubigw8dEnpmpERw==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/core@3.947.0':
- resolution: {integrity: sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/credential-provider-env@3.936.0':
- resolution: {integrity: sha512-dKajFuaugEA5i9gCKzOaVy9uTeZcApE+7Z5wdcZ6j40523fY1a56khDAUYkCfwqa7sHci4ccmxBkAo+fW1RChA==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/credential-provider-env@3.947.0':
- resolution: {integrity: sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/credential-provider-http@3.936.0':
- resolution: {integrity: sha512-5FguODLXG1tWx/x8fBxH+GVrk7Hey2LbXV5h9SFzYCx/2h50URBm0+9hndg0Rd23+xzYe14F6SI9HA9c1sPnjg==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/credential-provider-http@3.947.0':
- resolution: {integrity: sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/credential-provider-ini@3.936.0':
- resolution: {integrity: sha512-TbUv56ERQQujoHcLMcfL0Q6bVZfYF83gu/TjHkVkdSlHPOIKaG/mhE2XZSQzXv1cud6LlgeBbfzVAxJ+HPpffg==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/client-s3@3.995.0':
+ resolution: {integrity: sha512-r+t8qrQ0m9zoovYOH+wilp/glFRB/E+blsDyWzq2C+9qmyhCAQwaxjLaHM8T/uluAmhtZQIYqOH9ILRnvWtRNw==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-ini@3.948.0':
- resolution: {integrity: sha512-Cl//Qh88e8HBL7yYkJNpF5eq76IO6rq8GsatKcfVBm7RFVxCqYEPSSBtkHdbtNwQdRQqAMXc6E/lEB/CZUDxnA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/client-sso@3.993.0':
+ resolution: {integrity: sha512-VLUN+wIeNX24fg12SCbzTUBnBENlL014yMKZvRhPkcn4wHR6LKgNrjsG3fZ03Xs0XoKaGtNFi1VVrq666sGBoQ==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-login@3.936.0':
- resolution: {integrity: sha512-8DVrdRqPyUU66gfV7VZNToh56ZuO5D6agWrkLQE/xbLJOm2RbeRgh6buz7CqV8ipRd6m+zCl9mM4F3osQLZn8Q==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/core@3.973.11':
+ resolution: {integrity: sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-login@3.948.0':
- resolution: {integrity: sha512-gcKO2b6eeTuZGp3Vvgr/9OxajMrD3W+FZ2FCyJox363ZgMoYJsyNid1vuZrEuAGkx0jvveLXfwiVS0UXyPkgtw==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/crc64-nvme@3.972.0':
+ resolution: {integrity: sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-node@3.936.0':
- resolution: {integrity: sha512-rk/2PCtxX9xDsQW8p5Yjoca3StqmQcSfkmD7nQ61AqAHL1YgpSQWqHE+HjfGGiHDYKG7PvE33Ku2GyA7lEIJAw==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-env@3.972.9':
+ resolution: {integrity: sha512-ZptrOwQynfupubvcngLkbdIq/aXvl/czdpEG8XJ8mN8Nb19BR0jaK0bR+tfuMU36Ez9q4xv7GGkHFqEEP2hUUQ==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-node@3.948.0':
- resolution: {integrity: sha512-ep5vRLnrRdcsP17Ef31sNN4g8Nqk/4JBydcUJuFRbGuyQtrZZrVT81UeH2xhz6d0BK6ejafDB9+ZpBjXuWT5/Q==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-http@3.972.11':
+ resolution: {integrity: sha512-hECWoOoH386bGr89NQc9vA/abkGf5TJrMREt+lhNcnSNmoBS04fK7vc3LrJBSQAUGGVj0Tz3f4dHB3w5veovig==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-process@3.936.0':
- resolution: {integrity: sha512-GpA4AcHb96KQK2PSPUyvChvrsEKiLhQ5NWjeef2IZ3Jc8JoosiedYqp6yhZR+S8cTysuvx56WyJIJc8y8OTrLA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-ini@3.972.9':
+ resolution: {integrity: sha512-zr1csEu9n4eDiHMTYJabX1mDGuGLgjgUnNckIivvk43DocJC9/f6DefFrnUPZXE+GHtbW50YuXb+JIxKykU74A==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-process@3.947.0':
- resolution: {integrity: sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-login@3.972.9':
+ resolution: {integrity: sha512-m4RIpVgZChv0vWS/HKChg1xLgZPpx8Z+ly9Fv7FwA8SOfuC6I3htcSaBz2Ch4bneRIiBUhwP4ziUo0UZgtJStQ==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-sso@3.936.0':
- resolution: {integrity: sha512-wHlEAJJvtnSyxTfNhN98JcU4taA1ED2JvuI2eePgawqBwS/Tzi0mhED1lvNIaWOkjfLd+nHALwszGrtJwEq4yQ==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-node@3.972.10':
+ resolution: {integrity: sha512-70nCESlvnzjo4LjJ8By8MYIiBogkYPSXl3WmMZfH9RZcB/Nt9qVWbFpYj6Fk1vLa4Vk8qagFVeXgxdieMxG1QA==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-sso@3.948.0':
- resolution: {integrity: sha512-gqLhX1L+zb/ZDnnYbILQqJ46j735StfWV5PbDjxRzBKS7GzsiYoaf6MyHseEopmWrez5zl5l6aWzig7UpzSeQQ==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-process@3.972.9':
+ resolution: {integrity: sha512-gOWl0Fe2gETj5Bk151+LYKpeGi2lBDLNu+NMNpHRlIrKHdBmVun8/AalwMK8ci4uRfG5a3/+zvZBMpuen1SZ0A==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-web-identity@3.936.0':
- resolution: {integrity: sha512-v3qHAuoODkoRXsAF4RG+ZVO6q2P9yYBT4GMpMEfU9wXVNn7AIfwZgTwzSUfnjNiGva5BKleWVpRpJ9DeuLFbUg==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-sso@3.972.9':
+ resolution: {integrity: sha512-ey7S686foGTArvFhi3ifQXmgptKYvLSGE2250BAQceMSXZddz7sUSNERGJT2S7u5KIe/kgugxrt01hntXVln6w==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-web-identity@3.948.0':
- resolution: {integrity: sha512-MvYQlXVoJyfF3/SmnNzOVEtANRAiJIObEUYYyjTqKZTmcRIVVky0tPuG26XnB8LmTYgtESwJIZJj/Eyyc9WURQ==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-web-identity@3.972.9':
+ resolution: {integrity: sha512-8LnfS76nHXoEc9aRRiMMpxZxJeDG0yusdyo3NvPhCgESmBUgpMa4luhGbClW5NoX/qRcGxxM6Z/esqANSNMTow==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/lib-storage@3.948.0':
- resolution: {integrity: sha512-dY7wISfWgEqSHGps0DkQiDjHhCqR7bc0mMrBHZ810/j12uzhTakAcb9FlF7mFWkX6zEvz2kjxF4r91lBwNqt5w==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/lib-storage@3.995.0':
+ resolution: {integrity: sha512-gzWRrmTVfrVfgZ8GS4VwOzHKKODcLwbgyzRW7GpVmvazBDIbvKv2HIpLwZNxxa2sjkBFDjS2NAN1bvTrCXFT6Q==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
- '@aws-sdk/client-s3': ^3.948.0
-
- '@aws-sdk/middleware-bucket-endpoint@3.936.0':
- resolution: {integrity: sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/middleware-expect-continue@3.936.0':
- resolution: {integrity: sha512-Eb4ELAC23bEQLJmUMYnPWcjD3FZIsmz2svDiXEcxRkQU9r7NRID7pM7C5NPH94wOfiCk0b2Y8rVyFXW0lGQwbA==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/middleware-flexible-checksums@3.947.0':
- resolution: {integrity: sha512-kXXxS2raNESNO+zR0L4YInVjhcGGNI2Mx0AE1ThRhDkAt2se3a+rGf9equ9YvOqA1m8Jl/GSI8cXYvSxXmS9Ag==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/middleware-host-header@3.936.0':
- resolution: {integrity: sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==}
- engines: {node: '>=18.0.0'}
-
- '@aws-sdk/middleware-location-constraint@3.936.0':
- resolution: {integrity: sha512-SCMPenDtQMd9o5da9JzkHz838w3327iqXk3cbNnXWqnNRx6unyW8FL0DZ84gIY12kAyVHz5WEqlWuekc15ehfw==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/client-s3': ^3.995.0
- '@aws-sdk/middleware-logger@3.936.0':
- resolution: {integrity: sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-bucket-endpoint@3.972.3':
+ resolution: {integrity: sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-recursion-detection@3.936.0':
- resolution: {integrity: sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-expect-continue@3.972.3':
+ resolution: {integrity: sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-recursion-detection@3.948.0':
- resolution: {integrity: sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-flexible-checksums@3.972.9':
+ resolution: {integrity: sha512-E663+r/UQpvF3aJkD40p5ZANVQFsUcbE39jifMtN7wc0t1M0+2gJJp3i75R49aY9OiSX5lfVyPUNjN/BNRCCZA==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-sdk-s3@3.936.0':
- resolution: {integrity: sha512-UQs/pVq4cOygsnKON0pOdSKIWkfgY0dzq4h+fR+xHi/Ng3XzxPJhWeAE6tDsKrcyQc1X8UdSbS70XkfGYr5hng==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-host-header@3.972.3':
+ resolution: {integrity: sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-sdk-s3@3.947.0':
- resolution: {integrity: sha512-DS2tm5YBKhPW2PthrRBDr6eufChbwXe0NjtTZcYDfUCXf0OR+W6cIqyKguwHMJ+IyYdey30AfVw9/Lb5KB8U8A==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-location-constraint@3.972.3':
+ resolution: {integrity: sha512-nIg64CVrsXp67vbK0U1/Is8rik3huS3QkRHn2DRDx4NldrEFMgdkZGI/+cZMKD9k4YOS110Dfu21KZLHrFA/1g==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-ssec@3.936.0':
- resolution: {integrity: sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-logger@3.972.3':
+ resolution: {integrity: sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-user-agent@3.936.0':
- resolution: {integrity: sha512-YB40IPa7K3iaYX0lSnV9easDOLPLh+fJyUDF3BH8doX4i1AOSsYn86L4lVldmOaSX+DwiaqKHpvk4wPBdcIPWw==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-recursion-detection@3.972.3':
+ resolution: {integrity: sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-user-agent@3.947.0':
- resolution: {integrity: sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-sdk-s3@3.972.11':
+ resolution: {integrity: sha512-Qr0T7ZQTRMOuR6ahxEoJR1thPVovfWrKB2a6KBGR+a8/ELrFodrgHwhq50n+5VMaGuLtGhHiISU3XGsZmtmVXQ==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/nested-clients@3.936.0':
- resolution: {integrity: sha512-eyj2tz1XmDSLSZQ5xnB7cLTVKkSJnYAEoNDSUNhzWPxrBDYeJzIbatecOKceKCU8NBf8gWWZCK/CSY0mDxMO0A==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-ssec@3.972.3':
+ resolution: {integrity: sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/nested-clients@3.948.0':
- resolution: {integrity: sha512-zcbJfBsB6h254o3NuoEkf0+UY1GpE9ioiQdENWv7odo69s8iaGBEQ4BDpsIMqcuiiUXw1uKIVNxCB1gUGYz8lw==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-user-agent@3.972.11':
+ resolution: {integrity: sha512-R8CvPsPHXwzIHCAza+bllY6PrctEk4lYq/SkHJz9NLoBHCcKQrbOcsfXxO6xmipSbUNIbNIUhH0lBsJGgsRdiw==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/region-config-resolver@3.936.0':
- resolution: {integrity: sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/nested-clients@3.993.0':
+ resolution: {integrity: sha512-iOq86f2H67924kQUIPOAvlmMaOAvOLoDOIb66I2YqSUpMYB6ufiuJW3RlREgskxv86S5qKzMnfy/X6CqMjK6XQ==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/signature-v4-multi-region@3.936.0':
- resolution: {integrity: sha512-8qS0GFUqkmwO7JZ0P8tdluBmt1UTfYUah8qJXGzNh9n1Pcb0AIeT117cCSiCUtwk+gDbJvd4hhRIhJCNr5wgjg==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/region-config-resolver@3.972.3':
+ resolution: {integrity: sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/signature-v4-multi-region@3.947.0':
- resolution: {integrity: sha512-UaYmzoxf9q3mabIA2hc4T6x5YSFUG2BpNjAZ207EA1bnQMiK+d6vZvb83t7dIWL/U1de1sGV19c1C81Jf14rrA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/signature-v4-multi-region@3.995.0':
+ resolution: {integrity: sha512-9Qx5JcAucnxnomREPb2D6L8K8GLG0rknt3+VK/BU3qTUynAcV4W21DQ04Z2RKDw+DYpW88lsZpXbVetWST2WUg==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/token-providers@3.936.0':
- resolution: {integrity: sha512-vvw8+VXk0I+IsoxZw0mX9TMJawUJvEsg3EF7zcCSetwhNPAU8Xmlhv7E/sN/FgSmm7b7DsqKoW6rVtQiCs1PWQ==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/token-providers@3.993.0':
+ resolution: {integrity: sha512-+35g4c+8r7sB9Sjp1KPdM8qxGn6B/shBjJtEUN4e+Edw9UEQlZKIzioOGu3UAbyE0a/s450LdLZr4wbJChtmww==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/token-providers@3.948.0':
- resolution: {integrity: sha512-V487/kM4Teq5dcr1t5K6eoUKuqlGr9FRWL3MIMukMERJXHZvio6kox60FZ/YtciRHRI75u14YUqm2Dzddcu3+A==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/types@3.973.1':
+ resolution: {integrity: sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/types@3.936.0':
- resolution: {integrity: sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/util-arn-parser@3.972.2':
+ resolution: {integrity: sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/util-arn-parser@3.893.0':
- resolution: {integrity: sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/util-endpoints@3.993.0':
+ resolution: {integrity: sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw==}
+ engines: {node: '>=20.0.0'}
- '@aws-sdk/util-endpoints@3.936.0':
- resolution: {integrity: sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/util-endpoints@3.995.0':
+ resolution: {integrity: sha512-aym/pjB8SLbo9w2nmkrDdAAVKVlf7CM71B9mKhjDbJTzwpSFBPHqJIMdDyj0mLumKC0aIVDr1H6U+59m9GvMFw==}
+ engines: {node: '>=20.0.0'}
'@aws-sdk/util-locate-window@3.893.0':
resolution: {integrity: sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==}
engines: {node: '>=18.0.0'}
- '@aws-sdk/util-user-agent-browser@3.936.0':
- resolution: {integrity: sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==}
-
- '@aws-sdk/util-user-agent-node@3.936.0':
- resolution: {integrity: sha512-XOEc7PF9Op00pWV2AYCGDSu5iHgYjIO53Py2VUQTIvP7SRCaCsXmA33mjBvC2Ms6FhSyWNa4aK4naUGIz0hQcw==}
- engines: {node: '>=18.0.0'}
- peerDependencies:
- aws-crt: '>=1.0.0'
- peerDependenciesMeta:
- aws-crt:
- optional: true
+ '@aws-sdk/util-user-agent-browser@3.972.3':
+ resolution: {integrity: sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==}
- '@aws-sdk/util-user-agent-node@3.947.0':
- resolution: {integrity: sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/util-user-agent-node@3.972.10':
+ resolution: {integrity: sha512-LVXzICPlsheET+sE6tkcS47Q5HkSTrANIlqL1iFxGAY/wRQ236DX/PCAK56qMh9QJoXAfXfoRW0B0Og4R+X7Nw==}
+ engines: {node: '>=20.0.0'}
peerDependencies:
aws-crt: '>=1.0.0'
peerDependenciesMeta:
aws-crt:
optional: true
- '@aws-sdk/xml-builder@3.930.0':
- resolution: {integrity: sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==}
- engines: {node: '>=18.0.0'}
+ '@aws-sdk/xml-builder@3.972.5':
+ resolution: {integrity: sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA==}
+ engines: {node: '>=20.0.0'}
'@aws/lambda-invoke-store@0.2.2':
resolution: {integrity: sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==}
@@ -1865,8 +1763,8 @@ packages:
resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
engines: {node: '>=6.9.0'}
- '@babel/parser@7.28.5':
- resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
+ '@babel/parser@7.29.0':
+ resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -1961,8 +1859,8 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/runtime@7.28.4':
- resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
+ '@babel/runtime@7.28.6':
+ resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==}
engines: {node: '>=6.9.0'}
'@babel/template@7.27.2':
@@ -1973,8 +1871,8 @@ packages:
resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.28.5':
- resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
'@bcoe/v8-coverage@0.2.3':
@@ -2026,40 +1924,39 @@ packages:
'@cropper/utils@2.1.0':
resolution: {integrity: sha512-wLtpZ4/UWgo+fGmG8NBWge8x5ehjfDe9ovleDfLy8kpwFaw43XXOEXQtRL1UNr0u4JZxaeO8FcXcolRWUUrlRQ==}
- '@csstools/color-helpers@5.1.0':
- resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
- engines: {node: '>=18'}
+ '@csstools/color-helpers@6.0.1':
+ resolution: {integrity: sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==}
+ engines: {node: '>=20.19.0'}
- '@csstools/css-calc@2.1.4':
- resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
- engines: {node: '>=18'}
+ '@csstools/css-calc@3.0.0':
+ resolution: {integrity: sha512-q4d82GTl8BIlh/dTnVsWmxnbWJeb3kiU8eUH71UxlxnS+WIaALmtzTL8gR15PkYOexMQYVk0CO4qIG93C1IvPA==}
+ engines: {node: '>=20.19.0'}
peerDependencies:
- '@csstools/css-parser-algorithms': ^3.0.5
- '@csstools/css-tokenizer': ^3.0.4
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
- '@csstools/css-color-parser@3.1.0':
- resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==}
- engines: {node: '>=18'}
+ '@csstools/css-color-parser@4.0.1':
+ resolution: {integrity: sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==}
+ engines: {node: '>=20.19.0'}
peerDependencies:
- '@csstools/css-parser-algorithms': ^3.0.5
- '@csstools/css-tokenizer': ^3.0.4
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
- '@csstools/css-parser-algorithms@3.0.5':
- resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
- engines: {node: '>=18'}
+ '@csstools/css-parser-algorithms@4.0.0':
+ resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ engines: {node: '>=20.19.0'}
peerDependencies:
- '@csstools/css-tokenizer': ^3.0.4
+ '@csstools/css-tokenizer': ^4.0.0
- '@csstools/css-syntax-patches-for-csstree@1.0.16':
- resolution: {integrity: sha512-2SpS4/UaWQaGpBINyG5ZuCHnUDeVByOhvbkARwfmnfxDvTaj80yOI1cD8Tw93ICV5Fx4fnyDKWQZI1CDtcWyUg==}
- engines: {node: '>=18'}
+ '@csstools/css-syntax-patches-for-csstree@1.0.26':
+ resolution: {integrity: sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==}
- '@csstools/css-tokenizer@3.0.4':
- resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
- engines: {node: '>=18'}
+ '@csstools/css-tokenizer@4.0.0':
+ resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+ engines: {node: '>=20.19.0'}
- '@cypress/request@3.0.9':
- resolution: {integrity: sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==}
+ '@cypress/request@3.0.10':
+ resolution: {integrity: sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ==}
engines: {node: '>= 6'}
'@cypress/xvfb@1.2.4':
@@ -2078,320 +1975,164 @@ packages:
'@epic-web/invariant@1.0.0':
resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==}
- '@esbuild/aix-ppc64@0.25.11':
- resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/aix-ppc64@0.27.1':
- resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==}
+ '@esbuild/aix-ppc64@0.27.3':
+ resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
- '@esbuild/android-arm64@0.25.11':
- resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==}
+ '@esbuild/android-arm64@0.27.3':
+ resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
- '@esbuild/android-arm64@0.27.1':
- resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm@0.25.11':
- resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==}
+ '@esbuild/android-arm@0.27.3':
+ resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
- '@esbuild/android-arm@0.27.1':
- resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-x64@0.25.11':
- resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==}
+ '@esbuild/android-x64@0.27.3':
+ resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
- '@esbuild/android-x64@0.27.1':
- resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/darwin-arm64@0.25.11':
- resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-arm64@0.27.1':
- resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==}
+ '@esbuild/darwin-arm64@0.27.3':
+ resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-x64@0.25.11':
- resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==}
+ '@esbuild/darwin-x64@0.27.3':
+ resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
- '@esbuild/darwin-x64@0.27.1':
- resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/freebsd-arm64@0.25.11':
- resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==}
+ '@esbuild/freebsd-arm64@0.27.3':
+ resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-arm64@0.27.1':
- resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.25.11':
- resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==}
+ '@esbuild/freebsd-x64@0.27.3':
+ resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.27.1':
- resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/linux-arm64@0.25.11':
- resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm64@0.27.1':
- resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==}
+ '@esbuild/linux-arm64@0.27.3':
+ resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm@0.25.11':
- resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==}
+ '@esbuild/linux-arm@0.27.3':
+ resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
- '@esbuild/linux-arm@0.27.1':
- resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-ia32@0.25.11':
- resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==}
+ '@esbuild/linux-ia32@0.27.3':
+ resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
- '@esbuild/linux-ia32@0.27.1':
- resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-loong64@0.25.11':
- resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==}
- engines: {node: '>=18'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-loong64@0.27.1':
- resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==}
+ '@esbuild/linux-loong64@0.27.3':
+ resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
- '@esbuild/linux-mips64el@0.25.11':
- resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==}
+ '@esbuild/linux-mips64el@0.27.3':
+ resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-mips64el@0.27.1':
- resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==}
- engines: {node: '>=18'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.25.11':
- resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==}
+ '@esbuild/linux-ppc64@0.27.3':
+ resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-ppc64@0.27.1':
- resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.25.11':
- resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==}
- engines: {node: '>=18'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.27.1':
- resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==}
+ '@esbuild/linux-riscv64@0.27.3':
+ resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-s390x@0.25.11':
- resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==}
- engines: {node: '>=18'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-s390x@0.27.1':
- resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==}
+ '@esbuild/linux-s390x@0.27.3':
+ resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
- '@esbuild/linux-x64@0.25.11':
- resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==}
+ '@esbuild/linux-x64@0.27.3':
+ resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
- '@esbuild/linux-x64@0.27.1':
- resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/netbsd-arm64@0.25.11':
- resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==}
+ '@esbuild/netbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
- '@esbuild/netbsd-arm64@0.27.1':
- resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [netbsd]
-
- '@esbuild/netbsd-x64@0.25.11':
- resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==}
+ '@esbuild/netbsd-x64@0.27.3':
+ resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.27.1':
- resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-arm64@0.25.11':
- resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
-
- '@esbuild/openbsd-arm64@0.27.1':
- resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==}
+ '@esbuild/openbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.25.11':
- resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==}
+ '@esbuild/openbsd-x64@0.27.3':
+ resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.27.1':
- resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/openharmony-arm64@0.25.11':
- resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openharmony]
-
- '@esbuild/openharmony-arm64@0.27.1':
- resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==}
+ '@esbuild/openharmony-arm64@0.27.3':
+ resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
- '@esbuild/sunos-x64@0.25.11':
- resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==}
+ '@esbuild/sunos-x64@0.27.3':
+ resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
- '@esbuild/sunos-x64@0.27.1':
- resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/win32-arm64@0.25.11':
- resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-arm64@0.27.1':
- resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==}
+ '@esbuild/win32-arm64@0.27.3':
+ resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
- '@esbuild/win32-ia32@0.25.11':
- resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==}
+ '@esbuild/win32-ia32@0.27.3':
+ resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
- '@esbuild/win32-ia32@0.27.1':
- resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-x64@0.25.11':
- resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [win32]
-
- '@esbuild/win32-x64@0.27.1':
- resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==}
+ '@esbuild/win32-x64@0.27.3':
+ resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
- '@eslint-community/eslint-utils@4.9.0':
- resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==}
+ '@eslint-community/eslint-utils@4.9.1':
+ resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
@@ -2429,8 +2170,8 @@ packages:
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@9.39.1':
- resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==}
+ '@eslint/js@9.39.3':
+ resolution: {integrity: sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.7':
@@ -2462,8 +2203,8 @@ packages:
'@fastify/error@4.2.0':
resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
- '@fastify/express@4.0.2':
- resolution: {integrity: sha512-lzu9MLdjlsK4Q2RiqEAwTgwQPrWQVP0kmbgAi/w9rIUqtnacjKvj3EHVTR6PIvXDs6Ut1jnTHiGbuNxHTsZwHQ==}
+ '@fastify/express@4.0.4':
+ resolution: {integrity: sha512-3D4sNT+s66T2yb3ILwv2lU5TB+1sG5gq37Zn1ucCxSk+8P4Bi8QH5Lb6eQ/3tUdg4+W9upWaU+1z0Mt9dTA7hw==}
'@fastify/fast-json-stringify-compiler@5.0.3':
resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==}
@@ -2477,8 +2218,8 @@ packages:
'@fastify/merge-json-schemas@0.2.1':
resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
- '@fastify/multipart@9.3.0':
- resolution: {integrity: sha512-NpeKipTOjjL1dA7SSlRMrOWWtrE8/0yKOmeudkdQoEaz4sVDJw5MVdZIahsWhvpc3YTN7f04f9ep/Y65RKoOWA==}
+ '@fastify/multipart@9.4.0':
+ resolution: {integrity: sha512-Z404bzZeLSXTBmp/trCBuoVFX28pM7rhv849Q5TsbTFZHuk1lc4QjQITTPK92DKVpXmNtJXeHSSc7GYvqFpxAQ==}
'@fastify/proxy-addr@5.1.0':
resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
@@ -2489,8 +2230,8 @@ packages:
'@fastify/send@4.1.0':
resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==}
- '@fastify/static@8.3.0':
- resolution: {integrity: sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==}
+ '@fastify/static@9.0.0':
+ resolution: {integrity: sha512-r64H8Woe/vfilg5RTy7lwWlE8ZZcTrc3kebYFMEUBrMqlydhQyoiExQXdYAy2REVpST/G35+stAM8WYp1WGmMA==}
'@file-type/xml@0.4.4':
resolution: {integrity: sha512-NhCyXoHlVZ8TqM476hyzwGJ24+D5IPSaZhmrPj7qXnEVb3q6jrFzA3mM9TBpknKSI9EuQeGTKRg2DXGUwvBBoQ==}
@@ -2710,21 +2451,17 @@ packages:
'@types/node':
optional: true
- '@ioredis/commands@1.4.0':
- resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==}
-
- '@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}
+ '@ioredis/commands@1.5.0':
+ resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
+ '@isaacs/cliui@9.0.0':
+ resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==}
+ engines: {node: '>=18'}
+
'@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
engines: {node: '>=18.0.0'}
@@ -2819,8 +2556,8 @@ packages:
resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
- '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1':
- resolution: {integrity: sha512-J4BaTocTOYFkMHIra1JDWrMWpNmBl4EkplIwHEsV8aeUOtdWjwSnln9U7twjMFTAEB7mptNtSKyVi1Y2W9sDJw==}
+ '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4':
+ resolution: {integrity: sha512-6PyZBYKnnVNqOSB0YFly+62R7dmov8segT27A+RVTBVd4iAE6kbW9QBJGlyR2yG4D4ohzhZSTIu7BK1UTtmFFA==}
peerDependencies:
typescript: '>= 4.3.x'
vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
@@ -2850,16 +2587,16 @@ packages:
'@keyv/serialize@1.1.1':
resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
- '@kitajs/html@4.2.11':
- resolution: {integrity: sha512-gOe+zzCZKN2fPT1FUK32mHsr21ILcAOUUux/yDqQthInW8egN8RuxVp+zP3KhwWETVACkurBiKV9RWuNw+ceiw==}
+ '@kitajs/html@4.2.13':
+ resolution: {integrity: sha512-o+8e61EsoLDPTP7rsPkYolca1YFybHuxU2Lr5fWDZCUkYT/6uBlVkvnZUdCXMQKentJL9dxwpR8/xK2Q+U4LhA==}
engines: {node: '>=12'}
- '@kitajs/ts-html-plugin@4.1.3':
- resolution: {integrity: sha512-NlYrID5yMxfRKiO1eiiSC4MWveKe0ffoCJOZm4idNOqwimmLXr0g1NmvCcquOU2XLRrgzynxZqw6rhwR5CY5Nw==}
+ '@kitajs/ts-html-plugin@4.1.4':
+ resolution: {integrity: sha512-xK5mNrhnIy73kJFKx5yVGChJyWFRGmIaE0sjlVxVYllk5dyaEYVCrIh1N8AfnseEHka8gAqzPGW95HlkhDvnJA==}
hasBin: true
peerDependencies:
'@kitajs/html': ^4.2.10
- typescript: ^5.6.2
+ typescript: ^5.9.3
'@kurkle/color@0.3.4':
resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==}
@@ -2879,11 +2616,8 @@ packages:
resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
hasBin: true
- '@mcaptcha/core-glue@0.1.0-rc1':
- resolution: {integrity: sha512-P4SgUioJDR38QpnP9sPY72NyaYex8MXD6RbzrfKra+ngamT26XjqVZEHBiZU2RT7u0SsWhuko4N1ntNOghsgpg==}
-
- '@mcaptcha/vanilla-glue@0.1.0-rc2':
- resolution: {integrity: sha512-LDjn9lrKioJ3zwaQOfql7PXsnxCAHg7b1rPw7G0OxpvVE7xLB/a40SHfIIiocce2VS9TPI4MbcKm5pcuy8fU5g==}
+ '@mcaptcha/core-glue@0.1.0-alpha-5':
+ resolution: {integrity: sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA==}
'@mdx-js/react@3.1.1':
resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==}
@@ -2891,11 +2625,11 @@ packages:
'@types/react': '>=16'
react: '>=16'
- '@microsoft/api-extractor-model@7.32.2':
- resolution: {integrity: sha512-Ussc25rAalc+4JJs9HNQE7TuO9y6jpYQX9nWD1DhqUzYPBr3Lr7O9intf+ZY8kD5HnIqeIRJX7ccCT0QyBy2Ww==}
+ '@microsoft/api-extractor-model@7.33.1':
+ resolution: {integrity: sha512-KX0LI6xzI0gcBOXXmr5mnnbdhsK2W93pqvJo8OgJgWvRRh+wMEp0Ccj38h1XKeJ29E1tuAZKSUOfHUQ1WA8fZg==}
- '@microsoft/api-extractor@7.55.2':
- resolution: {integrity: sha512-1jlWO4qmgqYoVUcyh+oXYRztZde/pAi7cSVzBz/rc+S7CoVzDasy8QE13dx6sLG4VRo8SfkkLbFORR6tBw4uGQ==}
+ '@microsoft/api-extractor@7.57.2':
+ resolution: {integrity: sha512-Dih58xLlG+M6k2qVSksk9xJx8HvmJEyK3LcHrcqXE7eK/U7pg/8cTT8j1TKijU4P4639wvCy1zhDuvtjRy+02Q==}
hasBin: true
'@microsoft/tsdoc-config@0.18.0':
@@ -2907,17 +2641,16 @@ packages:
'@misskey-dev/browser-image-resizer@2024.1.0':
resolution: {integrity: sha512-4EnO0zLW5NDtng3Gaz5MuT761uiuoOuplwX18wBqgj8w56LTU5BjLn/vbHwDIIe0j2gwqDYhMb7bDjmr1/Fomg==}
- '@misskey-dev/eslint-plugin@2.2.0':
- resolution: {integrity: sha512-NlDtaFRaO2r4KRnacTiYK2Ax/z95ZvhszQ3gfZ7VWWQzPvgoQHSEBT6q94i1/HPzWrcR+l+76c3Y3T1V/yVPeg==}
+ '@misskey-dev/eslint-plugin@2.1.0':
+ resolution: {integrity: sha512-f++Vv1r3BQyGqEE0SB5algUZwRoTMZIYfVtpcuQ2fLuYUm0cQ5BBTs/gwAHPajVB2YD8F33gzPIReTKtuJyCwQ==}
peerDependencies:
'@eslint/compat': '>= 1'
- '@eslint/js': '>= 9'
- '@stylistic/eslint-plugin': '>= 5'
+ '@stylistic/eslint-plugin': '>= 2'
'@typescript-eslint/eslint-plugin': '>= 8'
'@typescript-eslint/parser': '>= 8'
eslint: '>= 9'
eslint-plugin-import: '>= 2'
- globals: '>= 16'
+ globals: '>= 15'
'@misskey-dev/sharp-read-bmp@1.2.0':
resolution: {integrity: sha512-er4pRakXzHYfEgOFAFfQagqDouG+wLm+kwNq1I30oSdIHDa0wM3KjFpfIGQ25Fks4GcmOl1s7Zh6xoQu5dNjTw==}
@@ -2955,77 +2688,83 @@ packages:
cpu: [x64]
os: [win32]
- '@mswjs/interceptors@0.40.0':
- resolution: {integrity: sha512-EFd6cVbHsgLa6wa4RljGj6Wk75qoHxUSyc5asLyyPSyuhIcdS2Q3Phw6ImS1q+CkALthJRShiYfKANcQMuMqsQ==}
+ '@mswjs/interceptors@0.41.2':
+ resolution: {integrity: sha512-7G0Uf0yK3f2bjElBLGHIQzgRgMESczOMyYVasq1XK8P5HaXtlW4eQhz9MBL+TQILZLaruq+ClGId+hH0w4jvWw==}
engines: {node: '>=18'}
- '@napi-rs/canvas-android-arm64@0.1.84':
- resolution: {integrity: sha512-pdvuqvj3qtwVryqgpAGornJLV6Ezpk39V6wT4JCnRVGy8I3Tk1au8qOalFGrx/r0Ig87hWslysPpHBxVpBMIww==}
+ '@napi-rs/canvas-android-arm64@0.1.94':
+ resolution: {integrity: sha512-YQ6K83RWNMQOtgpk1aIML97QTE3zxPmVCHTi5eA8Nss4+B9JZi5J7LHQr7B5oD7VwSfWd++xsPdUiJ1+frqsMg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
- '@napi-rs/canvas-darwin-arm64@0.1.84':
- resolution: {integrity: sha512-A8IND3Hnv0R6abc6qCcCaOCujTLMmGxtucMTZ5vbQUrEN/scxi378MyTLtyWg+MRr6bwQJ6v/orqMS9datIcww==}
+ '@napi-rs/canvas-darwin-arm64@0.1.94':
+ resolution: {integrity: sha512-h1yl9XjqSrYZAbBUHCVLAhwd2knM8D8xt081Pv40KqNJXfeMmBrhG1SfroRymG2ak+pl42iQlWjFZ2Z8AWFdSw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@napi-rs/canvas-darwin-x64@0.1.84':
- resolution: {integrity: sha512-AUW45lJhYWwnA74LaNeqhvqYKK/2hNnBBBl03KRdqeCD4tKneUSrxUqIv8d22CBweOvrAASyKN3W87WO2zEr/A==}
+ '@napi-rs/canvas-darwin-x64@0.1.94':
+ resolution: {integrity: sha512-rkr/lrafbU0IIHebst+sQJf1HjdHvTMN0GGqWvw5OfaVS0K/sVxhNHtxi8oCfaRSvRE62aJZjWTcdc2ue/o6yw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@napi-rs/canvas-linux-arm-gnueabihf@0.1.84':
- resolution: {integrity: sha512-8zs5ZqOrdgs4FioTxSBrkl/wHZB56bJNBqaIsfPL4ZkEQCinOkrFF7xIcXiHiKp93J3wUtbIzeVrhTIaWwqk+A==}
+ '@napi-rs/canvas-linux-arm-gnueabihf@0.1.94':
+ resolution: {integrity: sha512-q95TDo32YkTKdi+Sp2yQ2Npm7pmfKEruNoJ3RUIw1KvQQ9EHKL3fii/iuU60tnzP0W+c8BKN7BFstNFcm2KXCQ==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
- '@napi-rs/canvas-linux-arm64-gnu@0.1.84':
- resolution: {integrity: sha512-i204vtowOglJUpbAFWU5mqsJgH0lVpNk/Ml4mQtB4Lndd86oF+Otr6Mr5KQnZHqYGhlSIKiU2SYnUbhO28zGQA==}
+ '@napi-rs/canvas-linux-arm64-gnu@0.1.94':
+ resolution: {integrity: sha512-Je5/gKVybWAoIGyDOcJF1zYgBTKWkPIkfOgvCzrQcl8h7DiDvRvEY70EapA+NicGe4X3DW9VsCT34KZJnerShA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@napi-rs/canvas-linux-arm64-musl@0.1.84':
- resolution: {integrity: sha512-VyZq0EEw+OILnWk7G3ZgLLPaz1ERaPP++jLjeyLMbFOF+Tr4zHzWKiKDsEV/cT7btLPZbVoR3VX+T9/QubnURQ==}
+ '@napi-rs/canvas-linux-arm64-musl@0.1.94':
+ resolution: {integrity: sha512-9YleDDauDEZNsFnfz3HyZvp1LK1ECu8N2gDUg1wtL7uWLQv8dUbfVeFtp5HOdxht1o7LsWRmQeqeIbnD4EqE2A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@napi-rs/canvas-linux-riscv64-gnu@0.1.84':
- resolution: {integrity: sha512-PSMTh8DiThvLRsbtc/a065I/ceZk17EXAATv9uNvHgkgo7wdEfTh2C3aveNkBMGByVO3tvnvD5v/YFtZL07cIg==}
+ '@napi-rs/canvas-linux-riscv64-gnu@0.1.94':
+ resolution: {integrity: sha512-lQUy9Xvz7ch8+0AXq8RkioLD41iQ6EqdKFu5uV40BxkBDijB2SCm1jna/BRhqitQRSjwAk2KlLUxTjHChyfNGg==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
- '@napi-rs/canvas-linux-x64-gnu@0.1.84':
- resolution: {integrity: sha512-N1GY3noO1oqgEo3rYQIwY44kfM11vA0lDbN0orTOHfCSUZTUyiYCY0nZ197QMahZBm1aR/vYgsWpV74MMMDuNA==}
+ '@napi-rs/canvas-linux-x64-gnu@0.1.94':
+ resolution: {integrity: sha512-0IYgyuUaugHdWxXRhDQUCMxTou8kAHHmpIBFtbmdRlciPlfK7AYQW5agvUU1PghPc5Ja3Zzp5qZfiiLu36vIWQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@napi-rs/canvas-linux-x64-musl@0.1.84':
- resolution: {integrity: sha512-vUZmua6ADqTWyHyei81aXIt9wp0yjeNwTH0KdhdeoBb6azHmFR8uKTukZMXfLCC3bnsW0t4lW7K78KNMknmtjg==}
+ '@napi-rs/canvas-linux-x64-musl@0.1.94':
+ resolution: {integrity: sha512-xuetfzzcflCIiBw2HJlOU4/+zTqhdxoe1BEcwdBsHAd/5wAQ4Pp+FGPi5g74gDvtcXQmTdEU3fLQvHc/j3wbxQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
- '@napi-rs/canvas-win32-x64-msvc@0.1.84':
- resolution: {integrity: sha512-YSs8ncurc1xzegUMNnQUTYrdrAuaXdPMOa+iYYyAxydOtg0ppV386hyYMsy00Yip1NlTgLCseRG4sHSnjQx6og==}
+ '@napi-rs/canvas-win32-arm64-msvc@0.1.94':
+ resolution: {integrity: sha512-2F3p8wci4Q4vjbENlQtSibqFWxBdpzYk1c8Jh1mqqLE92rBKElG018dBJ6C8Dp49vE350Hmy5LrfdLgFKMG8sg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@napi-rs/canvas-win32-x64-msvc@0.1.94':
+ resolution: {integrity: sha512-hjwaIKMrQLoNiu3724octSGhDVKkBwJtMeQ3qUXOi+y60h2q6Sxq3+MM2za3V88+XQzzwn0DgG0Xo6v6gzV8kQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
- '@napi-rs/canvas@0.1.84':
- resolution: {integrity: sha512-88FTNFs4uuiFKP0tUrPsEXhpe9dg7za9ILZJE08pGdUveMIDeana1zwfVkqRHJDPJFAmGY3dXmJ99dzsy57YnA==}
+ '@napi-rs/canvas@0.1.94':
+ resolution: {integrity: sha512-8jBkvqynXNdQPNZjLJxB/Rp9PdnnMSHFBLzPmMc615nlt/O6w0ergBbkEDEOr8EbjL8nRQDpEklPx4pzD7zrbg==}
engines: {node: '>= 10'}
'@napi-rs/nice-android-arm-eabi@1.1.1':
@@ -3141,8 +2880,8 @@ packages:
resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==}
engines: {node: '>= 10'}
- '@nestjs/common@11.1.9':
- resolution: {integrity: sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==}
+ '@nestjs/common@11.1.14':
+ resolution: {integrity: sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==}
peerDependencies:
class-transformer: '>=0.4.1'
class-validator: '>=0.13.2'
@@ -3154,8 +2893,8 @@ packages:
class-validator:
optional: true
- '@nestjs/core@11.1.9':
- resolution: {integrity: sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==}
+ '@nestjs/core@11.1.14':
+ resolution: {integrity: sha512-7OXPPMoDr6z+5NkoQKu4hOhfjz/YYqM3bNilPqv1WVFWrzSmuNXxvhbX69YMmNmRYascPXiwESqf5jJdjKXEww==}
engines: {node: '>= 20'}
peerDependencies:
'@nestjs/common': ^11.0.0
@@ -3172,14 +2911,14 @@ packages:
'@nestjs/websockets':
optional: true
- '@nestjs/platform-express@11.1.9':
- resolution: {integrity: sha512-GVd3+0lO0mJq2m1kl9hDDnVrX3Nd4oH3oDfklz0pZEVEVS0KVSp63ufHq2Lu9cyPdSBuelJr9iPm2QQ1yX+Kmw==}
+ '@nestjs/platform-express@11.1.14':
+ resolution: {integrity: sha512-Fs+/j+mBSBSXErOQJ/YdUn/HqJGSJ4pGfiJyYOyz04l42uNVnqEakvu1kXLbxMabR6vd6/h9d6Bi4tso9p7o4Q==}
peerDependencies:
'@nestjs/common': ^11.0.0
'@nestjs/core': ^11.0.0
- '@nestjs/testing@11.1.9':
- resolution: {integrity: sha512-UFxerBDdb0RUNxQNj25pvkvNE7/vxKhXYWBt3QuwBFnYISzRIzhVlyIqLfoV5YI3zV0m0Nn4QAn1KM0zzwfEng==}
+ '@nestjs/testing@11.1.14':
+ resolution: {integrity: sha512-cQxX0ronsTbpfHz8/LYOVWXxoTxv6VoxrnuZoQaVX7QV2PSMqxWE7/9jSQR0GcqAFUEmFP34c6EJqfkjfX/k4Q==}
peerDependencies:
'@nestjs/common': ^11.0.0
'@nestjs/core': ^11.0.0
@@ -3195,6 +2934,10 @@ packages:
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
+ '@noble/hashes@2.0.1':
+ resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==}
+ engines: {node: '>= 20.19.0'}
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -3207,13 +2950,13 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
- '@npmcli/agent@3.0.0':
- resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ '@npmcli/agent@4.0.0':
+ resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==}
+ engines: {node: ^20.17.0 || >=22.9.0}
- '@npmcli/fs@4.0.0':
- resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ '@npmcli/fs@5.0.0':
+ resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==}
+ engines: {node: ^20.17.0 || >=22.9.0}
'@nuxt/opencollective@0.4.1':
resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==}
@@ -3232,160 +2975,170 @@ packages:
'@open-draft/until@2.1.0':
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
- '@opentelemetry/api-logs@0.208.0':
- resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==}
+ '@opentelemetry/api-logs@0.207.0':
+ resolution: {integrity: sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==}
+ engines: {node: '>=8.0.0'}
+
+ '@opentelemetry/api-logs@0.211.0':
+ resolution: {integrity: sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==}
engines: {node: '>=8.0.0'}
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
- '@opentelemetry/context-async-hooks@2.2.0':
- resolution: {integrity: sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==}
+ '@opentelemetry/context-async-hooks@2.5.0':
+ resolution: {integrity: sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
- '@opentelemetry/core@2.2.0':
- resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==}
+ '@opentelemetry/core@2.5.0':
+ resolution: {integrity: sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
- '@opentelemetry/instrumentation-amqplib@0.55.0':
- resolution: {integrity: sha512-5ULoU8p+tWcQw5PDYZn8rySptGSLZHNX/7srqo2TioPnAAcvTy6sQFQXsNPrAnyRRtYGMetXVyZUy5OaX1+IfA==}
+ '@opentelemetry/instrumentation-amqplib@0.58.0':
+ resolution: {integrity: sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-connect@0.52.0':
- resolution: {integrity: sha512-GXPxfNB5szMbV3I9b7kNWSmQBoBzw7MT0ui6iU/p+NIzVx3a06Ri2cdQO7tG9EKb4aKSLmfX9Cw5cKxXqX6Ohg==}
+ '@opentelemetry/instrumentation-connect@0.54.0':
+ resolution: {integrity: sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-dataloader@0.26.0':
- resolution: {integrity: sha512-P2BgnFfTOarZ5OKPmYfbXfDFjQ4P9WkQ1Jji7yH5/WwB6Wm/knynAoA1rxbjWcDlYupFkyT0M1j6XLzDzy0aCA==}
+ '@opentelemetry/instrumentation-dataloader@0.28.0':
+ resolution: {integrity: sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-express@0.57.0':
- resolution: {integrity: sha512-HAdx/o58+8tSR5iW+ru4PHnEejyKrAy9fYFhlEI81o10nYxrGahnMAHWiSjhDC7UQSY3I4gjcPgSKQz4rm/asg==}
+ '@opentelemetry/instrumentation-express@0.59.0':
+ resolution: {integrity: sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-fs@0.28.0':
- resolution: {integrity: sha512-FFvg8fq53RRXVBRHZViP+EMxMR03tqzEGpuq55lHNbVPyFklSVfQBN50syPhK5UYYwaStx0eyCtHtbRreusc5g==}
+ '@opentelemetry/instrumentation-fs@0.30.0':
+ resolution: {integrity: sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-generic-pool@0.52.0':
- resolution: {integrity: sha512-ISkNcv5CM2IwvsMVL31Tl61/p2Zm2I2NAsYq5SSBgOsOndT0TjnptjufYVScCnD5ZLD1tpl4T3GEYULLYOdIdQ==}
+ '@opentelemetry/instrumentation-generic-pool@0.54.0':
+ resolution: {integrity: sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-graphql@0.56.0':
- resolution: {integrity: sha512-IPvNk8AFoVzTAM0Z399t34VDmGDgwT6rIqCUug8P9oAGerl2/PEIYMPOl/rerPGu+q8gSWdmbFSjgg7PDVRd3Q==}
+ '@opentelemetry/instrumentation-graphql@0.58.0':
+ resolution: {integrity: sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-hapi@0.55.0':
- resolution: {integrity: sha512-prqAkRf9e4eEpy4G3UcR32prKE8NLNlA90TdEU1UsghOTg0jUvs40Jz8LQWFEs5NbLbXHYGzB4CYVkCI8eWEVQ==}
+ '@opentelemetry/instrumentation-hapi@0.57.0':
+ resolution: {integrity: sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-http@0.208.0':
- resolution: {integrity: sha512-rhmK46DRWEbQQB77RxmVXGyjs6783crXCnFjYQj+4tDH/Kpv9Rbg3h2kaNyp5Vz2emF1f9HOQQvZoHzwMWOFZQ==}
+ '@opentelemetry/instrumentation-http@0.211.0':
+ resolution: {integrity: sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-ioredis@0.56.0':
- resolution: {integrity: sha512-XSWeqsd3rKSsT3WBz/JKJDcZD4QYElZEa0xVdX8f9dh4h4QgXhKRLorVsVkK3uXFbC2sZKAS2Ds+YolGwD83Dg==}
+ '@opentelemetry/instrumentation-ioredis@0.59.0':
+ resolution: {integrity: sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-kafkajs@0.18.0':
- resolution: {integrity: sha512-KCL/1HnZN5zkUMgPyOxfGjLjbXjpd4odDToy+7c+UsthIzVLFf99LnfIBE8YSSrYE4+uS7OwJMhvhg3tWjqMBg==}
+ '@opentelemetry/instrumentation-kafkajs@0.20.0':
+ resolution: {integrity: sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-knex@0.53.0':
- resolution: {integrity: sha512-xngn5cH2mVXFmiT1XfQ1aHqq1m4xb5wvU6j9lSgLlihJ1bXzsO543cpDwjrZm2nMrlpddBf55w8+bfS4qDh60g==}
+ '@opentelemetry/instrumentation-knex@0.55.0':
+ resolution: {integrity: sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-koa@0.57.0':
- resolution: {integrity: sha512-3JS8PU/D5E3q295mwloU2v7c7/m+DyCqdu62BIzWt+3u9utjxC9QS7v6WmUNuoDN3RM+Q+D1Gpj13ERo+m7CGg==}
+ '@opentelemetry/instrumentation-koa@0.59.0':
+ resolution: {integrity: sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.9.0
- '@opentelemetry/instrumentation-lru-memoizer@0.53.0':
- resolution: {integrity: sha512-LDwWz5cPkWWr0HBIuZUjslyvijljTwmwiItpMTHujaULZCxcYE9eU44Qf/pbVC8TulT0IhZi+RoGvHKXvNhysw==}
+ '@opentelemetry/instrumentation-lru-memoizer@0.55.0':
+ resolution: {integrity: sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-mongodb@0.61.0':
- resolution: {integrity: sha512-OV3i2DSoY5M/pmLk+68xr5RvkHU8DRB3DKMzYJdwDdcxeLs62tLbkmRyqJZsYf3Ht7j11rq35pHOWLuLzXL7pQ==}
+ '@opentelemetry/instrumentation-mongodb@0.64.0':
+ resolution: {integrity: sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-mongoose@0.55.0':
- resolution: {integrity: sha512-5afj0HfF6aM6Nlqgu6/PPHFk8QBfIe3+zF9FGpX76jWPS0/dujoEYn82/XcLSaW5LPUDW8sni+YeK0vTBNri+w==}
+ '@opentelemetry/instrumentation-mongoose@0.57.0':
+ resolution: {integrity: sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-mysql2@0.55.0':
- resolution: {integrity: sha512-0cs8whQG55aIi20gnK8B7cco6OK6N+enNhW0p5284MvqJ5EPi+I1YlWsWXgzv/V2HFirEejkvKiI4Iw21OqDWg==}
+ '@opentelemetry/instrumentation-mysql2@0.57.0':
+ resolution: {integrity: sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-mysql@0.54.0':
- resolution: {integrity: sha512-bqC1YhnwAeWmRzy1/Xf9cDqxNG2d/JDkaxnqF5N6iJKN1eVWI+vg7NfDkf52/Nggp3tl1jcC++ptC61BD6738A==}
+ '@opentelemetry/instrumentation-mysql@0.57.0':
+ resolution: {integrity: sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-pg@0.61.0':
- resolution: {integrity: sha512-UeV7KeTnRSM7ECHa3YscoklhUtTQPs6V6qYpG283AB7xpnPGCUCUfECFT9jFg6/iZOQTt3FHkB1wGTJCNZEvPw==}
+ '@opentelemetry/instrumentation-pg@0.63.0':
+ resolution: {integrity: sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-redis@0.57.0':
- resolution: {integrity: sha512-bCxTHQFXzrU3eU1LZnOZQ3s5LURxQPDlU3/upBzlWY77qOI1GZuGofazj3jtzjctMJeBEJhNwIFEgRPBX1kp/Q==}
+ '@opentelemetry/instrumentation-redis@0.59.0':
+ resolution: {integrity: sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-tedious@0.27.0':
- resolution: {integrity: sha512-jRtyUJNZppPBjPae4ZjIQ2eqJbcRaRfJkr0lQLHFmOU/no5A6e9s1OHLd5XZyZoBJ/ymngZitanyRRA5cniseA==}
+ '@opentelemetry/instrumentation-tedious@0.30.0':
+ resolution: {integrity: sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
- '@opentelemetry/instrumentation-undici@0.19.0':
- resolution: {integrity: sha512-Pst/RhR61A2OoZQZkn6OLpdVpXp6qn3Y92wXa6umfJe9rV640r4bc6SWvw4pPN6DiQqPu2c8gnSSZPDtC6JlpQ==}
+ '@opentelemetry/instrumentation-undici@0.21.0':
+ resolution: {integrity: sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.7.0
- '@opentelemetry/instrumentation@0.208.0':
- resolution: {integrity: sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==}
+ '@opentelemetry/instrumentation@0.207.0':
+ resolution: {integrity: sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation@0.211.0':
+ resolution: {integrity: sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
@@ -3394,20 +3147,20 @@ packages:
resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==}
engines: {node: ^18.19.0 || >=20.6.0}
- '@opentelemetry/resources@2.2.0':
- resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==}
+ '@opentelemetry/resources@2.5.0':
+ resolution: {integrity: sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
- '@opentelemetry/sdk-trace-base@2.2.0':
- resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==}
+ '@opentelemetry/sdk-trace-base@2.5.0':
+ resolution: {integrity: sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
- '@opentelemetry/semantic-conventions@1.38.0':
- resolution: {integrity: sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==}
+ '@opentelemetry/semantic-conventions@1.39.0':
+ resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==}
engines: {node: '>=14'}
'@opentelemetry/sql-common@0.41.2':
@@ -3555,8 +3308,8 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
- '@prisma/instrumentation@6.19.0':
- resolution: {integrity: sha512-QcuYy25pkXM8BJ37wVFBO7Zh34nyRV1GOb2n3lPkkbRYfl4hWl3PTcImP41P0KrzVXfa/45p6eVCos27x3exIg==}
+ '@prisma/instrumentation@7.2.0':
+ resolution: {integrity: sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==}
peerDependencies:
'@opentelemetry/api': ^1.8
@@ -3566,8 +3319,8 @@ packages:
peerDependencies:
ajv: 4.11.8 - 8
- '@readme/openapi-parser@5.2.1':
- resolution: {integrity: sha512-dyRrGM2ZW+imhDkA8pcq4ChKDAFvhVf15hS+UEqrjdfQzN8r4ekUTtL5k0/4izfUork/rd8T+e/pbYOYk67Pnw==}
+ '@readme/openapi-parser@5.5.0':
+ resolution: {integrity: sha512-uY9+3ouf6NWd/ck5PzlhcRwvRFLDd9ajRsMzZPp/iqdReGgPR7m8xEXtCYbLTGXJZcrDgYyNsHUL7e87Wsh+ew==}
engines: {node: '>=20'}
peerDependencies:
openapi-types: '>=7'
@@ -3582,12 +3335,12 @@ packages:
'@redocly/config@0.22.2':
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
- '@redocly/openapi-core@1.34.5':
- resolution: {integrity: sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==}
+ '@redocly/openapi-core@1.34.6':
+ resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==}
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
- '@rolldown/pluginutils@1.0.0-beta.50':
- resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==}
+ '@rolldown/pluginutils@1.0.0-rc.2':
+ resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==}
'@rollup/plugin-json@6.1.0':
resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
@@ -3616,249 +3369,284 @@ packages:
rollup:
optional: true
- '@rollup/rollup-android-arm-eabi@4.53.3':
- resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==}
+ '@rollup/rollup-android-arm-eabi@4.59.0':
+ resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm64@4.53.3':
- resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==}
+ '@rollup/rollup-android-arm64@4.59.0':
+ resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.53.3':
- resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==}
+ '@rollup/rollup-darwin-arm64@4.59.0':
+ resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.53.3':
- resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==}
+ '@rollup/rollup-darwin-x64@4.59.0':
+ resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.53.3':
- resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==}
+ '@rollup/rollup-freebsd-arm64@4.59.0':
+ resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.53.3':
- resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==}
+ '@rollup/rollup-freebsd-x64@4.59.0':
+ resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.53.3':
- resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==}
+ '@rollup/rollup-linux-arm-gnueabihf@4.59.0':
+ resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-arm-musleabihf@4.53.3':
- resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==}
+ '@rollup/rollup-linux-arm-musleabihf@4.59.0':
+ resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-arm64-gnu@4.53.3':
- resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==}
+ '@rollup/rollup-linux-arm64-gnu@4.59.0':
+ resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-arm64-musl@4.53.3':
- resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==}
+ '@rollup/rollup-linux-arm64-musl@4.59.0':
+ resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-loong64-gnu@4.53.3':
- resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==}
+ '@rollup/rollup-linux-loong64-gnu@4.59.0':
+ resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-ppc64-gnu@4.53.3':
- resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==}
+ '@rollup/rollup-linux-loong64-musl@4.59.0':
+ resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.59.0':
+ resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-riscv64-gnu@4.53.3':
- resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==}
+ '@rollup/rollup-linux-ppc64-musl@4.59.0':
+ resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.59.0':
+ resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-riscv64-musl@4.53.3':
- resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==}
+ '@rollup/rollup-linux-riscv64-musl@4.59.0':
+ resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
- '@rollup/rollup-linux-s390x-gnu@4.53.3':
- resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==}
+ '@rollup/rollup-linux-s390x-gnu@4.59.0':
+ resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-x64-gnu@4.53.3':
- resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==}
+ '@rollup/rollup-linux-x64-gnu@4.59.0':
+ resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@rollup/rollup-linux-x64-musl@4.53.3':
- resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==}
+ '@rollup/rollup-linux-x64-musl@4.59.0':
+ resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
libc: [musl]
- '@rollup/rollup-openharmony-arm64@4.53.3':
- resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==}
+ '@rollup/rollup-openbsd-x64@4.59.0':
+ resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.59.0':
+ resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
cpu: [arm64]
os: [openharmony]
- '@rollup/rollup-win32-arm64-msvc@4.53.3':
- resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==}
+ '@rollup/rollup-win32-arm64-msvc@4.59.0':
+ resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.53.3':
- resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==}
+ '@rollup/rollup-win32-ia32-msvc@4.59.0':
+ resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-x64-gnu@4.53.3':
- resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==}
+ '@rollup/rollup-win32-x64-gnu@4.59.0':
+ resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
cpu: [x64]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.53.3':
- resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==}
+ '@rollup/rollup-win32-x64-msvc@4.59.0':
+ resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
cpu: [x64]
os: [win32]
'@rtsao/scc@1.1.0':
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
- '@rushstack/node-core-library@5.19.1':
- resolution: {integrity: sha512-ESpb2Tajlatgbmzzukg6zyAhH+sICqJR2CNXNhXcEbz6UGCQfrKCtkxOpJTftWc8RGouroHG0Nud1SJAszvpmA==}
+ '@rushstack/node-core-library@5.20.1':
+ resolution: {integrity: sha512-QvxZyh+RsTJ77JpQkS9K9lJujh6lj5WyMxieT0bdACtwqxEkGB9zCuSMX5UlXRweaIgSpu1ztdHmhV07fKUpMg==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
- '@rushstack/problem-matcher@0.1.1':
- resolution: {integrity: sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==}
+ '@rushstack/problem-matcher@0.2.1':
+ resolution: {integrity: sha512-gulfhBs6n+I5b7DvjKRfhMGyUejtSgOHTclF/eONr8hcgF1APEDjhxIsfdUYYMzC3rvLwGluqLjbwCFZ8nxrog==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
- '@rushstack/rig-package@0.6.0':
- resolution: {integrity: sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==}
+ '@rushstack/rig-package@0.7.1':
+ resolution: {integrity: sha512-hLwDnp4yMcAd/gcUol8NPWNctpIXzVOgMyhZ8DagnEJls9TOZd0xF//5hS+YTiX7/+4rLfBra+NoB3rtFxjDdA==}
- '@rushstack/terminal@0.19.5':
- resolution: {integrity: sha512-6k5tpdB88G0K7QrH/3yfKO84HK9ggftfUZ51p7fePyCE7+RLLHkWZbID9OFWbXuna+eeCFE7AkKnRMHMxNbz7Q==}
+ '@rushstack/terminal@0.22.1':
+ resolution: {integrity: sha512-Mdtu0VN7v31O5Zcno8ZZH5kQHF13Ez7WN9Aio7nFJVcR36i4bkERionYrWgBDQJ0JdVPLKGecZER/xRU5IvGLw==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
- '@rushstack/ts-command-line@5.1.5':
- resolution: {integrity: sha512-YmrFTFUdHXblYSa+Xc9OO9FsL/XFcckZy0ycQ6q7VSBsVs5P0uD9vcges5Q9vctGlVdu27w+Ct6IuJ458V0cTQ==}
+ '@rushstack/ts-command-line@5.3.1':
+ resolution: {integrity: sha512-mid/JIZSJafwy3x9e4v0wVLuAqSSYYErEHV0HXPALYLSBN13YNkR5caOk0hf97lSRKrxhtvQjGaDKSEelR3sMg==}
'@sec-ant/readable-stream@0.4.1':
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
- '@sentry-internal/browser-utils@10.29.0':
- resolution: {integrity: sha512-M3kycMY6f3KY9a8jDYac+yG0E3ZgWVWSxlOEC5MhYyX+g7mqxkwrb3LFQyuxSm/m+CCgMTCaPOOaB2twXP6EQg==}
+ '@sentry-internal/browser-utils@10.39.0':
+ resolution: {integrity: sha512-W6WODonMGiI13Az5P7jd/m2lj/JpIyuVKg7wE4X+YdlMehLspAv6I7gRE4OBSumS14ZjdaYDpD/lwtnBwKAzcA==}
engines: {node: '>=18'}
- '@sentry-internal/feedback@10.29.0':
- resolution: {integrity: sha512-Y7IRsNeS99cEONu1mZWZc3HvbjNnu59Hgymm0swFFKbdgbCgdT6l85kn2oLsuq4Ew8Dw/pL/Sgpwsl9UgYFpUg==}
+ '@sentry-internal/feedback@10.39.0':
+ resolution: {integrity: sha512-cRXmmDeOr5FzVsBNRLU4WDEuC3fhuD0XV362EWl4DI3XBGao8ukaueKcLIKic5WZx6uXimjWw/UJmDLgxeCqkg==}
engines: {node: '>=18'}
'@sentry-internal/node-cpu-profiler@2.2.0':
resolution: {integrity: sha512-oLHVYurqZfADPh5hvmQYS5qx8t0UZzT2u6+/68VXsFruQEOnYJTODKgU3BVLmemRs3WE6kCJjPeFdHVYOQGSzQ==}
engines: {node: '>=18'}
- '@sentry-internal/replay-canvas@10.29.0':
- resolution: {integrity: sha512-typY4JrpAQQGPuSyd/BD8+nNCbvTV2UVvKzr+iKgI0m1qc4Dz8tHZ4Nfais2Z8eYn/pL1kqVQN5ERTmJoYFdIw==}
+ '@sentry-internal/replay-canvas@10.39.0':
+ resolution: {integrity: sha512-TTiX0XWCcqTqFGJjEZYObk93j/sJmXcqPzcu0cN2mIkKnnaHDY3w74SHZCshKqIr0AOQdt1HDNa36s3TCdt0Jw==}
engines: {node: '>=18'}
- '@sentry-internal/replay@10.29.0':
- resolution: {integrity: sha512-45NVw9PwB9TQ8z+xJ6G6Za+wmQ1RTA35heBSzR6U4bknj8LmA04k2iwnobvxCBEQXeLfcJEO1vFgagMoqMZMBw==}
+ '@sentry-internal/replay@10.39.0':
+ resolution: {integrity: sha512-obZoYOrUfxIYBHkmtPpItRdE38VuzF1VIxSgZ8Mbtq/9UvCWh+eOaVWU2stN/cVu1KYuYX0nQwBvdN28L6y/JA==}
engines: {node: '>=18'}
- '@sentry/browser@10.29.0':
- resolution: {integrity: sha512-XdbyIR6F4qoR9Z1JCWTgunVcTJjS9p2Th+v4wYs4ME+ZdLC4tuKKmRgYg3YdSIWCn1CBfIgdI6wqETSf7H6Njw==}
+ '@sentry/browser@10.39.0':
+ resolution: {integrity: sha512-I50W/1PDJWyqgNrGufGhBYCmmO3Bb159nx2Ut2bKoVveTfgH/hLEtDyW0kHo8Fu454mW+ukyXfU4L4s+kB9aaw==}
engines: {node: '>=18'}
- '@sentry/core@10.29.0':
- resolution: {integrity: sha512-olQ2DU9dA/Bwsz3PtA9KNXRMqBWRQSkPw+MxwWEoU1K1qtiM9L0j6lbEFb5iSY3d7WYD5MB+1d5COugjSBrHtw==}
+ '@sentry/core@10.39.0':
+ resolution: {integrity: sha512-xCLip2mBwCdRrvXHtVEULX0NffUTYZZBhEUGht0WFL+GNdNQ7gmBOGOczhZlrf2hgFFtDO0fs1xiP9bqq5orEQ==}
engines: {node: '>=18'}
- '@sentry/node-core@10.29.0':
- resolution: {integrity: sha512-f/Y0okHhPPb5HnYNBqCivJ2YuXtSadvcIx16dzU5mHQxZhgGednUCPEX7rsvPcd4HneQz12HKLqxbAmNu+b3FA==}
+ '@sentry/node-core@10.39.0':
+ resolution: {integrity: sha512-xdeBG00TmtAcGvXnZNbqOCvnZ5kY3s5aT/L8wUQ0w0TT2KmrC9XL/7UHUfJ45TLbjl10kZOtaMQXgUjpwSJW+g==}
engines: {node: '>=18'}
peerDependencies:
'@opentelemetry/api': ^1.9.0
- '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0
- '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0
+ '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0
+ '@opentelemetry/core': ^1.30.1 || ^2.1.0
'@opentelemetry/instrumentation': '>=0.57.1 <1'
- '@opentelemetry/resources': ^1.30.1 || ^2.1.0 || ^2.2.0
- '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0
- '@opentelemetry/semantic-conventions': ^1.37.0
+ '@opentelemetry/resources': ^1.30.1 || ^2.1.0
+ '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0
+ '@opentelemetry/semantic-conventions': ^1.39.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@opentelemetry/context-async-hooks':
+ optional: true
+ '@opentelemetry/core':
+ optional: true
+ '@opentelemetry/instrumentation':
+ optional: true
+ '@opentelemetry/resources':
+ optional: true
+ '@opentelemetry/sdk-trace-base':
+ optional: true
+ '@opentelemetry/semantic-conventions':
+ optional: true
- '@sentry/node@10.29.0':
- resolution: {integrity: sha512-9j8VzV06VCj+H8tlxpfa7BNN4HzH5exv68WOufdMTXzzWLOXnzrdNDoYplm1G2S3LMvWsc1SVI3a8A0yBY7oWg==}
+ '@sentry/node@10.39.0':
+ resolution: {integrity: sha512-dx66DtU/xkCTPEDsjU+mYSIEbzu06pzKNQcDA2wvx7wvwsUciZ5yA32Ce/o6p2uHHgy0/joJX9rP5J/BIijaOA==}
engines: {node: '>=18'}
- '@sentry/opentelemetry@10.29.0':
- resolution: {integrity: sha512-5QvtAwS73HlI/+OTF1poAFELzsc0se+PHmMsXGGrOeNBvjCr3ZE8qvke09aeMn7uRImf3Nc9J6i2KtSHJnbKPA==}
+ '@sentry/opentelemetry@10.39.0':
+ resolution: {integrity: sha512-eU8t/pyxjy7xYt6PNCVxT+8SJw5E3pnupdcUNN4ClqG4O5lX4QCDLtId48ki7i30VqrLtR7vmCHMSvqXXdvXPA==}
engines: {node: '>=18'}
peerDependencies:
'@opentelemetry/api': ^1.9.0
- '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0
- '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0
- '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0
- '@opentelemetry/semantic-conventions': ^1.37.0
+ '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0
+ '@opentelemetry/core': ^1.30.1 || ^2.1.0
+ '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0
+ '@opentelemetry/semantic-conventions': ^1.39.0
- '@sentry/profiling-node@10.29.0':
- resolution: {integrity: sha512-qt34HNmxIytAKoOYg64CIFoEHRfkxDB3qT2McB5zq1YbzU0b89j3h2roF8A/dsWAlppV0R2DAXBkoEMNgWzHEQ==}
+ '@sentry/profiling-node@10.39.0':
+ resolution: {integrity: sha512-S+YHtTyGIhUFg/qYsrJ6KYHkZRqKSNx4JDWIZsn1/w6+87NfijDT/pVj+mdUHV6Z2imj+/HyAURANHgaZJKy4w==}
engines: {node: '>=18'}
hasBin: true
- '@sentry/vue@10.29.0':
- resolution: {integrity: sha512-04Ng/ducvkTohSDhfQvRWrBxwGuQfCggHjTcEoyE3uUrR9ReF5VwyiYo5Kq4ADgZhsao5xqj4JQVg3bc8oTZUA==}
+ '@sentry/vue@10.39.0':
+ resolution: {integrity: sha512-5fLGfodMuFtOS/TAr9Il+zOLwmZdd1TNSg9Y4nMA5tRqhksM9EO7z9606mLxdlH7pMwn5UEySL4XKKvnJXfMmw==}
engines: {node: '>=18'}
peerDependencies:
+ '@tanstack/vue-router': ^1.64.0
pinia: 2.x || 3.x
vue: 2.x || 3.x
peerDependenciesMeta:
+ '@tanstack/vue-router':
+ optional: true
pinia:
optional: true
- '@shikijs/core@3.19.0':
- resolution: {integrity: sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA==}
+ '@shikijs/core@3.22.0':
+ resolution: {integrity: sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==}
- '@shikijs/engine-javascript@3.19.0':
- resolution: {integrity: sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ==}
+ '@shikijs/engine-javascript@3.22.0':
+ resolution: {integrity: sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw==}
- '@shikijs/engine-oniguruma@3.19.0':
- resolution: {integrity: sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg==}
+ '@shikijs/engine-oniguruma@3.22.0':
+ resolution: {integrity: sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==}
- '@shikijs/langs@3.19.0':
- resolution: {integrity: sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg==}
+ '@shikijs/langs@3.22.0':
+ resolution: {integrity: sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==}
- '@shikijs/themes@3.19.0':
- resolution: {integrity: sha512-H36qw+oh91Y0s6OlFfdSuQ0Ld+5CgB/VE6gNPK+Hk4VRbVG/XQgkjnt4KzfnnoO6tZPtKJKHPjwebOCfjd6F8A==}
+ '@shikijs/themes@3.22.0':
+ resolution: {integrity: sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==}
- '@shikijs/types@3.19.0':
- resolution: {integrity: sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ==}
+ '@shikijs/types@3.22.0':
+ resolution: {integrity: sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
@@ -3910,8 +3698,8 @@ packages:
'@sinonjs/fake-timers@13.0.5':
resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==}
- '@sinonjs/fake-timers@15.0.0':
- resolution: {integrity: sha512-dlUB2oL+hDIYkIq/OWFBDhQAuU6kDey3eeMiYpVb7UXHhkMq/r1HloKXAbJwJZpYWkFWsydLjMqDpueMUEOjXQ==}
+ '@sinonjs/fake-timers@15.1.0':
+ resolution: {integrity: sha512-cqfapCxwTGsrR80FEgOoPsTonoefMBY7dnUEbQ+GRcved0jvkJLzvX6F4WtN+HBqbPX/SiFsIRUp+IrCW/2I2w==}
'@sinonjs/samsam@8.0.3':
resolution: {integrity: sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==}
@@ -3919,8 +3707,8 @@ packages:
'@sinonjs/text-encoding@0.7.3':
resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==}
- '@smithy/abort-controller@4.2.5':
- resolution: {integrity: sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==}
+ '@smithy/abort-controller@4.2.8':
+ resolution: {integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==}
engines: {node: '>=18.0.0'}
'@smithy/chunked-blob-reader-native@4.2.1':
@@ -3931,56 +3719,56 @@ packages:
resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==}
engines: {node: '>=18.0.0'}
- '@smithy/config-resolver@4.4.3':
- resolution: {integrity: sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==}
+ '@smithy/config-resolver@4.4.6':
+ resolution: {integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==}
engines: {node: '>=18.0.0'}
- '@smithy/core@3.18.7':
- resolution: {integrity: sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==}
+ '@smithy/core@3.23.2':
+ resolution: {integrity: sha512-HaaH4VbGie4t0+9nY3tNBRSxVTr96wzIqexUa6C2qx3MPePAuz7lIxPxYtt1Wc//SPfJLNoZJzfdt0B6ksj2jA==}
engines: {node: '>=18.0.0'}
- '@smithy/credential-provider-imds@4.2.5':
- resolution: {integrity: sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==}
+ '@smithy/credential-provider-imds@4.2.8':
+ resolution: {integrity: sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-codec@4.2.5':
- resolution: {integrity: sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==}
+ '@smithy/eventstream-codec@4.2.8':
+ resolution: {integrity: sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-browser@4.2.5':
- resolution: {integrity: sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw==}
+ '@smithy/eventstream-serde-browser@4.2.8':
+ resolution: {integrity: sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-config-resolver@4.3.5':
- resolution: {integrity: sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ==}
+ '@smithy/eventstream-serde-config-resolver@4.3.8':
+ resolution: {integrity: sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-node@4.2.5':
- resolution: {integrity: sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg==}
+ '@smithy/eventstream-serde-node@4.2.8':
+ resolution: {integrity: sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-universal@4.2.5':
- resolution: {integrity: sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q==}
+ '@smithy/eventstream-serde-universal@4.2.8':
+ resolution: {integrity: sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==}
engines: {node: '>=18.0.0'}
- '@smithy/fetch-http-handler@5.3.6':
- resolution: {integrity: sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==}
+ '@smithy/fetch-http-handler@5.3.9':
+ resolution: {integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-blob-browser@4.2.6':
- resolution: {integrity: sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw==}
+ '@smithy/hash-blob-browser@4.2.9':
+ resolution: {integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-node@4.2.5':
- resolution: {integrity: sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==}
+ '@smithy/hash-node@4.2.8':
+ resolution: {integrity: sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-stream-node@4.2.5':
- resolution: {integrity: sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q==}
+ '@smithy/hash-stream-node@4.2.8':
+ resolution: {integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==}
engines: {node: '>=18.0.0'}
- '@smithy/invalid-dependency@4.2.5':
- resolution: {integrity: sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==}
+ '@smithy/invalid-dependency@4.2.8':
+ resolution: {integrity: sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==}
engines: {node: '>=18.0.0'}
'@smithy/is-array-buffer@2.2.0':
@@ -3991,76 +3779,76 @@ packages:
resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==}
engines: {node: '>=18.0.0'}
- '@smithy/md5-js@4.2.5':
- resolution: {integrity: sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg==}
+ '@smithy/md5-js@4.2.8':
+ resolution: {integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-content-length@4.2.5':
- resolution: {integrity: sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==}
+ '@smithy/middleware-content-length@4.2.8':
+ resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-endpoint@4.3.14':
- resolution: {integrity: sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg==}
+ '@smithy/middleware-endpoint@4.4.16':
+ resolution: {integrity: sha512-L5GICFCSsNhbJ5JSKeWFGFy16Q2OhoBizb3X2DrxaJwXSEujVvjG9Jt386dpQn2t7jINglQl0b4K/Su69BdbMA==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-retry@4.4.14':
- resolution: {integrity: sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q==}
+ '@smithy/middleware-retry@4.4.33':
+ resolution: {integrity: sha512-jLqZOdJhtIL4lnA9hXnAG6GgnJlo1sD3FqsTxm9wSfjviqgWesY/TMBVnT84yr4O0Vfe0jWoXlfFbzsBVph3WA==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-serde@4.2.6':
- resolution: {integrity: sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==}
+ '@smithy/middleware-serde@4.2.9':
+ resolution: {integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-stack@4.2.5':
- resolution: {integrity: sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==}
+ '@smithy/middleware-stack@4.2.8':
+ resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==}
engines: {node: '>=18.0.0'}
- '@smithy/node-config-provider@4.3.5':
- resolution: {integrity: sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==}
+ '@smithy/node-config-provider@4.3.8':
+ resolution: {integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==}
engines: {node: '>=18.0.0'}
- '@smithy/node-http-handler@4.4.5':
- resolution: {integrity: sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==}
+ '@smithy/node-http-handler@4.4.10':
+ resolution: {integrity: sha512-u4YeUwOWRZaHbWaebvrs3UhwQwj+2VNmcVCwXcYTvPIuVyM7Ex1ftAj+fdbG/P4AkBwLq/+SKn+ydOI4ZJE9PA==}
engines: {node: '>=18.0.0'}
- '@smithy/property-provider@4.2.5':
- resolution: {integrity: sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==}
+ '@smithy/property-provider@4.2.8':
+ resolution: {integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==}
engines: {node: '>=18.0.0'}
- '@smithy/protocol-http@5.3.5':
- resolution: {integrity: sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==}
+ '@smithy/protocol-http@5.3.8':
+ resolution: {integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==}
engines: {node: '>=18.0.0'}
- '@smithy/querystring-builder@4.2.5':
- resolution: {integrity: sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==}
+ '@smithy/querystring-builder@4.2.8':
+ resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==}
engines: {node: '>=18.0.0'}
- '@smithy/querystring-parser@4.2.5':
- resolution: {integrity: sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==}
+ '@smithy/querystring-parser@4.2.8':
+ resolution: {integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==}
engines: {node: '>=18.0.0'}
- '@smithy/service-error-classification@4.2.5':
- resolution: {integrity: sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==}
+ '@smithy/service-error-classification@4.2.8':
+ resolution: {integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==}
engines: {node: '>=18.0.0'}
- '@smithy/shared-ini-file-loader@4.4.0':
- resolution: {integrity: sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==}
+ '@smithy/shared-ini-file-loader@4.4.3':
+ resolution: {integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==}
engines: {node: '>=18.0.0'}
- '@smithy/signature-v4@5.3.5':
- resolution: {integrity: sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==}
+ '@smithy/signature-v4@5.3.8':
+ resolution: {integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==}
engines: {node: '>=18.0.0'}
- '@smithy/smithy-client@4.9.10':
- resolution: {integrity: sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ==}
+ '@smithy/smithy-client@4.11.5':
+ resolution: {integrity: sha512-xixwBRqoeP2IUgcAl3U9dvJXc+qJum4lzo3maaJxifsZxKUYLfVfCXvhT4/jD01sRrHg5zjd1cw2Zmjr4/SuKQ==}
engines: {node: '>=18.0.0'}
- '@smithy/types@4.9.0':
- resolution: {integrity: sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==}
+ '@smithy/types@4.12.0':
+ resolution: {integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==}
engines: {node: '>=18.0.0'}
- '@smithy/url-parser@4.2.5':
- resolution: {integrity: sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==}
+ '@smithy/url-parser@4.2.8':
+ resolution: {integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==}
engines: {node: '>=18.0.0'}
'@smithy/util-base64@4.3.0':
@@ -4087,32 +3875,32 @@ packages:
resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==}
engines: {node: '>=18.0.0'}
- '@smithy/util-defaults-mode-browser@4.3.13':
- resolution: {integrity: sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA==}
+ '@smithy/util-defaults-mode-browser@4.3.32':
+ resolution: {integrity: sha512-092sjYfFMQ/iaPH798LY/OJFBcYu0sSK34Oy9vdixhsU36zlZu8OcYjF3TD4e2ARupyK7xaxPXl+T0VIJTEkkg==}
engines: {node: '>=18.0.0'}
- '@smithy/util-defaults-mode-node@4.2.16':
- resolution: {integrity: sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg==}
+ '@smithy/util-defaults-mode-node@4.2.35':
+ resolution: {integrity: sha512-miz/ggz87M8VuM29y7jJZMYkn7+IErM5p5UgKIf8OtqVs/h2bXr1Bt3uTsREsI/4nK8a0PQERbAPsVPVNIsG7Q==}
engines: {node: '>=18.0.0'}
- '@smithy/util-endpoints@3.2.5':
- resolution: {integrity: sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==}
+ '@smithy/util-endpoints@3.2.8':
+ resolution: {integrity: sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==}
engines: {node: '>=18.0.0'}
'@smithy/util-hex-encoding@4.2.0':
resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==}
engines: {node: '>=18.0.0'}
- '@smithy/util-middleware@4.2.5':
- resolution: {integrity: sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==}
+ '@smithy/util-middleware@4.2.8':
+ resolution: {integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==}
engines: {node: '>=18.0.0'}
- '@smithy/util-retry@4.2.5':
- resolution: {integrity: sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==}
+ '@smithy/util-retry@4.2.8':
+ resolution: {integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==}
engines: {node: '>=18.0.0'}
- '@smithy/util-stream@4.5.6':
- resolution: {integrity: sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==}
+ '@smithy/util-stream@4.5.12':
+ resolution: {integrity: sha512-D8tgkrmhAX/UNeCZbqbEO3uqyghUnEmmoO9YEvRuwxjlkKKUE7FOgCJnqpTlQPe9MApdWPky58mNQQHbnCzoNg==}
engines: {node: '>=18.0.0'}
'@smithy/util-uri-escape@4.2.0':
@@ -4127,8 +3915,8 @@ packages:
resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==}
engines: {node: '>=18.0.0'}
- '@smithy/util-waiter@4.2.5':
- resolution: {integrity: sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==}
+ '@smithy/util-waiter@4.2.8':
+ resolution: {integrity: sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==}
engines: {node: '>=18.0.0'}
'@smithy/uuid@1.1.0':
@@ -4141,114 +3929,114 @@ packages:
'@standard-schema/spec@1.0.0':
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
- '@storybook/addon-actions@8.6.14':
- resolution: {integrity: sha512-mDQxylxGGCQSK7tJPkD144J8jWh9IU9ziJMHfB84PKpI/V5ZgqMDnpr2bssTrUaGDqU5e1/z8KcRF+Melhs9pQ==}
+ '@storybook/addon-actions@8.6.17':
+ resolution: {integrity: sha512-/G3Y7WIzGHMtKT6r3KCqgY/pAzfMhNHvBoRWoPfxMa27GiwmUaJlbfRPSReK/jGz6ye0Uwqix+NFbRXKiTqOJQ==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-backgrounds@8.6.14':
- resolution: {integrity: sha512-l9xS8qWe5n4tvMwth09QxH2PmJbCctEvBAc1tjjRasAfrd69f7/uFK4WhwJAstzBTNgTc8VXI4w8ZR97i1sFbg==}
+ '@storybook/addon-backgrounds@8.6.17':
+ resolution: {integrity: sha512-oh7jEQUt8WhH7cBm0jsJ1dujyaujM5rVS5IXJmDgdFJ8l0pqGzOUDmgkBVX147Uo5W1U47Sx+hA69lg6TKPOMQ==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-controls@8.6.14':
- resolution: {integrity: sha512-IiQpkNJdiRyA4Mq9mzjZlvQugL/aE7hNgVxBBGPiIZG6wb6Ht9hNnBYpap5ZXXFKV9p2qVI0FZK445ONmAa+Cw==}
+ '@storybook/addon-controls@8.6.17':
+ resolution: {integrity: sha512-kquUfiJyJMh8IHviTIz1A4UKqqNfvqrYFku9D3UukqkTriM1ngDOb9nryaJcUNTkco5JmIuGjYEJisva+u13lw==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-docs@8.6.14':
- resolution: {integrity: sha512-Obpd0OhAF99JyU5pp5ci17YmpcQtMNgqW2pTXV8jAiiipWpwO++hNDeQmLmlSXB399XjtRDOcDVkoc7rc6JzdQ==}
+ '@storybook/addon-docs@8.6.17':
+ resolution: {integrity: sha512-zvcSzoYvaZO4l9NxsviDr5vmuq8GVnH4Ap0v+5sSTq192yevm/iQcRnkWYBD9E/Lg5GBeyE+Ml2vjEOK+EPBEg==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-essentials@8.6.14':
- resolution: {integrity: sha512-5ZZSHNaW9mXMOFkoPyc3QkoNGdJHETZydI62/OASR0lmPlJ1065TNigEo5dJddmZNn0/3bkE8eKMAzLnO5eIdA==}
+ '@storybook/addon-essentials@8.6.17':
+ resolution: {integrity: sha512-nyp2RUxWGpD+xhGWOo221kHOY6cZlgXsV1F11sn7WxkH+yA7YHhLLYlIPHmNKKH+hdxN0rnlcpwjbr21u0Katg==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-highlight@8.6.14':
- resolution: {integrity: sha512-4H19OJlapkofiE9tM6K/vsepf4ir9jMm9T+zw5L85blJZxhKZIbJ6FO0TCG9PDc4iPt3L6+aq5B0X29s9zicNQ==}
+ '@storybook/addon-highlight@8.6.17':
+ resolution: {integrity: sha512-Tf7DxksSg+DqH0a0rLIpB5g9bJBUHcqmEGeYGX7EPQrXBpQAtFXz/XdzuD8eYDlPC1r42iQQw4w+CQnXJCOHFw==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-interactions@8.6.14':
- resolution: {integrity: sha512-8VmElhm2XOjh22l/dO4UmXxNOolGhNiSpBcls2pqWSraVh4a670EyYBZsHpkXqfNHo2YgKyZN3C91+9zfH79qQ==}
+ '@storybook/addon-interactions@8.6.17':
+ resolution: {integrity: sha512-aj0jC5kMnYYHBofAZrPAfZf2/j9BXMIwMJ9dLdc7Qwxsvig3txWlSx7Wh+szaR2jDz/areh1rZyrwiPHyxxQ4g==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-links@10.1.5':
- resolution: {integrity: sha512-a1uXpNgIZg6U2v3+431RNFCLvcuNPT2kQjFEKNAVLyNe4Krig/yR3HabGoxKHINLrtBzn/rE9yNeDhMKYfvVnA==}
+ '@storybook/addon-links@10.2.10':
+ resolution: {integrity: sha512-oo9Xx4/2OVJtptXKpqH4ySri7ZuBdiSOXlZVGejEfLa0Jeajlh/KIlREpGvzPPOqUVT7dSddWzBjJmJUyQC3ew==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.1.5
+ storybook: ^10.2.10
peerDependenciesMeta:
react:
optional: true
- '@storybook/addon-mdx-gfm@8.6.14':
- resolution: {integrity: sha512-ClfngOSwFrhc3x2dXSzfBSSbzz4VHzUs0XOg9V8fj1bgQhmPoMz9OD3vIjbnJOC33wORbC0ZpfcQPt3RGILYrA==}
+ '@storybook/addon-mdx-gfm@8.6.17':
+ resolution: {integrity: sha512-6gfRnNmUVYJNHrnOjix2v2QmsiJQW1+yCWDneO0fc6FDxZKzASb/sENgOEe04AItrdzEjPeKEIdsa35vmPtqlA==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-measure@8.6.14':
- resolution: {integrity: sha512-1Tlyb72NX8aAqm6I6OICsUuGOP6hgnXcuFlXucyhKomPa6j3Eu2vKu561t/f0oGtAK2nO93Z70kVaEh5X+vaGw==}
+ '@storybook/addon-measure@8.6.17':
+ resolution: {integrity: sha512-OFGmCrz9MTWfxa0t2GP+633VXZS7W3ahIM5bRmCTeG+jF/gdiVw3S2Adq7YiIZw+nROW9VtHBjuWGvy2miZxcQ==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-outline@8.6.14':
- resolution: {integrity: sha512-CW857JvN6OxGWElqjlzJO2S69DHf+xO3WsEfT5mT3ZtIjmsvRDukdWfDU9bIYUFyA2lFvYjncBGjbK+I91XR7w==}
+ '@storybook/addon-outline@8.6.17':
+ resolution: {integrity: sha512-UfFThgImS8cuv9Mts5JozGO2/SgjU61uKZxn7w+YpJQu0r2UAF56ZBBMZF/Ur5IC4HaoqDWF5DksMlFqMHPBsw==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-storysource@8.6.14':
- resolution: {integrity: sha512-/eDCNUHPdsVDF53B+Ebi9gHSNcRrA3puo1UCDio8wMN+jBMoWh6E5wSjXDsxWaOyp0Zwuq8XUx8AdgTlg/rcrw==}
+ '@storybook/addon-storysource@8.6.17':
+ resolution: {integrity: sha512-1OMB3FqphesU+aej/6Kj0x3NYiULRj5aIIfyWTexvjIgVqChTb7N7VEs7UxwHyXolFzUQS2ViSxCcvccwLLGbQ==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-toolbars@8.6.14':
- resolution: {integrity: sha512-W/wEXT8h3VyZTVfWK/84BAcjAxTdtRiAkT2KAN0nbSHxxB5KEM1MjKpKu2upyzzMa3EywITqbfy4dP6lpkVTwQ==}
+ '@storybook/addon-toolbars@8.6.17':
+ resolution: {integrity: sha512-LoWtnMVQJWivAu+SZdgYIsaiEqIq0mZ8Ses2xSwLnQZxndCakyqPYv/7YcdLDJaX5f8DpjPvSzJ77oa75oKgFw==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/addon-viewport@8.6.14':
- resolution: {integrity: sha512-gNzVQbMqRC+/4uQTPI2ZrWuRHGquTMZpdgB9DrD88VTEjNudP+J6r8myLfr2VvGksBbUMHkGHMXHuIhrBEnXYA==}
+ '@storybook/addon-viewport@8.6.17':
+ resolution: {integrity: sha512-gVUtbFQq/mpD5CFOqXGAu5hUpGm/t5G/psg3YsTjnaRBd18dJZ/+//HzZVbX9aEW8t5qTJeJcEYaF5463BVfNA==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/blocks@8.6.14':
- resolution: {integrity: sha512-rBMHAfA39AGHgkrDze4RmsnQTMw1ND5fGWobr9pDcJdnDKWQWNRD7Nrlxj0gFlN3n4D9lEZhWGdFrCbku7FVAQ==}
+ '@storybook/blocks@8.6.17':
+ resolution: {integrity: sha512-zuYHH+0egovMrjWRKwOtgVGbz6KALGowPSWBzQ8deTBu6IXfkz6Ce1hRLJPn5S6/jDqqr9xx8vuAiypnRQ98tA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^8.6.14
+ storybook: ^8.6.17
peerDependenciesMeta:
react:
optional: true
react-dom:
optional: true
- '@storybook/builder-vite@10.1.5':
- resolution: {integrity: sha512-5alpNa+TQXK1zp9MeovUK/yIUkZqpIFUScUer6cYgidI96Boovn7OXt5oXQ8CqqpzuEtgCvz44TzCmgZoGv41g==}
+ '@storybook/builder-vite@10.2.10':
+ resolution: {integrity: sha512-Wd6CYL7LvRRNiXMz977x9u/qMm7nmMw/7Dow2BybQo+Xbfy1KhVjIoZ/gOiG515zpojSozctNrJUbM0+jH1jwg==}
peerDependencies:
- storybook: ^10.1.5
+ storybook: ^10.2.10
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
- '@storybook/components@8.6.14':
- resolution: {integrity: sha512-HNR2mC5I4Z5ek8kTrVZlIY/B8gJGs5b3XdZPBPBopTIN6U/YHXiDyOjY3JlaS4fSG1fVhp/Qp1TpMn1w/9m1pw==}
+ '@storybook/components@8.6.17':
+ resolution: {integrity: sha512-0b8xkkuPCNbM8LTOzyfxuo2KdJCHIfu3+QxWBFllXap0eYNHwVeSxE5KERQ/bk2GDCiRzaUbwH9PeLorxOzJJQ==}
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/core-events@8.6.14':
- resolution: {integrity: sha512-RrJ95u3HuIE4Nk8VmZP0tc/u0vYoE2v9fYlMw6K2GUSExzKDITs3voy6WMIY7Q3qbQun8XUXVlmqkuFzTEy/pA==}
+ '@storybook/core-events@8.6.17':
+ resolution: {integrity: sha512-HiKVE2sSbJF6PVFt2DfJtLef1Mc35cN+sf2f8Ay2ibHy2gY1t3/7W1PhYVGt7UpJNOnVZfsmcE3yqGNojct3mw==}
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/csf-plugin@10.1.5':
- resolution: {integrity: sha512-v+D7PVRkNUHznfoQg8yqpLWZIIbPddqHDSi1oBGdegF0Kv/lVsGqTZGRLroApsMu7BLwLhpcMID6ofxlfftWKg==}
+ '@storybook/csf-plugin@10.2.10':
+ resolution: {integrity: sha512-aFvgaNDAnKMjuyhPK5ialT22pPqMN0XfPBNPeeNVPYztngkdKBa8WFqF/umDd47HxAjebq+vn6uId1xHyOHH3g==}
peerDependencies:
esbuild: '*'
rollup: '*'
- storybook: ^10.1.5
+ storybook: ^10.2.10
vite: '*'
webpack: '*'
peerDependenciesMeta:
@@ -4261,10 +4049,10 @@ packages:
webpack:
optional: true
- '@storybook/csf-plugin@8.6.14':
- resolution: {integrity: sha512-dErtc9teAuN+eelN8FojzFE635xlq9cNGGGEu0WEmMUQ4iJ8pingvBO1N8X3scz4Ry7KnxX++NNf3J3gpxS8qQ==}
+ '@storybook/csf-plugin@8.6.17':
+ resolution: {integrity: sha512-ouvF/izbKclZxpfnRUkyC5ZVDU7QA0cHhjQnXTDT4F8b0uciQUDw1LosDZy5MXf03BeIDdyBAtzd/ym3wzd+kw==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
'@storybook/global@5.0.0':
resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
@@ -4282,84 +4070,84 @@ packages:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- '@storybook/instrumenter@8.6.14':
- resolution: {integrity: sha512-iG4MlWCcz1L7Yu8AwgsnfVAmMbvyRSk700Mfy2g4c8y5O+Cv1ejshE1LBBsCwHgkuqU0H4R0qu4g23+6UnUemQ==}
+ '@storybook/instrumenter@8.6.17':
+ resolution: {integrity: sha512-uPqC0sPY2tYGkEVi1x+L4hvhkTwxT16B/LB8xIXh68co3gR8vY6wVskoBp2tM7LSUGl08U2ksZWxyTo1DaQY5Q==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/manager-api@8.6.14':
- resolution: {integrity: sha512-ez0Zihuy17udLbfHZQXkGqwtep0mSGgHcNzGN7iZrMP1m+VmNo+7aGCJJdvXi7+iU3yq8weXSQFWg5DqWgLS7g==}
+ '@storybook/manager-api@8.6.17':
+ resolution: {integrity: sha512-sPJytvClNrw5GgKcPletMTxDOAYcTRA8VRt9E+ncKvPSYHtPDqLfGTgWajXmt0hRsiBUN5bOgLS9bmNjNQWhrw==}
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/preview-api@8.6.14':
- resolution: {integrity: sha512-2GhcCd4dNMrnD7eooEfvbfL4I83qAqEyO0CO7JQAmIO6Rxb9BsOLLI/GD5HkvQB73ArTJ+PT50rfaO820IExOQ==}
+ '@storybook/preview-api@8.6.17':
+ resolution: {integrity: sha512-vpTCTkw11wXerYnlG5Q0y4SbFqG9O6GhR0hlYgCn3Z9kcHlNjK/xuwd3h4CvwNXxRNWZGT8qYYCLn5gSSrX6fA==}
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/react-dom-shim@10.1.5':
- resolution: {integrity: sha512-CsXcq26wINUgYP8KnfSuS60B10/Ag34YdcnWIEl9hM5UtTQ65WYJ9fVFqpzfnQrkpgRMd7iQjtmUhCe+4umnHg==}
+ '@storybook/react-dom-shim@10.2.10':
+ resolution: {integrity: sha512-TmBrhyLHn8B8rvDHKk5uW5BqzO1M1T+fqFNWg88NIAJOoyX4Uc90FIJjDuN1OJmWKGwB5vLmPwaKBYsTe1yS+w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.1.5
+ storybook: ^10.2.10
- '@storybook/react-dom-shim@8.6.14':
- resolution: {integrity: sha512-0hixr3dOy3f3M+HBofp3jtMQMS+sqzjKNgl7Arfuj3fvjmyXOks/yGjDImySR4imPtEllvPZfhiQNlejheaInw==}
+ '@storybook/react-dom-shim@8.6.17':
+ resolution: {integrity: sha512-bHLsR9b/tiwm9lXbN8kp9XlOgkRXeg84UFwXaWBPu3pOO7vRXukk23SQUpLW+HhjKtCJ3xClSi5uMpse5MpkVQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/react-vite@10.1.5':
- resolution: {integrity: sha512-27RiCVw5QZ/f9fXS8sGaPHuWkbHSoS66ifeakxHgbkbIXjVI4M6pWB7NUj49MwU1YUMOpB0T8KasvyMZzv/UPA==}
+ '@storybook/react-vite@10.2.10':
+ resolution: {integrity: sha512-C652GhZHXURi+gFqqLKmZPskEq1FQto4VCf/eQea2exmdVS0nOB+FFWQZNCivX6mpkDHza8UxRZNFpDB0mWcJQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.1.5
+ storybook: ^10.2.10
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
- '@storybook/react@10.1.5':
- resolution: {integrity: sha512-M8fR7WVs79fPJHwRZxkz4XzIfzs/bN0heWdZX0D4iRjeIcY4nLM/tyalCcQDrGgrSJbgAAf4xd7KXaZzaZSAqA==}
+ '@storybook/react@10.2.10':
+ resolution: {integrity: sha512-PcsChzPI8lhllB9exV7nFb96093i6sTwIl0jpPjaTFPQCRoueR9E/YeP3qSKQL9xt4cmii0cW7F0RUx25rW93Q==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- storybook: ^10.1.5
+ storybook: ^10.2.10
typescript: '>= 4.9.x'
peerDependenciesMeta:
typescript:
optional: true
- '@storybook/source-loader@8.6.14':
- resolution: {integrity: sha512-aFUqrkWh4XSXDmkrK0Nm8q4K94bfgnixFMmql8lBAFuEllFek9Rd3i2RwGOhLUtwzpM89f74nzEgR1kd/ijJ+g==}
+ '@storybook/source-loader@8.6.17':
+ resolution: {integrity: sha512-pjm+Pc6Jk7zcZTbvaua38en2hWQ9gwv9dqWqkuwSH8V63LjVzFKuZ4XvK/C8QAcRg/7aXJjib+FlR0Uq9TicSg==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/test@8.6.14':
- resolution: {integrity: sha512-GkPNBbbZmz+XRdrhMtkxPotCLOQ1BaGNp/gFZYdGDk2KmUWBKmvc5JxxOhtoXM2703IzNFlQHSSNnhrDZYuLlw==}
+ '@storybook/test@8.6.17':
+ resolution: {integrity: sha512-VTuCylXGQrFDZXqZ29+yvJ+A4TZ69jG72rLjiic8hI0SOt87AC/8X1NaYvd2NS4TY0G0PwqtxmKeig8qRDrhNg==}
peerDependencies:
- storybook: ^8.6.14
+ storybook: ^8.6.17
- '@storybook/theming@8.6.14':
- resolution: {integrity: sha512-r4y+LsiB37V5hzpQo+BM10PaCsp7YlZ0YcZzQP1OCkPlYXmUAFy2VvDKaFRpD8IeNPKug2u4iFm/laDEbs03dg==}
+ '@storybook/theming@8.6.17':
+ resolution: {integrity: sha512-IttFvRqozpuzN5MlQEWGOzUA2rZg86688Dyv1d+bjpYcFHtY1X4XyTCGwv1BPTaTsB959oM8R2yoNYWQkABbBA==}
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/types@8.6.14':
- resolution: {integrity: sha512-33kzHZa7h6/EygeLZDcm1PNRTlybokz8dzAh2JYjpETf77pG8jhPmEfrI2oHSAdgNeK7A3OMcGA/EwEN7EJdzw==}
+ '@storybook/types@8.6.17':
+ resolution: {integrity: sha512-nNlbGulqqk2XzroD5jxcsw+13nWlGxBD9XRAht4Peh71Z4OcdBJCnc9UmHkTgUakmRJ/T6p7xabURDG3KpayIQ==}
peerDependencies:
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
- '@storybook/vue3-vite@10.1.5':
- resolution: {integrity: sha512-ExwJyE4bkkdCUxlGoqGK9VRfJwvAF9iYOurtryMqMHBSr55u2id3fzD/PmjraeedWpElxbm2ZeIzBNnATdsSEQ==}
+ '@storybook/vue3-vite@10.2.10':
+ resolution: {integrity: sha512-r2mmbY2Cskk/pN56OTVT0cu9Ocn+h8/sq8M2C0NhViJWQeoANTMCCScR1a2GxFuYs1bX8YMWnwR6wghgvGJLBw==}
peerDependencies:
- storybook: ^10.1.5
+ storybook: ^10.2.10
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
- '@storybook/vue3@10.1.5':
- resolution: {integrity: sha512-C0QIE4nLoGWHdfKXl6pHyThWc/OPt8DqnlUF8yMMDHCjPGt1q9RQZcYINtVleZ5WhAck4Q2ZvXys372ridf0wA==}
+ '@storybook/vue3@10.2.10':
+ resolution: {integrity: sha512-hp9d11+Nz2hQCbAoggVJYDuNIguEUwaeuBmYjX84vSUI2jafYHAo9ir3xE2jdWwQsbvdNIUc0puFKD0S3WR0TA==}
peerDependencies:
- storybook: ^10.1.5
+ storybook: ^10.2.10
vue: ^3.0.0
'@stylistic/eslint-plugin@5.5.0':
@@ -4368,9 +4156,9 @@ packages:
peerDependencies:
eslint: '>=9.0.0'
- '@swc/cli@0.7.9':
- resolution: {integrity: sha512-AFQu3ZZ9IcdClTknxbug08S9ed/q8F3aYkO5NoZ+6IjQ5UEo1s2HN1GRKNvUslYx2EoVYxd+6xGcp6C7wwtxyQ==}
- engines: {node: '>= 16.14.0'}
+ '@swc/cli@0.8.0':
+ resolution: {integrity: sha512-vzUkYzlqLe9dC+B0ZIH62CzfSZOCTjIsmquYyyyi45JCm1xmRfLDKeEeMrEPPyTWnEEN84e4iVd49Tgqa+2GaA==}
+ engines: {node: '>= 20.19.0'}
hasBin: true
peerDependencies:
'@swc/core': ^1.2.66
@@ -4385,14 +4173,14 @@ packages:
cpu: [arm64]
os: [android]
- '@swc/core-darwin-arm64@1.15.3':
- resolution: {integrity: sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ==}
+ '@swc/core-darwin-arm64@1.15.11':
+ resolution: {integrity: sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==}
engines: {node: '>=10'}
cpu: [arm64]
os: [darwin]
- '@swc/core-darwin-x64@1.15.3':
- resolution: {integrity: sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A==}
+ '@swc/core-darwin-x64@1.15.11':
+ resolution: {integrity: sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==}
engines: {node: '>=10'}
cpu: [x64]
os: [darwin]
@@ -4403,60 +4191,60 @@ packages:
cpu: [x64]
os: [freebsd]
- '@swc/core-linux-arm-gnueabihf@1.15.3':
- resolution: {integrity: sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg==}
+ '@swc/core-linux-arm-gnueabihf@1.15.11':
+ resolution: {integrity: sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==}
engines: {node: '>=10'}
cpu: [arm]
os: [linux]
- '@swc/core-linux-arm64-gnu@1.15.3':
- resolution: {integrity: sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw==}
+ '@swc/core-linux-arm64-gnu@1.15.11':
+ resolution: {integrity: sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@swc/core-linux-arm64-musl@1.15.3':
- resolution: {integrity: sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g==}
+ '@swc/core-linux-arm64-musl@1.15.11':
+ resolution: {integrity: sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@swc/core-linux-x64-gnu@1.15.3':
- resolution: {integrity: sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A==}
+ '@swc/core-linux-x64-gnu@1.15.11':
+ resolution: {integrity: sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@swc/core-linux-x64-musl@1.15.3':
- resolution: {integrity: sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug==}
+ '@swc/core-linux-x64-musl@1.15.11':
+ resolution: {integrity: sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
libc: [musl]
- '@swc/core-win32-arm64-msvc@1.15.3':
- resolution: {integrity: sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA==}
+ '@swc/core-win32-arm64-msvc@1.15.11':
+ resolution: {integrity: sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==}
engines: {node: '>=10'}
cpu: [arm64]
os: [win32]
- '@swc/core-win32-ia32-msvc@1.15.3':
- resolution: {integrity: sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw==}
+ '@swc/core-win32-ia32-msvc@1.15.11':
+ resolution: {integrity: sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==}
engines: {node: '>=10'}
cpu: [ia32]
os: [win32]
- '@swc/core-win32-x64-msvc@1.15.3':
- resolution: {integrity: sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog==}
+ '@swc/core-win32-x64-msvc@1.15.11':
+ resolution: {integrity: sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==}
engines: {node: '>=10'}
cpu: [x64]
os: [win32]
- '@swc/core@1.15.3':
- resolution: {integrity: sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==}
+ '@swc/core@1.15.11':
+ resolution: {integrity: sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==}
engines: {node: '>=10'}
peerDependencies:
'@swc/helpers': '>=0.5.17'
@@ -4482,8 +4270,8 @@ packages:
'@syuilo/aiscript@0.19.0':
resolution: {integrity: sha512-ZWG4s1m6RrFjE7NeIMaxFz769YO1jW5ReTrOROrEO4IHheOrjxxJ/Ffe2TUNqX9/XxDloMwfWplKhfSzx8LGMA==}
- '@syuilo/aiscript@1.2.0':
- resolution: {integrity: sha512-3MkKfqDftqHyouINgHEIHnoTVWEeL0L1o7Y7Y9d8qb5h5vZKphzH/KVG3R2iI6CHtmO1mWHHJx5A499f80H9FA==}
+ '@syuilo/aiscript@1.2.1':
+ resolution: {integrity: sha512-jTW4dFGBJVNfq3kn0+tbtQqcr71sUILZpAs8tV2ZC/zO4y7LsG8hSbIQ9QfRWWWdnNjr5R6EoEoOsC1hGqxu0A==}
'@szmarczak/http-timer@5.0.1':
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
@@ -4577,10 +4365,6 @@ packages:
resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==}
engines: {node: '>=18'}
- '@tokenizer/inflate@0.3.1':
- resolution: {integrity: sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==}
- engines: {node: '>=18'}
-
'@tokenizer/inflate@0.4.1':
resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==}
engines: {node: '>=18'}
@@ -4588,10 +4372,6 @@ packages:
'@tokenizer/token@0.3.0':
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
- '@tootallnate/once@2.0.0':
- resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
- engines: {node: '>= 10'}
-
'@tsd/typescript@5.9.3':
resolution: {integrity: sha512-JSSdNiS0wgd8GHhBwnMAI18Y8XPhLVN+dNelPfZCXFhy9Lb3NbnFyp9JKxxr54jSUkEJPk3cidvCoHducSaRMQ==}
engines: {node: '>=14.17'}
@@ -4704,6 +4484,9 @@ packages:
'@types/http-link-header@1.0.7':
resolution: {integrity: sha512-snm5oLckop0K3cTDAiBnZDy6ncx9DJ3mCRDvs42C884MbVYPP74Tiq2hFsSDRTyjK6RyDYDIulPiW23ge+g5Lw==}
+ '@types/insert-text-at-cursor@0.3.2':
+ resolution: {integrity: sha512-S80ZeMGJ1YMwF/CwvvGNCoWS0klVpJmjtyEL1kbYgYXRMPMEfNWXXnnnLQaim3FNp6tJzOhy/yeQXInfC6o1sA==}
+
'@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
@@ -4719,9 +4502,6 @@ packages:
'@types/js-yaml@4.0.9':
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
- '@types/jsdom@20.0.1':
- resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==}
-
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@@ -4767,14 +4547,11 @@ packages:
'@types/node-fetch@2.6.13':
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
- '@types/node@20.19.25':
- resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==}
-
- '@types/node@24.10.2':
- resolution: {integrity: sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==}
+ '@types/node@24.10.13':
+ resolution: {integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==}
- '@types/nodemailer@7.0.4':
- resolution: {integrity: sha512-ee8fxWqOchH+Hv6MDDNNy028kwvVnLplrStm4Zf/3uHWw5zzo8FoYYeffpJtGs2wWysEumMH0ZIdMGMY1eMAow==}
+ '@types/nodemailer@7.0.11':
+ resolution: {integrity: sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==}
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
@@ -4791,12 +4568,15 @@ packages:
'@types/offscreencanvas@2019.7.3':
resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==}
- '@types/pg-pool@2.0.6':
- resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==}
+ '@types/pg-pool@2.0.7':
+ resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==}
'@types/pg@8.15.6':
resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==}
+ '@types/pg@8.16.0':
+ resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==}
+
'@types/punycode@2.1.4':
resolution: {integrity: sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ==}
@@ -4821,9 +4601,6 @@ packages:
'@types/readdir-glob@1.1.5':
resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==}
- '@types/redis-info@3.0.3':
- resolution: {integrity: sha512-VIkNy6JbYI/RLdbPHdm9JQvv6RVld2uE2/6Hdid38Qdq+zvDli2FTpImI8pC5zwp8xS8qVqfzlfyAub8xZEd5g==}
-
'@types/rename@1.0.7':
resolution: {integrity: sha512-E9qapfghUGfBMi3jNhsmCKPIp3f2zvNKpaX1BDGLGJNjzpgsZ/RTx7NaNksFjGoJ+r9NvWF1NSM5vVecnNjVmw==}
@@ -4884,6 +4661,9 @@ packages:
'@types/tedious@4.0.14':
resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==}
+ '@types/textarea-caret@3.0.4':
+ resolution: {integrity: sha512-epJGYB37/sNrTDbhfyRjHkXsQSAcO6zby0JBDS0QMt6HQ1f1W2E4YpSc7TQkNmWaWmYXv92zOIfN5PHA8CmThg==}
+
'@types/throttle-debounce@5.0.2':
resolution: {integrity: sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==}
@@ -4893,9 +4673,6 @@ packages:
'@types/tmp@0.2.6':
resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==}
- '@types/tough-cookie@4.0.5':
- resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
-
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
@@ -4926,80 +4703,119 @@ packages:
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
- '@typescript-eslint/eslint-plugin@8.49.0':
- resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==}
+ '@typescript-eslint/eslint-plugin@8.56.0':
+ resolution: {integrity: sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.49.0
- eslint: ^8.57.0 || ^9.0.0
+ '@typescript-eslint/parser': ^8.56.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/parser@8.49.0':
- resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==}
+ '@typescript-eslint/parser@8.56.0':
+ resolution: {integrity: sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/project-service@8.49.0':
- resolution: {integrity: sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==}
+ '@typescript-eslint/project-service@8.56.0':
+ resolution: {integrity: sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/scope-manager@8.49.0':
- resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==}
+ '@typescript-eslint/scope-manager@8.56.0':
+ resolution: {integrity: sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/tsconfig-utils@8.49.0':
- resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==}
+ '@typescript-eslint/tsconfig-utils@8.56.0':
+ resolution: {integrity: sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/type-utils@8.49.0':
- resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==}
+ '@typescript-eslint/type-utils@8.56.0':
+ resolution: {integrity: sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/types@8.49.0':
- resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==}
+ '@typescript-eslint/types@8.56.0':
+ resolution: {integrity: sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.49.0':
- resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==}
+ '@typescript-eslint/typescript-estree@8.56.0':
+ resolution: {integrity: sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/utils@8.49.0':
- resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==}
+ '@typescript-eslint/utils@8.56.0':
+ resolution: {integrity: sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0'
- '@typescript-eslint/visitor-keys@8.49.0':
- resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==}
+ '@typescript-eslint/visitor-keys@8.56.0':
+ resolution: {integrity: sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-+HoFBoTy6GrdqpN2sBCmvNNVqKXndVqgKPojaTpc1OfiH2YAa1p2zXsHuw2+PtXcsGDBvIX4Fce4i8urHlF6mQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@typescript/native-preview-darwin-x64@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-cKSm37T0rrCskKKjwr/i97+k9fIQydsOfYO8oUzOFWEHKYl83vQib0JdSyelbLGEfT5Xpt6Wm12ul6MS42e+xw==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@typescript/native-preview-linux-arm64@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-mF+y2V95kYJx6ELk3BaXd0lDRGq3ce8fCDxx3MRgCEuexm/SYXC9OnQfkdyEPqU366sYwYw98TFIYjf6ruhZ7Q==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@typescript/native-preview-linux-arm@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-fCgBsrrbURMutyBdnYXa13OVDOGtlu1AWMCqq21jQFDv2hglUR2l7BOZmqobLgWY76DGHQS7zK+hXcEcOqR7hA==}
+ cpu: [arm]
+ os: [linux]
+
+ '@typescript/native-preview-linux-x64@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-7lxKOVF0ph2+92ySc8HSUxKnBHkfoj7bm43QE482fb0z1YhNuUc/3R79NJGKywgmDP5IXEb1PY8piQbwoPYHwg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@typescript/native-preview-win32-arm64@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-VzS2LDsXqmC+6v+DEdjHxhadKzN5l/yMMQp3pVvq/ZM52NkUWzUtBpAU3nxB8n02L9Xr6625jEMduyZba0TVVg==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@typescript/native-preview-win32-x64@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-OUmQLOIoknUmwaNz2iA0tkClIbbuzjHLrnFZNF4rlw1LlelNExGQlrMmx0szMRjTz9CFOh9qhYT23/3jOoOsqA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@typescript/native-preview@7.0.0-dev.20260116.1':
+ resolution: {integrity: sha512-9N2Sa3Ap8E5+V4K+eTs5y8//dW77qFKzo+OfmYpL4f/Vb6WEoo6SDhjaSB2eNtgeiCKkNQySLO3pz3/QEpuVXg==}
+ hasBin: true
+
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
- '@vitejs/plugin-vue@6.0.2':
- resolution: {integrity: sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==}
+ '@vitejs/plugin-vue@6.0.4':
+ resolution: {integrity: sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==}
engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies:
- vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
vue: ^3.2.25
- '@vitest/coverage-v8@4.0.15':
- resolution: {integrity: sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==}
+ '@vitest/coverage-v8@4.0.18':
+ resolution: {integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==}
peerDependencies:
- '@vitest/browser': 4.0.15
- vitest: 4.0.15
+ '@vitest/browser': 4.0.18
+ vitest: 4.0.18
peerDependenciesMeta:
'@vitest/browser':
optional: true
@@ -5010,22 +4826,11 @@ packages:
'@vitest/expect@3.2.4':
resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==}
- '@vitest/expect@4.0.15':
- resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==}
+ '@vitest/expect@4.0.18':
+ resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==}
- '@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
- peerDependenciesMeta:
- msw:
- optional: true
- vite:
- optional: true
-
- '@vitest/mocker@4.0.15':
- resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==}
+ '@vitest/mocker@4.0.18':
+ resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==}
peerDependencies:
msw: ^2.4.9
vite: ^6.0.0 || ^7.0.0-0
@@ -5044,14 +4849,14 @@ packages:
'@vitest/pretty-format@3.2.4':
resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==}
- '@vitest/pretty-format@4.0.15':
- resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==}
+ '@vitest/pretty-format@4.0.18':
+ resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==}
- '@vitest/runner@4.0.15':
- resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==}
+ '@vitest/runner@4.0.18':
+ resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==}
- '@vitest/snapshot@4.0.15':
- resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==}
+ '@vitest/snapshot@4.0.18':
+ resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==}
'@vitest/spy@2.0.5':
resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==}
@@ -5059,8 +4864,8 @@ packages:
'@vitest/spy@3.2.4':
resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==}
- '@vitest/spy@4.0.15':
- resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==}
+ '@vitest/spy@4.0.18':
+ resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==}
'@vitest/utils@2.0.5':
resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==}
@@ -5071,38 +4876,38 @@ packages:
'@vitest/utils@3.2.4':
resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==}
- '@vitest/utils@4.0.15':
- resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==}
+ '@vitest/utils@4.0.18':
+ resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==}
'@volar/language-core@2.4.15':
resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
- '@volar/language-core@2.4.26':
- resolution: {integrity: sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A==}
+ '@volar/language-core@2.4.27':
+ resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==}
'@volar/source-map@2.4.15':
resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
- '@volar/source-map@2.4.26':
- resolution: {integrity: sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw==}
+ '@volar/source-map@2.4.27':
+ resolution: {integrity: sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==}
'@volar/typescript@2.4.15':
resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
- '@volar/typescript@2.4.26':
- resolution: {integrity: sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA==}
+ '@volar/typescript@2.4.27':
+ resolution: {integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==}
- '@vue/compiler-core@3.5.25':
- resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==}
+ '@vue/compiler-core@3.5.28':
+ resolution: {integrity: sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ==}
- '@vue/compiler-dom@3.5.25':
- resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==}
+ '@vue/compiler-dom@3.5.28':
+ resolution: {integrity: sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA==}
- '@vue/compiler-sfc@3.5.25':
- resolution: {integrity: sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==}
+ '@vue/compiler-sfc@3.5.28':
+ resolution: {integrity: sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g==}
- '@vue/compiler-ssr@3.5.25':
- resolution: {integrity: sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==}
+ '@vue/compiler-ssr@3.5.28':
+ resolution: {integrity: sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g==}
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
@@ -5115,30 +4920,25 @@ packages:
typescript:
optional: true
- '@vue/language-core@3.1.8':
- resolution: {integrity: sha512-PfwAW7BLopqaJbneChNL6cUOTL3GL+0l8paYP5shhgY5toBNidWnMXWM+qDwL7MC9+zDtzCF2enT8r6VPu64iw==}
- peerDependencies:
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
+ '@vue/language-core@3.2.4':
+ resolution: {integrity: sha512-bqBGuSG4KZM45KKTXzGtoCl9cWju5jsaBKaJJe3h5hRAAWpZUuj5G+L+eI01sPIkm4H6setKRlw7E85wLdDNew==}
- '@vue/reactivity@3.5.25':
- resolution: {integrity: sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==}
+ '@vue/reactivity@3.5.28':
+ resolution: {integrity: sha512-gr5hEsxvn+RNyu9/9o1WtdYdwDjg5FgjUSBEkZWqgTKlo/fvwZ2+8W6AfKsc9YN2k/+iHYdS9vZYAhpi10kNaw==}
- '@vue/runtime-core@3.5.25':
- resolution: {integrity: sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==}
+ '@vue/runtime-core@3.5.28':
+ resolution: {integrity: sha512-POVHTdbgnrBBIpnbYU4y7pOMNlPn2QVxVzkvEA2pEgvzbelQq4ZOUxbp2oiyo+BOtiYlm8Q44wShHJoBvDPAjQ==}
- '@vue/runtime-dom@3.5.25':
- resolution: {integrity: sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==}
+ '@vue/runtime-dom@3.5.28':
+ resolution: {integrity: sha512-4SXxSF8SXYMuhAIkT+eBRqOkWEfPu6nhccrzrkioA6l0boiq7sp18HCOov9qWJA5HML61kW8p/cB4MmBiG9dSA==}
- '@vue/server-renderer@3.5.25':
- resolution: {integrity: sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==}
+ '@vue/server-renderer@3.5.28':
+ resolution: {integrity: sha512-pf+5ECKGj8fX95bNincbzJ6yp6nyzuLDhYZCeFxUNp8EBrQpPpQaLX3nNCp49+UbgbPun3CeVE+5CXVV1Xydfg==}
peerDependencies:
- vue: 3.5.25
+ vue: 3.5.28
- '@vue/shared@3.5.25':
- resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==}
+ '@vue/shared@3.5.28':
+ resolution: {integrity: sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ==}
'@vue/test-utils@2.4.6':
resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
@@ -5190,10 +4990,6 @@ packages:
resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==}
engines: {node: '>=14.6'}
- abab@2.0.6:
- resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
- deprecated: Use your platform's native atob() and btoa() methods instead
-
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
@@ -5201,9 +4997,9 @@ packages:
resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
- abbrev@3.0.1:
- resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ abbrev@4.0.0:
+ resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==}
+ engines: {node: ^20.17.0 || >=22.9.0}
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
@@ -5220,9 +5016,6 @@ packages:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
- acorn-globals@7.0.1:
- resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==}
-
acorn-import-attributes@1.9.5:
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
peerDependencies:
@@ -5233,17 +5026,13 @@ packages:
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
- acorn-walk@8.3.4:
- resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
- engines: {node: '>=0.4.0'}
-
acorn@7.4.1:
resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
engines: {node: '>=0.4.0'}
hasBin: true
- acorn@8.15.0:
- resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -5297,14 +5086,14 @@ packages:
ajv@8.13.0:
resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
- ajv@8.17.1:
- resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
+ ajv@8.18.0:
+ resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
alien-signals@1.0.13:
resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
- alien-signals@3.1.0:
- resolution: {integrity: sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==}
+ alien-signals@3.1.2:
+ resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
analytics-utils@1.1.1:
resolution: {integrity: sha512-nRybjTpRAcHVhWb1cvYaOLJaI3R79r8XjMbu5c0wd2jKmANNqSrYwybiU0X3mp+CQQdm4YiAggTXb2cIA8XhUg==}
@@ -5461,8 +5250,8 @@ packages:
resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
engines: {node: '>=4'}
- ast-v8-to-istanbul@0.3.8:
- resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==}
+ ast-v8-to-istanbul@0.3.10:
+ resolution: {integrity: sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==}
astral-regex@2.0.0:
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
@@ -5561,6 +5350,10 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ balanced-match@4.0.2:
+ resolution: {integrity: sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==}
+ engines: {node: 20 || >=22}
+
bare-events@2.8.1:
resolution: {integrity: sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==}
peerDependencies:
@@ -5610,8 +5403,8 @@ packages:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
- body-parser@2.2.1:
- resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==}
+ body-parser@2.2.2:
+ resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
engines: {node: '>=18'}
boolbase@1.0.0:
@@ -5626,12 +5419,16 @@ packages:
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+ brace-expansion@5.0.2:
+ resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==}
+ engines: {node: 20 || >=22}
+
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
- broadcast-channel@7.2.0:
- resolution: {integrity: sha512-JgraikEriG/TxBUi2W/w2O0jhHjXZUtXAvCZH0Yr3whjxYVgAg0hSe6r/teM+I5H5Q/q6RhyuKdC2pHNlFyepQ==}
+ broadcast-channel@7.3.0:
+ resolution: {integrity: sha512-UHPhLBQKfQ8OmMFMpmPfO5dRakyA1vsfiDGWTYNvChYol65tbuhivPEGgZZiuetorvExdvxaWiBy/ym1Ty08yA==}
browserslist@4.28.0:
resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==}
@@ -5663,12 +5460,16 @@ packages:
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
- bufferutil@4.0.9:
- resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==}
+ bufferutil@4.1.0:
+ resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==}
engines: {node: '>=6.14.2'}
- bullmq@5.65.1:
- resolution: {integrity: sha512-QgDAzX1G9L5IRy4Orva5CfQTXZT+5K+OfO/kbPrAqN+pmL9LJekCzxijXehlm/u2eXfWPfWvIdJJIqiuz3WJSg==}
+ bullmq@5.69.4:
+ resolution: {integrity: sha512-Lp7ymp875I/rtjMm6oxzQ3PrvDDHkgge0oaAznmZsKtGyglfdrg9zbidPSszTXgWFkS2rCgMcTRNJfM3uUMOjQ==}
+
+ bundle-name@4.1.0:
+ resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+ engines: {node: '>=18'}
buraha@0.0.1:
resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==}
@@ -5685,9 +5486,9 @@ packages:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
- cacache@19.0.1:
- resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ cacache@20.0.3:
+ resolution: {integrity: sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==}
+ engines: {node: ^20.17.0 || >=22.9.0}
cacheable-lookup@7.0.0:
resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
@@ -5855,8 +5656,8 @@ packages:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
- chromatic@13.3.4:
- resolution: {integrity: sha512-TR5rvyH0ESXobBB3bV8jc87AEAFQC7/n+Eb4XWhJz6hW3YNxIQPVjcbgLv+a4oKHEl1dUBueWSoIQsOVGTd+RQ==}
+ chromatic@15.1.1:
+ resolution: {integrity: sha512-T42eLy1fuckVIzZAoryYjxOEFXgOC9BUi+oT0VjmCz9PaJ/tvItTagIxIxA65vgrtCymXKSZDrGe7Hg2y7iAMA==}
hasBin: true
peerDependencies:
'@chromatic-com/cypress': ^0.*.* || ^1.0.0
@@ -5878,6 +5679,9 @@ packages:
cjs-module-lexer@1.4.3:
resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
+ cjs-module-lexer@2.2.0:
+ resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==}
+
clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
@@ -6072,8 +5876,8 @@ packages:
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
- cors@2.8.5:
- resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+ cors@2.8.6:
+ resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
engines: {node: '>= 0.10'}
crc-32@1.2.2:
@@ -6157,25 +5961,15 @@ packages:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
- cssom@0.3.8:
- resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
-
- cssom@0.5.0:
- resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==}
-
- cssstyle@2.3.0:
- resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
- engines: {node: '>=8'}
-
- cssstyle@5.3.3:
- resolution: {integrity: sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==}
+ cssstyle@5.3.7:
+ resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==}
engines: {node: '>=20'}
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
- cypress@15.7.1:
- resolution: {integrity: sha512-U3sYnJ+Cnpgr6IPycxsznTg//mGVXfPGeGV+om7VQCyp5XyVkhG4oPr3X3hTq1+OB0Om0O5DxusYmt7cbvwqMQ==}
+ cypress@15.10.0:
+ resolution: {integrity: sha512-OtUh7OMrfEjKoXydlAD1CfG2BvKxIqgWGY4/RMjrqQ3BKGBo5JFKoYNH+Tpcj4xKxWH4XK0Xri+9y8WkxhYbqQ==}
engines: {node: ^20.1.0 || ^22.0.0 || >=24.0.0}
hasBin: true
@@ -6187,12 +5981,8 @@ packages:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
- data-urls@3.0.2:
- resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
- engines: {node: '>=12'}
-
- data-urls@6.0.0:
- resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
+ data-urls@6.0.1:
+ resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==}
engines: {node: '>=20'}
data-view-buffer@1.0.2:
@@ -6297,6 +6087,14 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
+ default-browser-id@5.0.1:
+ resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==}
+ engines: {node: '>=18'}
+
+ default-browser@5.4.0:
+ resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==}
+ engines: {node: '>=18'}
+
defaults@2.0.2:
resolution: {integrity: sha512-cuIw0PImdp76AOfgkjbW4VhQODRmNNcKR73vdCH5cLd/ifj7aamfoXvYgfGkEAjNJZ3ozMIy9Gu2LutUkGEPbA==}
engines: {node: '>=16'}
@@ -6309,6 +6107,10 @@ packages:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
+ define-lazy-prop@3.0.0:
+ resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
+ engines: {node: '>=12'}
+
define-properties@1.2.1:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
@@ -6400,35 +6202,16 @@ packages:
dom-accessibility-api@0.6.3:
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
- dom-serializer@1.4.1:
- resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==}
-
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
- domexception@4.0.0:
- resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==}
- engines: {node: '>=12'}
- deprecated: Use your platform's native DOMException instead
-
- domhandler@3.3.0:
- resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==}
- engines: {node: '>= 4'}
-
- domhandler@4.3.1:
- resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==}
- engines: {node: '>= 4'}
-
domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
- domutils@2.8.0:
- resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
-
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
@@ -6512,8 +6295,8 @@ packages:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
- entities@7.0.0:
- resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==}
+ entities@7.0.1:
+ resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
env-paths@2.2.1:
@@ -6569,13 +6352,11 @@ packages:
es6-promisify@5.0.0:
resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==}
- esbuild@0.25.11:
- resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==}
- engines: {node: '>=18'}
- hasBin: true
+ esbuild-plugin-swc@1.0.1:
+ resolution: {integrity: sha512-K/basZARuDSHH7Krr7FdzwZF4WRLTcZa4c0R2FPuXCyYdh2nZMDdi6SYuuDv0MzKQY+jm0Afl4gltLsBzPesiQ==}
- esbuild@0.27.1:
- resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==}
+ esbuild@0.27.3:
+ resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
engines: {node: '>=18'}
hasBin: true
@@ -6609,11 +6390,6 @@ packages:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
- escodegen@2.1.0:
- resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
- engines: {node: '>=6.0'}
- hasBin: true
-
eslint-formatter-pretty@4.1.0:
resolution: {integrity: sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==}
engines: {node: '>=10'}
@@ -6652,13 +6428,13 @@ packages:
'@typescript-eslint/parser':
optional: true
- eslint-plugin-vue@10.6.2:
- resolution: {integrity: sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==}
+ eslint-plugin-vue@10.8.0:
+ resolution: {integrity: sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
'@typescript-eslint/parser': ^7.0.0 || ^8.0.0
- eslint: ^8.57.0 || ^9.0.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
vue-eslint-parser: ^10.0.0
peerDependenciesMeta:
'@stylistic/eslint-plugin':
@@ -6681,8 +6457,12 @@ packages:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint@9.39.1:
- resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==}
+ eslint-visitor-keys@5.0.1:
+ resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint@9.39.3:
+ resolution: {integrity: sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -6742,8 +6522,8 @@ packages:
eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
- eventemitter3@5.0.1:
- resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+ eventemitter3@5.0.4:
+ resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
events-universal@1.0.1:
resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==}
@@ -6772,8 +6552,8 @@ packages:
resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==}
engines: {node: '>=4'}
- exifreader@4.33.1:
- resolution: {integrity: sha512-KsVc4bRfZW255PSst5Opt5jUeLp+SD2+q6fmXQkMMkphpFCDBFjzNAvswgQa1YcMrXq+9Na6HJ6gS3wo2x7RRw==}
+ exifreader@4.36.2:
+ resolution: {integrity: sha512-Rpboqge86aBhRVJeW70BZHIkFNCi6rVlidzKuDyRxsreS/SbT983wFk/88+ddJu7zSrOae5fuiyXO7X91qq88Q==}
exit@0.1.2:
resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
@@ -6794,8 +6574,8 @@ packages:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
- express@5.1.0:
- resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==}
+ express@5.2.1:
+ resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
engines: {node: '>= 18'}
ext-list@2.2.2:
@@ -6852,8 +6632,8 @@ packages:
fast-uri@3.1.0:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
- fast-xml-parser@5.2.5:
- resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==}
+ fast-xml-parser@5.3.6:
+ resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==}
hasBin: true
fastify-plugin@5.1.0:
@@ -6863,11 +6643,11 @@ packages:
resolution: {integrity: sha512-2qfoaQ3BQDhZ1gtbkKZd6n0kKxJISJGM6u/skD9ljdWItAscjXrtZ1lnjr7PavmXX9j4EyCPmBDiIsLn07d5vA==}
engines: {node: '>= 10'}
- fastify@5.6.2:
- resolution: {integrity: sha512-dPugdGnsvYkBlENLhCgX8yhyGCsCPrpA8lFWbTNU428l+YOnLgYHR69hzV8HWPC79n536EqzqQtvhtdaCE0dKg==}
+ fastify@5.7.4:
+ resolution: {integrity: sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA==}
- fastq@1.19.1:
- resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+ fastq@1.20.1:
+ resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
fb-watchman@2.0.2:
resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
@@ -6884,8 +6664,8 @@ packages:
picomatch:
optional: true
- feed@5.1.0:
- resolution: {integrity: sha512-qGNhgYygnefSkAHHrNHqC7p3R8J0/xQDS/cYUud8er/qD9EFGWyCdUDfULHTJQN1d3H3WprzVwMc9MfB4J50Wg==}
+ feed@5.2.0:
+ resolution: {integrity: sha512-hgH6CCb+7+0c8PBlakI2KubG6R+Rb1MhpNcdvqUXZTBwBHf32piwY255diAkAmkGZ6AWlywOU88AkOgP9q8Rdw==}
engines: {node: '>=20', pnpm: '>=10'}
fetch-blob@3.2.0:
@@ -6911,12 +6691,8 @@ packages:
resolution: {integrity: sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==}
engines: {node: '>=18'}
- file-type@21.1.0:
- resolution: {integrity: sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==}
- engines: {node: '>=20'}
-
- file-type@21.1.1:
- resolution: {integrity: sha512-ifJXo8zUqbQ/bLbl9sFoqHNTNWbnPY1COImFfM6CCy7z+E+jC1eY9YfOKkx0fckIg+VljAy2/87T61fp0+eEkg==}
+ file-type@21.3.0:
+ resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==}
engines: {node: '>=20'}
filename-reserved-regex@3.0.0:
@@ -6955,8 +6731,8 @@ packages:
resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==}
engines: {node: '>=12'}
- fkill@10.0.1:
- resolution: {integrity: sha512-ecaskrAMoRXdtrGEQI/NyEJ3ZoZrMUVPb3EPNMrSqP/Tb08ZN22KqMJN24cDgNJ+ddkQR8W6cJRMvF9+nnZg6A==}
+ fkill@10.0.3:
+ resolution: {integrity: sha512-E0zxFLM/drPziQ8UXxbgD2L1N3oSllgdNJjDOdUNx3g5l9vdouPZiF9up1av27sA9taZmS5VowatuVDNHAUymg==}
engines: {node: '>=20'}
flat-cache@4.0.1:
@@ -7132,16 +6908,16 @@ packages:
glob@10.5.0:
resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
- glob@11.1.0:
- resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==}
+ glob@13.0.1:
+ resolution: {integrity: sha512-B7U/vJpE3DkJ5WXTgTpTRN63uV42DseiXXKMwG14LQBXmsdeIoHAPbU/MEo6II0k5ED74uc2ZGTC6MwHFQhF6w==}
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
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-dirs@3.0.1:
resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
@@ -7151,8 +6927,8 @@ packages:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
- globals@16.5.0:
- resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==}
+ globals@17.3.0:
+ resolution: {integrity: sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==}
engines: {node: '>=18'}
globalthis@1.0.4:
@@ -7174,8 +6950,8 @@ packages:
resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==}
engines: {node: '>=16'}
- got@14.6.5:
- resolution: {integrity: sha512-Su87c0NNeg97de1sO02gy9I8EmE7DCJ1gzcFLcgGpYeq2PnLg4xz73MWrp6HjqbSsjb6Glf4UBDW6JNyZA6uSg==}
+ got@14.6.6:
+ resolution: {integrity: sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==}
engines: {node: '>=20'}
graceful-fs@4.2.11:
@@ -7189,16 +6965,16 @@ packages:
resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
engines: {node: '>=0.8.0'}
- happy-dom@20.0.11:
- resolution: {integrity: sha512-QsCdAUHAmiDeKeaNojb1OHOPF7NjcWPBR7obdu3NwH2a/oyQaLg5d0aaCy/9My6CdPChYF07dvz5chaXBGaD4g==}
+ happy-dom@20.7.0:
+ resolution: {integrity: sha512-hR/uLYQdngTyEfxnOoa+e6KTcfBFyc1hgFj/Cc144A5JJUuHFYqIEBDcD4FeGqUeKLRZqJ9eN9u7/GDjYEgS1g==}
engines: {node: '>=20.0.0'}
hard-rejection@2.1.0:
resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
engines: {node: '>=6'}
- harfbuzzjs@0.4.13:
- resolution: {integrity: sha512-EC0q1K9Axs2IhgzDcTlYSv0m/Xo3VU36BQXtRoUS/4eMpy0rsMHePHwIjHKdhaouVO5O03sfUOwHP6ZzZ3HwFA==}
+ harfbuzzjs@0.8.0:
+ resolution: {integrity: sha512-tx3Wz2ylG+wzK5aWqrkHkCxPrns/7JJhFWHwsJ+XWNAxdRSVZ5KyT+njX5sl7zmE/328HfChsQC0Wp1NaiGQ8Q==}
has-bigints@1.1.0:
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
@@ -7269,10 +7045,6 @@ packages:
resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==}
engines: {node: '>=14'}
- html-encoding-sniffer@3.0.0:
- resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
- engines: {node: '>=12'}
-
html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
@@ -7289,9 +7061,6 @@ packages:
htmlparser2@10.0.0:
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
- htmlparser2@5.0.1:
- resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==}
-
htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
@@ -7305,14 +7074,14 @@ packages:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
+ http-errors@2.0.1:
+ resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
+ engines: {node: '>= 0.8'}
+
http-link-header@1.1.3:
resolution: {integrity: sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==}
engines: {node: '>=6.0.0'}
- http-proxy-agent@5.0.0:
- resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
- engines: {node: '>= 6'}
-
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
@@ -7397,8 +7166,8 @@ packages:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
- import-in-the-middle@2.0.0:
- resolution: {integrity: sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A==}
+ import-in-the-middle@2.0.6:
+ resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==}
import-lazy@4.0.0:
resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
@@ -7453,8 +7222,12 @@ packages:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019.
- ioredis@5.8.2:
- resolution: {integrity: sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==}
+ ioredis@5.9.2:
+ resolution: {integrity: sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ==}
+ engines: {node: '>=12.22.0'}
+
+ ioredis@5.9.3:
+ resolution: {integrity: sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==}
engines: {node: '>=12.22.0'}
ios-haptics@0.1.4:
@@ -7530,6 +7303,11 @@ packages:
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
+ is-docker@3.0.0:
+ resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ hasBin: true
+
is-expression@4.0.0:
resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==}
@@ -7560,6 +7338,11 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
+ is-inside-container@1.0.0:
+ resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
+ engines: {node: '>=14.16'}
+ hasBin: true
+
is-installed-globally@0.4.0:
resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
engines: {node: '>=10'}
@@ -7671,6 +7454,10 @@ packages:
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
engines: {node: '>= 0.4'}
+ is-wsl@3.1.0:
+ resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==}
+ engines: {node: '>=16'}
+
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
@@ -7707,10 +7494,6 @@ packages:
resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
engines: {node: '>=10'}
- istanbul-lib-source-maps@5.0.6:
- resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
- engines: {node: '>=10'}
-
istanbul-reports@3.2.0:
resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
engines: {node: '>=8'}
@@ -7722,8 +7505,8 @@ packages:
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
- jackspeak@4.1.1:
- resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==}
+ jackspeak@4.2.3:
+ resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==}
engines: {node: 20 || >=22}
jest-changed-files@29.7.0:
@@ -7768,15 +7551,6 @@ packages:
resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- jest-environment-jsdom@29.7.0:
- resolution: {integrity: sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==}
- engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- peerDependencies:
- canvas: ^2.5.0
- peerDependenciesMeta:
- canvas:
- optional: true
-
jest-environment-node@29.7.0:
resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -7918,15 +7692,6 @@ packages:
resolution: {integrity: sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==}
engines: {node: '>=0.1.90'}
- jsdom@20.0.3:
- resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==}
- engines: {node: '>=14'}
- peerDependencies:
- canvas: ^2.5.0
- peerDependenciesMeta:
- canvas:
- optional: true
-
jsdom@27.2.0:
resolution: {integrity: sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
@@ -8005,8 +7770,8 @@ packages:
jstransformer@1.0.0:
resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==}
- juice@11.0.3:
- resolution: {integrity: sha512-VYjPg4WylyWyLPnSiUsJ9tnnGhRZF0vn0YD8WWwaI8FhP9+1UdRMyRDbvqPOH/nBotmLKOc+FI+Oma6FwVWfSw==}
+ juice@11.1.1:
+ resolution: {integrity: sha512-4SBfZqKcc6DrIS+5b/WiGoWaZsdUPBH+e6SbRlNjJpaIRtfoBhYReAtobIEW6mcLeFFDXLBJMuZwkJLkBJjs2w==}
engines: {node: '>=18.17'}
hasBin: true
@@ -8102,8 +7867,8 @@ packages:
lodash.uniq@4.5.0:
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
- lodash@4.17.21:
- resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+ lodash@4.17.23:
+ resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
@@ -8129,8 +7894,8 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
- lru-cache@11.2.2:
- resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==}
+ lru-cache@11.2.5:
+ resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==}
engines: {node: 20 || >=22}
lru-cache@5.1.1:
@@ -8169,9 +7934,9 @@ packages:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
- make-fetch-happen@14.0.3:
- resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ make-fetch-happen@15.0.3:
+ resolution: {integrity: sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==}
+ engines: {node: ^20.17.0 || >=22.9.0}
makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
@@ -8250,11 +8015,11 @@ packages:
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
engines: {node: '>= 0.8'}
- mediabunny@1.25.8:
- resolution: {integrity: sha512-2WCa9WtEbHOvg5rAWXQjVE+O/r01fpj65ymZdA2XhIeaWmdDQ40p32F0WY6tdBi+aeY+4ldwhAOpo0eyVrYTGg==}
+ mediabunny@1.34.4:
+ resolution: {integrity: sha512-f1B95A60YoCsZQO/JQYxPDorybEz2Sjasf4RrpwGSMmJW6JVyhI/iJDri9LF6kk5WwUovF8oiTvRNM6xGjWo5w==}
- meilisearch@0.54.0:
- resolution: {integrity: sha512-b1bwJAEfj8C6hgSN88+/LvW3pe3nWC+thBS2seAbPZGakf/vzsLqppgZquiomzCr2GhU7U7H289625qhYe3rbw==}
+ meilisearch@0.55.0:
+ resolution: {integrity: sha512-qSMeiezfDgIqciIeYzh5E4pXDZZD7CtHeWDCs43kN3trLgl5FtfmBAIkljL3huFaOx08feYtC8FfIFUpVwq6rg==}
memoizerific@1.11.3:
resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==}
@@ -8429,13 +8194,13 @@ packages:
minimalistic-assert@1.0.1:
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
- minimatch@10.0.3:
- resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==}
+ minimatch@10.2.1:
+ resolution: {integrity: sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==}
engines: {node: 20 || >=22}
- minimatch@10.1.1:
- resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
- engines: {node: 20 || >=22}
+ minimatch@10.2.4:
+ resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==}
+ engines: {node: 18 || 20 || >=22}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -8463,9 +8228,9 @@ packages:
resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==}
engines: {node: '>=16 || 14 >=14.17'}
- minipass-fetch@4.0.1:
- resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ minipass-fetch@5.0.0:
+ resolution: {integrity: sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==}
+ engines: {node: ^20.17.0 || >=22.9.0}
minipass-flush@1.0.5:
resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
@@ -8537,8 +8302,8 @@ packages:
peerDependencies:
msw: ^2.0.0
- msw@2.12.4:
- resolution: {integrity: sha512-rHNiVfTyKhzc0EjoXUBVGteNKBevdjOlVC6GlIRXpy+/3LHEIGRovnB5WPjcvmNODVQ1TNFnoa7wsGbd0V3epg==}
+ msw@2.12.10:
+ resolution: {integrity: sha512-G3VUymSE0/iegFnuipujpwyTM2GuZAKXNeerUSrG2+Eg391wW63xFs5ixWsK9MWzr1AGoSkYGmyAzNgbR3+urw==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
@@ -8562,8 +8327,8 @@ packages:
resolution: {integrity: sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==}
engines: {node: '>=16.0.0'}
- nan@2.23.1:
- resolution: {integrity: sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==}
+ nan@2.25.0:
+ resolution: {integrity: sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
@@ -8650,13 +8415,13 @@ packages:
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
hasBin: true
- node-gyp@11.5.0:
- resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ node-gyp@12.2.0:
+ resolution: {integrity: sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==}
+ engines: {node: ^20.17.0 || >=22.9.0}
hasBin: true
- node-html-parser@7.0.1:
- resolution: {integrity: sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==}
+ node-html-parser@7.0.2:
+ resolution: {integrity: sha512-DxodLVh7a6JMkYzWyc8nBX9MaF4M0lLFYkJHlWOiu7+9/I6mwNK9u5TbAMC7qfqDJEPX9OIoWA2A9t4C2l1mUQ==}
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
@@ -8664,12 +8429,12 @@ packages:
node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
- nodemailer@7.0.11:
- resolution: {integrity: sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==}
+ nodemailer@8.0.1:
+ resolution: {integrity: sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg==}
engines: {node: '>=6.0.0'}
- nodemon@3.1.11:
- resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
+ nodemon@3.1.14:
+ resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==}
engines: {node: '>=10'}
hasBin: true
@@ -8687,9 +8452,9 @@ packages:
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
hasBin: true
- nopt@8.1.0:
- resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ nopt@9.0.0:
+ resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==}
+ engines: {node: ^20.17.0 || >=22.9.0}
hasBin: true
normalize-package-data@2.5.0:
@@ -8732,9 +8497,6 @@ packages:
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
- nwsapi@2.2.23:
- resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==}
-
oauth2orize-pkce@0.1.2:
resolution: {integrity: sha512-grto2UYhXHi9GLE3IBgBBbV87xci55+bCyjpVuxKyzol6I5Rg0K1MiTuXE+JZk54R86SG2wqXODMiZYHraPpxw==}
@@ -8806,11 +8568,15 @@ packages:
oniguruma-to-es@4.3.4:
resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==}
+ open@10.2.0:
+ resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
+ engines: {node: '>=18'}
+
openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
- openapi-typescript@7.10.1:
- resolution: {integrity: sha512-rBcU8bjKGGZQT4K2ekSTY2Q5veOQbVG/lTKZ49DeCyT9z62hM2Vj/LLHjDHC9W7LJG8YMHcdXpRZDqC1ojB/lw==}
+ openapi-typescript@7.13.0:
+ resolution: {integrity: sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==}
hasBin: true
peerDependencies:
typescript: ^5.x
@@ -8825,8 +8591,8 @@ packages:
ospath@1.2.2:
resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
- otpauth@9.4.1:
- resolution: {integrity: sha512-+iVvys36CFsyXEqfNftQm1II7SW23W1wx9RwNk0Cd97lbvorqAhBDksb/0bYry087QMxjiuBS0wokdoZ0iUeAw==}
+ otpauth@9.5.0:
+ resolution: {integrity: sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==}
outvariant@1.4.3:
resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==}
@@ -8980,30 +8746,30 @@ packages:
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
- pg-cloudflare@1.2.7:
- resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==}
+ pg-cloudflare@1.3.0:
+ resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==}
- pg-connection-string@2.9.1:
- resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==}
+ pg-connection-string@2.11.0:
+ resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==}
pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
- pg-pool@3.10.1:
- resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==}
+ pg-pool@3.11.0:
+ resolution: {integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==}
peerDependencies:
pg: '>=8.0'
- pg-protocol@1.10.3:
- resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==}
+ pg-protocol@1.11.0:
+ resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==}
pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
- pg@8.16.3:
- resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==}
+ pg@8.18.0:
+ resolution: {integrity: sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==}
engines: {node: '>= 16.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
@@ -9029,8 +8795,8 @@ packages:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
- pid-port@2.0.0:
- resolution: {integrity: sha512-EDmfRxLl6lkhPjDI+19l5pkII89xVsiCP3aGjS808f7M16DyCKSXEWthD/hjyDLn5I4gKqTVw7hSgdvdXRJDTw==}
+ pid-port@2.0.1:
+ resolution: {integrity: sha512-pnLo01AmMclw8l+/gfknsP2N351oe8VkVmCLFUvJZ11NRPPmghJrv0OcwsdgPQxsZkFYwm6hPWW0JKmXYCaXAw==}
engines: {node: '>=20'}
pify@2.3.0:
@@ -9054,8 +8820,8 @@ packages:
piscina@4.9.2:
resolution: {integrity: sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==}
- pkce-challenge@5.0.1:
- resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
+ pkce-challenge@6.0.0:
+ resolution: {integrity: sha512-KrEAOa2aGlEyV+gN2jMXwSQhZ5gUR4WwivtPjSktYzh7srb70pYvVGV0uLfmoCx75fUQtqTairuoxayHLtvo9w==}
engines: {node: '>=16.20.0'}
pkg-dir@4.2.0:
@@ -9078,8 +8844,8 @@ packages:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
- pnpm@10.25.0:
- resolution: {integrity: sha512-XoJjkCevN8+DIGG8xtY5whljRIjg8rrr54UCink957Ul/80/f/V09emGBlTgmP6FK6isXdXO/hdn0joCCpL1AQ==}
+ pnpm@10.30.1:
+ resolution: {integrity: sha512-NZDlUNU4TKo5vVx8c59yJwI0svYFnhMBj5dcMTseuf78wJcUBIdl1Nnv6WE4LDEuYkVywEIHYr3F1ZQM35vnOg==}
engines: {node: '>=18.12'}
hasBin: true
@@ -9284,8 +9050,8 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
- prettier@3.7.4:
- resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
+ prettier@3.8.1:
+ resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==}
engines: {node: '>=14'}
hasBin: true
@@ -9312,9 +9078,9 @@ packages:
probe-image-size@7.2.3:
resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==}
- proc-log@5.0.0:
- resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ proc-log@6.1.0:
+ resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==}
+ engines: {node: ^20.17.0 || >=22.9.0}
process-exists@5.0.0:
resolution: {integrity: sha512-6QPRh5fyHD8MaXr4GYML8K/YY0Sq5dKHGIOrAKS3cYpHQdmygFCcijIu1dVoNKAZ0TWAMoeh8KDK9dF8auBkJA==}
@@ -9380,9 +9146,6 @@ packages:
engines: {node: '>= 0.10'}
hasBin: true
- psl@1.15.0:
- resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
-
pstree.remy@1.1.8:
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
@@ -9462,13 +9225,10 @@ packages:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
- qs@6.14.0:
- resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
+ qs@6.14.1:
+ resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==}
engines: {node: '>=0.6'}
- querystringify@2.2.0:
- resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
-
queue-lit@1.5.2:
resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==}
engines: {node: '>=12'}
@@ -9510,8 +9270,8 @@ packages:
resolution: {integrity: sha512-g8OUrgMXAR9ys/ZuJVfBr05sPPoMA7nHIVs8VEvg9QwM5W4GR2qSFEEHjsyHF1eWlBaf8Ev40WNjQFQ+nJTO3w==}
engines: {node: '>=18'}
- re2@1.22.3:
- resolution: {integrity: sha512-002aE82U91DiaUA16U6vbiJusvPXn1OWiQukOxJkVUTXbzrSuQbFNHYKcGw8QK/uifRCfjl2Hd/vXYDanKkmaQ==}
+ re2@1.23.3:
+ resolution: {integrity: sha512-5jh686rmj/8dYpBo72XYgwzgG8Y9HNDATYZ1x01gqZ6FvXVUP33VZ0+6GLCeavaNywz3OkXBU8iNX7LjiuisPg==}
react-docgen-typescript@2.4.0:
resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==}
@@ -9522,10 +9282,10 @@ packages:
resolution: {integrity: sha512-+NRMYs2DyTP4/tqWz371Oo50JqmWltR1h2gcdgUMAWZJIAvrd0/SqlCfx7tpzpl/s36rzw6qH2MjoNrxtRNYhA==}
engines: {node: ^20.9.0 || >=22}
- react-dom@19.2.1:
- resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==}
+ react-dom@19.2.4:
+ resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
peerDependencies:
- react: ^19.2.1
+ react: ^19.2.4
react-is@17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
@@ -9533,8 +9293,8 @@ packages:
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
- react@19.2.1:
- resolution: {integrity: sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==}
+ react@19.2.4:
+ resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
engines: {node: '>=0.10.0'}
read-pkg-up@7.0.1:
@@ -9582,9 +9342,6 @@ packages:
resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
engines: {node: '>=4'}
- redis-info@3.1.0:
- resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==}
-
redis-parser@3.0.0:
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
engines: {node: '>=4'}
@@ -9642,9 +9399,6 @@ packages:
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
- requires-port@1.0.0:
- resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
-
resolve-alpn@1.2.1:
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
@@ -9692,8 +9446,8 @@ packages:
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
engines: {node: '>= 4'}
- rettime@0.7.0:
- resolution: {integrity: sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==}
+ rettime@0.10.1:
+ resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==}
reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
@@ -9712,8 +9466,8 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
- rollup@4.53.3:
- resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==}
+ rollup@4.59.0:
+ resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -9724,6 +9478,10 @@ packages:
rss-parser@3.13.0:
resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==}
+ run-applescript@7.1.0:
+ resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
+ engines: {node: '>=18'}
+
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -9758,11 +9516,11 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
- sanitize-html@2.17.0:
- resolution: {integrity: sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==}
+ sanitize-html@2.17.1:
+ resolution: {integrity: sha512-ehFCW+q1a4CSOWRAdX97BX/6/PDEkCqw7/0JXZAGQV57FQB3YOkTa/rrzHPeJ+Aghy4vZAFfWMYyfxIiB7F/gw==}
- sass@1.95.1:
- resolution: {integrity: sha512-uPoDh5NIEZV4Dp5GBodkmNY9tSQfXY02pmCcUo+FR1P+x953HGkpw+vV28D4IqYB6f8webZtwoSaZaiPtpTeMg==}
+ sass@1.97.3:
+ resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -9810,8 +9568,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
- semver@7.7.3:
- resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ semver@7.7.4:
+ resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
hasBin: true
@@ -9872,8 +9630,8 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
- shiki@3.19.0:
- resolution: {integrity: sha512-77VJr3OR/VUZzPiStyRhADmO2jApMM0V2b1qf0RpfWya8Zr1PeZev5AEpPGAAKWdiYUtcZGBE4F5QvJml1PvWA==}
+ shiki@3.22.0:
+ resolution: {integrity: sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g==}
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
@@ -10040,9 +9798,6 @@ packages:
resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==}
engines: {node: '>=0.10.0'}
- sortablejs@1.14.0:
- resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
-
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -10098,9 +9853,9 @@ packages:
engines: {node: '>=0.10.0'}
hasBin: true
- ssri@12.0.0:
- resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ ssri@13.0.0:
+ resolution: {integrity: sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==}
+ engines: {node: ^20.17.0 || >=22.9.0}
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
@@ -10151,8 +9906,8 @@ packages:
react-dom:
optional: true
- storybook@10.1.5:
- resolution: {integrity: sha512-q3xB1pOcmmHUH9LfQNY/BWMGxp3fc1OALJf+F5BXIxHGQUEIizz6V1AbDOngWN9oWzuA8Gdz5rOCe7yelOMWVg==}
+ storybook@10.2.10:
+ resolution: {integrity: sha512-N4U42qKgzMHS7DjqLz5bY4P7rnvJtYkWFCyKspZr3FhPUuy6CWOae3aYC2BjXkHrdug0Jyta6VxFTuB1tYUKhg==}
hasBin: true
peerDependencies:
prettier: ^2 || ^3
@@ -10269,8 +10024,8 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
- strnum@2.1.1:
- resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==}
+ strnum@2.1.2:
+ resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==}
strtok3@10.3.4:
resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==}
@@ -10282,12 +10037,12 @@ packages:
peerDependencies:
postcss: ^8.4.32
- superagent@10.2.3:
- resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==}
+ superagent@10.3.0:
+ resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==}
engines: {node: '>=14.18.0'}
- supertest@7.1.4:
- resolution: {integrity: sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==}
+ supertest@7.2.2:
+ resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==}
engines: {node: '>=14.18.0'}
supports-color@10.2.2:
@@ -10322,14 +10077,8 @@ packages:
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
- systeminformation@5.27.14:
- resolution: {integrity: sha512-3DoNDYSZBLxBwaJtQGWNpq0fonga/VZ47HY1+7/G3YoIPaPz93Df6egSzzTKbEMmlzUpy3eQ0nR9REuYIycXGg==}
- engines: {node: '>=8.0.0'}
- os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
- hasBin: true
-
- systeminformation@5.27.7:
- resolution: {integrity: sha512-saaqOoVEEFaux4v0K8Q7caiauRwjXC4XbD2eH60dxHXbpKxQ8kH9Rf7Jh+nryKpOUSEFxtCdBlSUx0/lO6rwRg==}
+ systeminformation@5.31.1:
+ resolution: {integrity: sha512-6pRwxoGeV/roJYpsfcP6tN9mep6pPeCtXbUOCdVa0nme05Brwcwdge/fVNhIZn2wuUitAKZm4IYa7QjnRIa9zA==}
engines: {node: '>=8.0.0'}
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
hasBin: true
@@ -10344,17 +10093,18 @@ packages:
tar@6.2.1:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
- tar@7.5.2:
- resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==}
+ tar@7.5.9:
+ resolution: {integrity: sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==}
engines: {node: '>=18'}
taskkill@5.0.0:
resolution: {integrity: sha512-+HRtZ40Vc+6YfCDWCeAsixwxJgMbPY4HHuTgzPYH3JXvqHWUlsCfy+ylXlAKhFNcuLp4xVeWeFBUhDk+7KYUvQ==}
engines: {node: '>=14.16'}
- terser@5.44.1:
- resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==}
+ terser@5.46.0:
+ resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==}
engines: {node: '>=10'}
hasBin: true
@@ -10371,8 +10121,8 @@ packages:
thread-stream@3.1.0:
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
- three@0.181.2:
- resolution: {integrity: sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==}
+ three@0.183.1:
+ resolution: {integrity: sha512-Psv6bbd3d/M/01MT2zZ+VmD0Vj2dbWTNhfe4CuSg7w5TuW96M3NOyCVuh9SZQ05CpGmD7NEcJhZw4GVjhCYxfQ==}
throttle-debounce@5.0.2:
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
@@ -10472,10 +10222,6 @@ packages:
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
hasBin: true
- tough-cookie@4.1.4:
- resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
- engines: {node: '>=6'}
-
tough-cookie@5.1.2:
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
engines: {node: '>=16'}
@@ -10487,10 +10233,6 @@ packages:
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
- tr46@3.0.0:
- resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
- engines: {node: '>=12'}
-
tr46@6.0.0:
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
engines: {node: '>=20'}
@@ -10509,8 +10251,8 @@ packages:
trough@2.2.0:
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
- ts-api-utils@2.1.0:
- resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+ ts-api-utils@2.4.0:
+ resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
engines: {node: '>=18.12'}
peerDependencies:
typescript: '>=4.8.4'
@@ -10720,9 +10462,6 @@ packages:
undefsafe@2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
- undici-types@6.21.0:
- resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
-
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
@@ -10741,13 +10480,13 @@ packages:
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
- unique-filename@4.0.0:
- resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ unique-filename@5.0.0:
+ resolution: {integrity: sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==}
+ engines: {node: ^20.17.0 || >=22.9.0}
- unique-slug@5.0.0:
- resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ unique-slug@6.0.0:
+ resolution: {integrity: sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==}
+ engines: {node: ^20.17.0 || >=22.9.0}
unist-util-is@6.0.1:
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
@@ -10768,10 +10507,6 @@ packages:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
engines: {node: '>= 4.0.0'}
- universalify@0.2.0:
- resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
- engines: {node: '>= 4.0.0'}
-
universalify@2.0.1:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
@@ -10807,16 +10542,13 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
- url-parse@1.5.10:
- resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
-
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- utf-8-validate@6.0.5:
- resolution: {integrity: sha512-EYZR+OpIXp9Y1eG1iueg8KRsY8TuT8VNgnanZ0uA3STqhHQTLwbl+WX76/9X5OY12yQubymBpaBSmMPkSTQcKA==}
+ utf-8-validate@6.0.6:
+ resolution: {integrity: sha512-q3l3P9UtEEiAHcsgsqTgf9PPjctrDWoIXW3NpOHFdRDbLvu4DLIcxHangJ4RLrWkBcKjmcs/6NkerI8T/rE4LA==}
engines: {node: '>=6.14.2'}
util-deprecate@1.0.2:
@@ -10892,8 +10624,8 @@ packages:
vite-plugin-turbosnap@1.0.3:
resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
- vite@7.2.7:
- resolution: {integrity: sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==}
+ vite@7.3.1:
+ resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -10943,18 +10675,18 @@ packages:
peerDependencies:
vitest: '>=3'
- vitest@4.0.15:
- resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==}
+ vitest@4.0.18:
+ resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@opentelemetry/api': ^1.9.0
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
- '@vitest/browser-playwright': 4.0.15
- '@vitest/browser-preview': 4.0.15
- '@vitest/browser-webdriverio': 4.0.15
- '@vitest/ui': 4.0.15
+ '@vitest/browser-playwright': 4.0.18
+ '@vitest/browser-preview': 4.0.18
+ '@vitest/browser-webdriverio': 4.0.18
+ '@vitest/ui': 4.0.18
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
@@ -11009,8 +10741,8 @@ packages:
vue-component-type-helpers@2.2.12:
resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
- vue-component-type-helpers@3.1.8:
- resolution: {integrity: sha512-oaowlmEM6BaYY+8o+9D9cuzxpWQWHqHTMKakMxXu0E+UCIOMTljyIPO15jcnaCwJtZu/zWDotK7mOIHvWD9mcw==}
+ vue-component-type-helpers@3.2.4:
+ resolution: {integrity: sha512-05lR16HeZDcDpB23ku5b5f1fBOoHqFnMiKRr2CiEvbG5Ux4Yi0McmQBOET0dR0nxDXosxyVqv67q6CzS3AK8rw==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
@@ -11028,40 +10760,31 @@ packages:
peerDependencies:
vue: '>=2'
- vue-eslint-parser@10.2.0:
- resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==}
+ vue-eslint-parser@10.4.0:
+ resolution: {integrity: sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^8.57.0 || ^9.0.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
vue-inbrowser-compiler-independent-utils@4.71.1:
resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==}
peerDependencies:
vue: '>=2'
- vue-tsc@3.1.8:
- resolution: {integrity: sha512-deKgwx6exIHeZwF601P1ktZKNF0bepaSN4jBU3AsbldPx9gylUc1JDxYppl82yxgkAgaz0Y0LCLOi+cXe9HMYA==}
+ vue-tsc@3.2.4:
+ resolution: {integrity: sha512-xj3YCvSLNDKt1iF9OcImWHhmYcihVu9p4b9s4PGR/qp6yhW+tZJaypGxHScRyOrdnHvaOeF+YkZOdKwbgGvp5g==}
hasBin: true
peerDependencies:
typescript: '>=5.0.0'
- vue@3.5.25:
- resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==}
+ vue@3.5.28:
+ resolution: {integrity: sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- vuedraggable@4.1.0:
- resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
- peerDependencies:
- vue: ^3.0.1
-
- w3c-xmlserializer@4.0.0:
- resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==}
- engines: {node: '>=14'}
-
w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
@@ -11087,8 +10810,8 @@ packages:
engines: {node: '>= 16'}
hasBin: true
- web-resource-inliner@7.0.0:
- resolution: {integrity: sha512-NlfnGF8MY9ZUwFjyq3vOUBx7KwF8bmE+ywR781SB0nWB6MoMxN4BA8gtgP1KGTZo/O/AyWJz7HZpR704eaj4mg==}
+ web-resource-inliner@8.0.0:
+ resolution: {integrity: sha512-Ezr98sqXW/+OCGoUEXuOKVR+oVFlSdn1tIySEEJdiSAw4IjrW8hQkwARSSBJTSB5Us5dnytDgL0ZDliAYBhaNA==}
engines: {node: '>=10.0.0'}
web-streams-polyfill@3.3.3:
@@ -11098,24 +10821,17 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
- webidl-conversions@7.0.0:
- resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
- engines: {node: '>=12'}
-
- webidl-conversions@8.0.0:
- resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==}
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
engines: {node: '>=20'}
webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
- whatwg-encoding@2.0.0:
- resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
- engines: {node: '>=12'}
-
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
+ deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
whatwg-mimetype@3.0.0:
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
@@ -11125,9 +10841,9 @@ packages:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
- whatwg-url@11.0.0:
- resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==}
- engines: {node: '>=12'}
+ whatwg-mimetype@5.0.0:
+ resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+ engines: {node: '>=20'}
whatwg-url@15.1.0:
resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==}
@@ -11164,9 +10880,9 @@ packages:
engines: {node: '>= 8'}
hasBin: true
- which@5.0.0:
- resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
- engines: {node: ^18.17.0 || >=20.5.0}
+ which@6.0.0:
+ resolution: {integrity: sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==}
+ engines: {node: ^20.17.0 || >=22.9.0}
hasBin: true
why-is-node-running@2.3.0:
@@ -11208,8 +10924,8 @@ packages:
resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
- ws@8.18.3:
- resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ ws@8.19.0:
+ resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@@ -11220,6 +10936,10 @@ packages:
utf-8-validate:
optional: true
+ wsl-utils@0.1.0:
+ resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
+ engines: {node: '>=18'}
+
xev@3.0.2:
resolution: {integrity: sha512-8kxuH95iMXzHZj+fwqfA4UrPcYOy6bGIgfWzo9Ji23JoEc30ge/Z++Ubkiuy8c0+M64nXmmxrmJ7C8wnuBhluw==}
@@ -11330,7 +11050,7 @@ packages:
snapshots:
- '@acemir/cssom@0.9.23':
+ '@acemir/cssom@0.9.31':
optional: true
'@adobe/css-tools@4.4.4': {}
@@ -11386,22 +11106,22 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@asamuzakjp/css-color@4.0.5':
+ '@asamuzakjp/css-color@4.1.2':
dependencies:
- '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
- '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
- '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
- '@csstools/css-tokenizer': 3.0.4
- lru-cache: 11.2.2
+ '@csstools/css-calc': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-color-parser': 4.0.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+ lru-cache: 11.2.5
optional: true
- '@asamuzakjp/dom-selector@6.7.4':
+ '@asamuzakjp/dom-selector@6.7.8':
dependencies:
'@asamuzakjp/nwsapi': 2.3.9
bidi-js: 1.0.3
css-tree: 3.1.0
is-potential-custom-element-name: 1.0.1
- lru-cache: 11.2.2
+ lru-cache: 11.2.5
optional: true
'@asamuzakjp/nwsapi@2.3.9':
@@ -11410,20 +11130,20 @@ snapshots:
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/types': 3.973.1
tslib: 2.8.1
'@aws-crypto/crc32c@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/types': 3.973.1
tslib: 2.8.1
'@aws-crypto/sha1-browser@5.2.0':
dependencies:
'@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/types': 3.973.1
'@aws-sdk/util-locate-window': 3.893.0
'@smithy/util-utf8': 2.3.0
tslib: 2.8.1
@@ -11433,7 +11153,7 @@ snapshots:
'@aws-crypto/sha256-js': 5.2.0
'@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/types': 3.973.1
'@aws-sdk/util-locate-window': 3.893.0
'@smithy/util-utf8': 2.3.0
tslib: 2.8.1
@@ -11441,7 +11161,7 @@ snapshots:
'@aws-crypto/sha256-js@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/types': 3.973.1
tslib: 2.8.1
'@aws-crypto/supports-web-crypto@5.2.0':
@@ -11450,764 +11170,463 @@ snapshots:
'@aws-crypto/util@5.2.0':
dependencies:
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/types': 3.973.1
'@smithy/util-utf8': 2.3.0
tslib: 2.8.1
- '@aws-sdk/client-s3@3.948.0':
+ '@aws-sdk/client-s3@3.995.0':
dependencies:
'@aws-crypto/sha1-browser': 5.2.0
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/credential-provider-node': 3.948.0
- '@aws-sdk/middleware-bucket-endpoint': 3.936.0
- '@aws-sdk/middleware-expect-continue': 3.936.0
- '@aws-sdk/middleware-flexible-checksums': 3.947.0
- '@aws-sdk/middleware-host-header': 3.936.0
- '@aws-sdk/middleware-location-constraint': 3.936.0
- '@aws-sdk/middleware-logger': 3.936.0
- '@aws-sdk/middleware-recursion-detection': 3.948.0
- '@aws-sdk/middleware-sdk-s3': 3.947.0
- '@aws-sdk/middleware-ssec': 3.936.0
- '@aws-sdk/middleware-user-agent': 3.947.0
- '@aws-sdk/region-config-resolver': 3.936.0
- '@aws-sdk/signature-v4-multi-region': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@aws-sdk/util-user-agent-browser': 3.936.0
- '@aws-sdk/util-user-agent-node': 3.947.0
- '@smithy/config-resolver': 4.4.3
- '@smithy/core': 3.18.7
- '@smithy/eventstream-serde-browser': 4.2.5
- '@smithy/eventstream-serde-config-resolver': 4.3.5
- '@smithy/eventstream-serde-node': 4.2.5
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/hash-blob-browser': 4.2.6
- '@smithy/hash-node': 4.2.5
- '@smithy/hash-stream-node': 4.2.5
- '@smithy/invalid-dependency': 4.2.5
- '@smithy/md5-js': 4.2.5
- '@smithy/middleware-content-length': 4.2.5
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/middleware-retry': 4.4.14
- '@smithy/middleware-serde': 4.2.6
- '@smithy/middleware-stack': 4.2.5
- '@smithy/node-config-provider': 4.3.5
- '@smithy/node-http-handler': 4.4.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
- '@smithy/util-base64': 4.3.0
- '@smithy/util-body-length-browser': 4.2.0
- '@smithy/util-body-length-node': 4.2.1
- '@smithy/util-defaults-mode-browser': 4.3.13
- '@smithy/util-defaults-mode-node': 4.2.16
- '@smithy/util-endpoints': 3.2.5
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-retry': 4.2.5
- '@smithy/util-stream': 4.5.6
- '@smithy/util-utf8': 4.2.0
- '@smithy/util-waiter': 4.2.5
- tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
-
- '@aws-sdk/client-sesv2@3.938.0':
- dependencies:
- '@aws-crypto/sha256-browser': 5.2.0
- '@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/credential-provider-node': 3.936.0
- '@aws-sdk/middleware-host-header': 3.936.0
- '@aws-sdk/middleware-logger': 3.936.0
- '@aws-sdk/middleware-recursion-detection': 3.936.0
- '@aws-sdk/middleware-user-agent': 3.936.0
- '@aws-sdk/region-config-resolver': 3.936.0
- '@aws-sdk/signature-v4-multi-region': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@aws-sdk/util-user-agent-browser': 3.936.0
- '@aws-sdk/util-user-agent-node': 3.936.0
- '@smithy/config-resolver': 4.4.3
- '@smithy/core': 3.18.7
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/hash-node': 4.2.5
- '@smithy/invalid-dependency': 4.2.5
- '@smithy/middleware-content-length': 4.2.5
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/middleware-retry': 4.4.14
- '@smithy/middleware-serde': 4.2.6
- '@smithy/middleware-stack': 4.2.5
- '@smithy/node-config-provider': 4.3.5
- '@smithy/node-http-handler': 4.4.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/credential-provider-node': 3.972.10
+ '@aws-sdk/middleware-bucket-endpoint': 3.972.3
+ '@aws-sdk/middleware-expect-continue': 3.972.3
+ '@aws-sdk/middleware-flexible-checksums': 3.972.9
+ '@aws-sdk/middleware-host-header': 3.972.3
+ '@aws-sdk/middleware-location-constraint': 3.972.3
+ '@aws-sdk/middleware-logger': 3.972.3
+ '@aws-sdk/middleware-recursion-detection': 3.972.3
+ '@aws-sdk/middleware-sdk-s3': 3.972.11
+ '@aws-sdk/middleware-ssec': 3.972.3
+ '@aws-sdk/middleware-user-agent': 3.972.11
+ '@aws-sdk/region-config-resolver': 3.972.3
+ '@aws-sdk/signature-v4-multi-region': 3.995.0
+ '@aws-sdk/types': 3.973.1
+ '@aws-sdk/util-endpoints': 3.995.0
+ '@aws-sdk/util-user-agent-browser': 3.972.3
+ '@aws-sdk/util-user-agent-node': 3.972.10
+ '@smithy/config-resolver': 4.4.6
+ '@smithy/core': 3.23.2
+ '@smithy/eventstream-serde-browser': 4.2.8
+ '@smithy/eventstream-serde-config-resolver': 4.3.8
+ '@smithy/eventstream-serde-node': 4.2.8
+ '@smithy/fetch-http-handler': 5.3.9
+ '@smithy/hash-blob-browser': 4.2.9
+ '@smithy/hash-node': 4.2.8
+ '@smithy/hash-stream-node': 4.2.8
+ '@smithy/invalid-dependency': 4.2.8
+ '@smithy/md5-js': 4.2.8
+ '@smithy/middleware-content-length': 4.2.8
+ '@smithy/middleware-endpoint': 4.4.16
+ '@smithy/middleware-retry': 4.4.33
+ '@smithy/middleware-serde': 4.2.9
+ '@smithy/middleware-stack': 4.2.8
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/node-http-handler': 4.4.10
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
+ '@smithy/url-parser': 4.2.8
'@smithy/util-base64': 4.3.0
'@smithy/util-body-length-browser': 4.2.0
'@smithy/util-body-length-node': 4.2.1
- '@smithy/util-defaults-mode-browser': 4.3.13
- '@smithy/util-defaults-mode-node': 4.2.16
- '@smithy/util-endpoints': 3.2.5
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-retry': 4.2.5
+ '@smithy/util-defaults-mode-browser': 4.3.32
+ '@smithy/util-defaults-mode-node': 4.2.35
+ '@smithy/util-endpoints': 3.2.8
+ '@smithy/util-middleware': 4.2.8
+ '@smithy/util-retry': 4.2.8
+ '@smithy/util-stream': 4.5.12
'@smithy/util-utf8': 4.2.0
+ '@smithy/util-waiter': 4.2.8
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/client-sso@3.936.0':
+ '@aws-sdk/client-sso@3.993.0':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/middleware-host-header': 3.936.0
- '@aws-sdk/middleware-logger': 3.936.0
- '@aws-sdk/middleware-recursion-detection': 3.936.0
- '@aws-sdk/middleware-user-agent': 3.936.0
- '@aws-sdk/region-config-resolver': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@aws-sdk/util-user-agent-browser': 3.936.0
- '@aws-sdk/util-user-agent-node': 3.936.0
- '@smithy/config-resolver': 4.4.3
- '@smithy/core': 3.18.7
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/hash-node': 4.2.5
- '@smithy/invalid-dependency': 4.2.5
- '@smithy/middleware-content-length': 4.2.5
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/middleware-retry': 4.4.14
- '@smithy/middleware-serde': 4.2.6
- '@smithy/middleware-stack': 4.2.5
- '@smithy/node-config-provider': 4.3.5
- '@smithy/node-http-handler': 4.4.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/middleware-host-header': 3.972.3
+ '@aws-sdk/middleware-logger': 3.972.3
+ '@aws-sdk/middleware-recursion-detection': 3.972.3
+ '@aws-sdk/middleware-user-agent': 3.972.11
+ '@aws-sdk/region-config-resolver': 3.972.3
+ '@aws-sdk/types': 3.973.1
+ '@aws-sdk/util-endpoints': 3.993.0
+ '@aws-sdk/util-user-agent-browser': 3.972.3
+ '@aws-sdk/util-user-agent-node': 3.972.10
+ '@smithy/config-resolver': 4.4.6
+ '@smithy/core': 3.23.2
+ '@smithy/fetch-http-handler': 5.3.9
+ '@smithy/hash-node': 4.2.8
+ '@smithy/invalid-dependency': 4.2.8
+ '@smithy/middleware-content-length': 4.2.8
+ '@smithy/middleware-endpoint': 4.4.16
+ '@smithy/middleware-retry': 4.4.33
+ '@smithy/middleware-serde': 4.2.9
+ '@smithy/middleware-stack': 4.2.8
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/node-http-handler': 4.4.10
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
+ '@smithy/url-parser': 4.2.8
'@smithy/util-base64': 4.3.0
'@smithy/util-body-length-browser': 4.2.0
'@smithy/util-body-length-node': 4.2.1
- '@smithy/util-defaults-mode-browser': 4.3.13
- '@smithy/util-defaults-mode-node': 4.2.16
- '@smithy/util-endpoints': 3.2.5
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-retry': 4.2.5
+ '@smithy/util-defaults-mode-browser': 4.3.32
+ '@smithy/util-defaults-mode-node': 4.2.35
+ '@smithy/util-endpoints': 3.2.8
+ '@smithy/util-middleware': 4.2.8
+ '@smithy/util-retry': 4.2.8
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/client-sso@3.948.0':
+ '@aws-sdk/core@3.973.11':
dependencies:
- '@aws-crypto/sha256-browser': 5.2.0
- '@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/middleware-host-header': 3.936.0
- '@aws-sdk/middleware-logger': 3.936.0
- '@aws-sdk/middleware-recursion-detection': 3.948.0
- '@aws-sdk/middleware-user-agent': 3.947.0
- '@aws-sdk/region-config-resolver': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@aws-sdk/util-user-agent-browser': 3.936.0
- '@aws-sdk/util-user-agent-node': 3.947.0
- '@smithy/config-resolver': 4.4.3
- '@smithy/core': 3.18.7
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/hash-node': 4.2.5
- '@smithy/invalid-dependency': 4.2.5
- '@smithy/middleware-content-length': 4.2.5
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/middleware-retry': 4.4.14
- '@smithy/middleware-serde': 4.2.6
- '@smithy/middleware-stack': 4.2.5
- '@smithy/node-config-provider': 4.3.5
- '@smithy/node-http-handler': 4.4.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
- '@smithy/util-base64': 4.3.0
- '@smithy/util-body-length-browser': 4.2.0
- '@smithy/util-body-length-node': 4.2.1
- '@smithy/util-defaults-mode-browser': 4.3.13
- '@smithy/util-defaults-mode-node': 4.2.16
- '@smithy/util-endpoints': 3.2.5
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-retry': 4.2.5
- '@smithy/util-utf8': 4.2.0
- tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
-
- '@aws-sdk/core@3.936.0':
- dependencies:
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/xml-builder': 3.930.0
- '@smithy/core': 3.18.7
- '@smithy/node-config-provider': 4.3.5
- '@smithy/property-provider': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/signature-v4': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/util-base64': 4.3.0
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-utf8': 4.2.0
- tslib: 2.8.1
-
- '@aws-sdk/core@3.947.0':
- dependencies:
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/xml-builder': 3.930.0
- '@smithy/core': 3.18.7
- '@smithy/node-config-provider': 4.3.5
- '@smithy/property-provider': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/signature-v4': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@aws-sdk/xml-builder': 3.972.5
+ '@smithy/core': 3.23.2
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/property-provider': 4.2.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/signature-v4': 5.3.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
'@smithy/util-base64': 4.3.0
- '@smithy/util-middleware': 4.2.5
+ '@smithy/util-middleware': 4.2.8
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
- '@aws-sdk/credential-provider-env@3.936.0':
- dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/credential-provider-env@3.947.0':
+ '@aws-sdk/crc64-nvme@3.972.0':
dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/credential-provider-http@3.936.0':
+ '@aws-sdk/credential-provider-env@3.972.9':
dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/node-http-handler': 4.4.5
- '@smithy/property-provider': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/util-stream': 4.5.6
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/types': 3.973.1
+ '@smithy/property-provider': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/credential-provider-http@3.947.0':
+ '@aws-sdk/credential-provider-http@3.972.11':
dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/node-http-handler': 4.4.5
- '@smithy/property-provider': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/util-stream': 4.5.6
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/types': 3.973.1
+ '@smithy/fetch-http-handler': 5.3.9
+ '@smithy/node-http-handler': 4.4.10
+ '@smithy/property-provider': 4.2.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
+ '@smithy/util-stream': 4.5.12
tslib: 2.8.1
- '@aws-sdk/credential-provider-ini@3.936.0':
+ '@aws-sdk/credential-provider-ini@3.972.9':
dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/credential-provider-env': 3.936.0
- '@aws-sdk/credential-provider-http': 3.936.0
- '@aws-sdk/credential-provider-login': 3.936.0
- '@aws-sdk/credential-provider-process': 3.936.0
- '@aws-sdk/credential-provider-sso': 3.936.0
- '@aws-sdk/credential-provider-web-identity': 3.936.0
- '@aws-sdk/nested-clients': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/credential-provider-imds': 4.2.5
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/credential-provider-env': 3.972.9
+ '@aws-sdk/credential-provider-http': 3.972.11
+ '@aws-sdk/credential-provider-login': 3.972.9
+ '@aws-sdk/credential-provider-process': 3.972.9
+ '@aws-sdk/credential-provider-sso': 3.972.9
+ '@aws-sdk/credential-provider-web-identity': 3.972.9
+ '@aws-sdk/nested-clients': 3.993.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/credential-provider-imds': 4.2.8
+ '@smithy/property-provider': 4.2.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-ini@3.948.0':
+ '@aws-sdk/credential-provider-login@3.972.9':
dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/credential-provider-env': 3.947.0
- '@aws-sdk/credential-provider-http': 3.947.0
- '@aws-sdk/credential-provider-login': 3.948.0
- '@aws-sdk/credential-provider-process': 3.947.0
- '@aws-sdk/credential-provider-sso': 3.948.0
- '@aws-sdk/credential-provider-web-identity': 3.948.0
- '@aws-sdk/nested-clients': 3.948.0
- '@aws-sdk/types': 3.936.0
- '@smithy/credential-provider-imds': 4.2.5
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/nested-clients': 3.993.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/property-provider': 4.2.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-login@3.936.0':
+ '@aws-sdk/credential-provider-node@3.972.10':
dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/nested-clients': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/credential-provider-env': 3.972.9
+ '@aws-sdk/credential-provider-http': 3.972.11
+ '@aws-sdk/credential-provider-ini': 3.972.9
+ '@aws-sdk/credential-provider-process': 3.972.9
+ '@aws-sdk/credential-provider-sso': 3.972.9
+ '@aws-sdk/credential-provider-web-identity': 3.972.9
+ '@aws-sdk/types': 3.973.1
+ '@smithy/credential-provider-imds': 4.2.8
+ '@smithy/property-provider': 4.2.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-login@3.948.0':
+ '@aws-sdk/credential-provider-process@3.972.9':
dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/nested-clients': 3.948.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/types': 3.973.1
+ '@smithy/property-provider': 4.2.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
- '@aws-sdk/credential-provider-node@3.936.0':
+ '@aws-sdk/credential-provider-sso@3.972.9':
dependencies:
- '@aws-sdk/credential-provider-env': 3.936.0
- '@aws-sdk/credential-provider-http': 3.936.0
- '@aws-sdk/credential-provider-ini': 3.936.0
- '@aws-sdk/credential-provider-process': 3.936.0
- '@aws-sdk/credential-provider-sso': 3.936.0
- '@aws-sdk/credential-provider-web-identity': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/credential-provider-imds': 4.2.5
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/client-sso': 3.993.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/token-providers': 3.993.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/property-provider': 4.2.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-node@3.948.0':
+ '@aws-sdk/credential-provider-web-identity@3.972.9':
dependencies:
- '@aws-sdk/credential-provider-env': 3.947.0
- '@aws-sdk/credential-provider-http': 3.947.0
- '@aws-sdk/credential-provider-ini': 3.948.0
- '@aws-sdk/credential-provider-process': 3.947.0
- '@aws-sdk/credential-provider-sso': 3.948.0
- '@aws-sdk/credential-provider-web-identity': 3.948.0
- '@aws-sdk/types': 3.936.0
- '@smithy/credential-provider-imds': 4.2.5
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/nested-clients': 3.993.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/property-provider': 4.2.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/credential-provider-process@3.936.0':
+ '@aws-sdk/lib-storage@3.995.0(@aws-sdk/client-s3@3.995.0)':
dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/credential-provider-process@3.947.0':
- dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/credential-provider-sso@3.936.0':
- dependencies:
- '@aws-sdk/client-sso': 3.936.0
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/token-providers': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
- tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
-
- '@aws-sdk/credential-provider-sso@3.948.0':
- dependencies:
- '@aws-sdk/client-sso': 3.948.0
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/token-providers': 3.948.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
- tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
-
- '@aws-sdk/credential-provider-web-identity@3.936.0':
- dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/nested-clients': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
- tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
-
- '@aws-sdk/credential-provider-web-identity@3.948.0':
- dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/nested-clients': 3.948.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
- tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
-
- '@aws-sdk/lib-storage@3.948.0(@aws-sdk/client-s3@3.948.0)':
- dependencies:
- '@aws-sdk/client-s3': 3.948.0
- '@smithy/abort-controller': 4.2.5
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/smithy-client': 4.9.10
+ '@aws-sdk/client-s3': 3.995.0
+ '@smithy/abort-controller': 4.2.8
+ '@smithy/middleware-endpoint': 4.4.16
+ '@smithy/smithy-client': 4.11.5
buffer: 5.6.0
events: 3.3.0
stream-browserify: 3.0.0
tslib: 2.8.1
- '@aws-sdk/middleware-bucket-endpoint@3.936.0':
+ '@aws-sdk/middleware-bucket-endpoint@3.972.3':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-arn-parser': 3.893.0
- '@smithy/node-config-provider': 4.3.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@aws-sdk/util-arn-parser': 3.972.2
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
'@smithy/util-config-provider': 4.2.0
tslib: 2.8.1
- '@aws-sdk/middleware-expect-continue@3.936.0':
+ '@aws-sdk/middleware-expect-continue@3.972.3':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/middleware-flexible-checksums@3.947.0':
+ '@aws-sdk/middleware-flexible-checksums@3.972.9':
dependencies:
'@aws-crypto/crc32': 5.2.0
'@aws-crypto/crc32c': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/crc64-nvme': 3.972.0
+ '@aws-sdk/types': 3.973.1
'@smithy/is-array-buffer': 4.2.0
- '@smithy/node-config-provider': 4.3.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-stream': 4.5.6
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
+ '@smithy/util-middleware': 4.2.8
+ '@smithy/util-stream': 4.5.12
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
- '@aws-sdk/middleware-host-header@3.936.0':
+ '@aws-sdk/middleware-host-header@3.972.3':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/middleware-location-constraint@3.936.0':
+ '@aws-sdk/middleware-location-constraint@3.972.3':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/middleware-logger@3.936.0':
+ '@aws-sdk/middleware-logger@3.972.3':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/middleware-recursion-detection@3.936.0':
+ '@aws-sdk/middleware-recursion-detection@3.972.3':
dependencies:
- '@aws-sdk/types': 3.936.0
+ '@aws-sdk/types': 3.973.1
'@aws/lambda-invoke-store': 0.2.2
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/middleware-recursion-detection@3.948.0':
- dependencies:
- '@aws-sdk/types': 3.936.0
- '@aws/lambda-invoke-store': 0.2.2
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/middleware-sdk-s3@3.936.0':
- dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-arn-parser': 3.893.0
- '@smithy/core': 3.18.7
- '@smithy/node-config-provider': 4.3.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/signature-v4': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/util-config-provider': 4.2.0
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-stream': 4.5.6
- '@smithy/util-utf8': 4.2.0
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/middleware-sdk-s3@3.947.0':
+ '@aws-sdk/middleware-sdk-s3@3.972.11':
dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-arn-parser': 3.893.0
- '@smithy/core': 3.18.7
- '@smithy/node-config-provider': 4.3.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/signature-v4': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/types': 3.973.1
+ '@aws-sdk/util-arn-parser': 3.972.2
+ '@smithy/core': 3.23.2
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/signature-v4': 5.3.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
'@smithy/util-config-provider': 4.2.0
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-stream': 4.5.6
+ '@smithy/util-middleware': 4.2.8
+ '@smithy/util-stream': 4.5.12
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
- '@aws-sdk/middleware-ssec@3.936.0':
- dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/middleware-user-agent@3.936.0':
+ '@aws-sdk/middleware-ssec@3.972.3':
dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@smithy/core': 3.18.7
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/middleware-user-agent@3.947.0':
+ '@aws-sdk/middleware-user-agent@3.972.11':
dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@smithy/core': 3.18.7
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/types': 3.973.1
+ '@aws-sdk/util-endpoints': 3.993.0
+ '@smithy/core': 3.23.2
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/nested-clients@3.936.0':
+ '@aws-sdk/nested-clients@3.993.0':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/middleware-host-header': 3.936.0
- '@aws-sdk/middleware-logger': 3.936.0
- '@aws-sdk/middleware-recursion-detection': 3.936.0
- '@aws-sdk/middleware-user-agent': 3.936.0
- '@aws-sdk/region-config-resolver': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@aws-sdk/util-user-agent-browser': 3.936.0
- '@aws-sdk/util-user-agent-node': 3.936.0
- '@smithy/config-resolver': 4.4.3
- '@smithy/core': 3.18.7
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/hash-node': 4.2.5
- '@smithy/invalid-dependency': 4.2.5
- '@smithy/middleware-content-length': 4.2.5
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/middleware-retry': 4.4.14
- '@smithy/middleware-serde': 4.2.6
- '@smithy/middleware-stack': 4.2.5
- '@smithy/node-config-provider': 4.3.5
- '@smithy/node-http-handler': 4.4.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/middleware-host-header': 3.972.3
+ '@aws-sdk/middleware-logger': 3.972.3
+ '@aws-sdk/middleware-recursion-detection': 3.972.3
+ '@aws-sdk/middleware-user-agent': 3.972.11
+ '@aws-sdk/region-config-resolver': 3.972.3
+ '@aws-sdk/types': 3.973.1
+ '@aws-sdk/util-endpoints': 3.993.0
+ '@aws-sdk/util-user-agent-browser': 3.972.3
+ '@aws-sdk/util-user-agent-node': 3.972.10
+ '@smithy/config-resolver': 4.4.6
+ '@smithy/core': 3.23.2
+ '@smithy/fetch-http-handler': 5.3.9
+ '@smithy/hash-node': 4.2.8
+ '@smithy/invalid-dependency': 4.2.8
+ '@smithy/middleware-content-length': 4.2.8
+ '@smithy/middleware-endpoint': 4.4.16
+ '@smithy/middleware-retry': 4.4.33
+ '@smithy/middleware-serde': 4.2.9
+ '@smithy/middleware-stack': 4.2.8
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/node-http-handler': 4.4.10
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
+ '@smithy/url-parser': 4.2.8
'@smithy/util-base64': 4.3.0
'@smithy/util-body-length-browser': 4.2.0
'@smithy/util-body-length-node': 4.2.1
- '@smithy/util-defaults-mode-browser': 4.3.13
- '@smithy/util-defaults-mode-node': 4.2.16
- '@smithy/util-endpoints': 3.2.5
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-retry': 4.2.5
+ '@smithy/util-defaults-mode-browser': 4.3.32
+ '@smithy/util-defaults-mode-node': 4.2.35
+ '@smithy/util-endpoints': 3.2.8
+ '@smithy/util-middleware': 4.2.8
+ '@smithy/util-retry': 4.2.8
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/nested-clients@3.948.0':
+ '@aws-sdk/region-config-resolver@3.972.3':
dependencies:
- '@aws-crypto/sha256-browser': 5.2.0
- '@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/middleware-host-header': 3.936.0
- '@aws-sdk/middleware-logger': 3.936.0
- '@aws-sdk/middleware-recursion-detection': 3.948.0
- '@aws-sdk/middleware-user-agent': 3.947.0
- '@aws-sdk/region-config-resolver': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@aws-sdk/util-endpoints': 3.936.0
- '@aws-sdk/util-user-agent-browser': 3.936.0
- '@aws-sdk/util-user-agent-node': 3.947.0
- '@smithy/config-resolver': 4.4.3
- '@smithy/core': 3.18.7
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/hash-node': 4.2.5
- '@smithy/invalid-dependency': 4.2.5
- '@smithy/middleware-content-length': 4.2.5
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/middleware-retry': 4.4.14
- '@smithy/middleware-serde': 4.2.6
- '@smithy/middleware-stack': 4.2.5
- '@smithy/node-config-provider': 4.3.5
- '@smithy/node-http-handler': 4.4.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
- '@smithy/util-base64': 4.3.0
- '@smithy/util-body-length-browser': 4.2.0
- '@smithy/util-body-length-node': 4.2.1
- '@smithy/util-defaults-mode-browser': 4.3.13
- '@smithy/util-defaults-mode-node': 4.2.16
- '@smithy/util-endpoints': 3.2.5
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-retry': 4.2.5
- '@smithy/util-utf8': 4.2.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/config-resolver': 4.4.6
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
- '@aws-sdk/region-config-resolver@3.936.0':
+ '@aws-sdk/signature-v4-multi-region@3.995.0':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/config-resolver': 4.4.3
- '@smithy/node-config-provider': 4.3.5
- '@smithy/types': 4.9.0
+ '@aws-sdk/middleware-sdk-s3': 3.972.11
+ '@aws-sdk/types': 3.973.1
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/signature-v4': 5.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/signature-v4-multi-region@3.936.0':
+ '@aws-sdk/token-providers@3.993.0':
dependencies:
- '@aws-sdk/middleware-sdk-s3': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/protocol-http': 5.3.5
- '@smithy/signature-v4': 5.3.5
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/signature-v4-multi-region@3.947.0':
- dependencies:
- '@aws-sdk/middleware-sdk-s3': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@smithy/protocol-http': 5.3.5
- '@smithy/signature-v4': 5.3.5
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/token-providers@3.936.0':
- dependencies:
- '@aws-sdk/core': 3.936.0
- '@aws-sdk/nested-clients': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/core': 3.973.11
+ '@aws-sdk/nested-clients': 3.993.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/property-provider': 4.2.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
- '@aws-sdk/token-providers@3.948.0':
+ '@aws-sdk/types@3.973.1':
dependencies:
- '@aws-sdk/core': 3.947.0
- '@aws-sdk/nested-clients': 3.948.0
- '@aws-sdk/types': 3.936.0
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- transitivePeerDependencies:
- - aws-crt
- '@aws-sdk/types@3.936.0':
+ '@aws-sdk/util-arn-parser@3.972.2':
dependencies:
- '@smithy/types': 4.9.0
tslib: 2.8.1
- '@aws-sdk/util-arn-parser@3.893.0':
+ '@aws-sdk/util-endpoints@3.993.0':
dependencies:
+ '@aws-sdk/types': 3.973.1
+ '@smithy/types': 4.12.0
+ '@smithy/url-parser': 4.2.8
+ '@smithy/util-endpoints': 3.2.8
tslib: 2.8.1
- '@aws-sdk/util-endpoints@3.936.0':
+ '@aws-sdk/util-endpoints@3.995.0':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
- '@smithy/util-endpoints': 3.2.5
+ '@aws-sdk/types': 3.973.1
+ '@smithy/types': 4.12.0
+ '@smithy/url-parser': 4.2.8
+ '@smithy/util-endpoints': 3.2.8
tslib: 2.8.1
'@aws-sdk/util-locate-window@3.893.0':
dependencies:
tslib: 2.8.1
- '@aws-sdk/util-user-agent-browser@3.936.0':
+ '@aws-sdk/util-user-agent-browser@3.972.3':
dependencies:
- '@aws-sdk/types': 3.936.0
- '@smithy/types': 4.9.0
+ '@aws-sdk/types': 3.973.1
+ '@smithy/types': 4.12.0
bowser: 2.12.1
tslib: 2.8.1
- '@aws-sdk/util-user-agent-node@3.936.0':
- dependencies:
- '@aws-sdk/middleware-user-agent': 3.936.0
- '@aws-sdk/types': 3.936.0
- '@smithy/node-config-provider': 4.3.5
- '@smithy/types': 4.9.0
- tslib: 2.8.1
-
- '@aws-sdk/util-user-agent-node@3.947.0':
+ '@aws-sdk/util-user-agent-node@3.972.10':
dependencies:
- '@aws-sdk/middleware-user-agent': 3.947.0
- '@aws-sdk/types': 3.936.0
- '@smithy/node-config-provider': 4.3.5
- '@smithy/types': 4.9.0
+ '@aws-sdk/middleware-user-agent': 3.972.11
+ '@aws-sdk/types': 3.973.1
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@aws-sdk/xml-builder@3.930.0':
+ '@aws-sdk/xml-builder@3.972.5':
dependencies:
- '@smithy/types': 4.9.0
- fast-xml-parser: 5.2.5
+ '@smithy/types': 4.12.0
+ fast-xml-parser: 5.3.6
tslib: 2.8.1
'@aws/lambda-invoke-store@0.2.2': {}
@@ -12227,10 +11646,10 @@ snapshots:
'@babel/helper-compilation-targets': 7.27.2
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5)
'@babel/helpers': 7.28.4
- '@babel/parser': 7.28.5
+ '@babel/parser': 7.29.0
'@babel/template': 7.27.2
'@babel/traverse': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
debug: 4.4.3(supports-color@10.2.2)
@@ -12242,8 +11661,8 @@ snapshots:
'@babel/generator@7.28.5':
dependencies:
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
jsesc: 3.1.0
@@ -12261,7 +11680,7 @@ snapshots:
'@babel/helper-module-imports@7.27.1':
dependencies:
'@babel/traverse': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
transitivePeerDependencies:
- supports-color
@@ -12285,11 +11704,11 @@ snapshots:
'@babel/helpers@7.28.4':
dependencies:
'@babel/template': 7.27.2
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
- '@babel/parser@7.28.5':
+ '@babel/parser@7.29.0':
dependencies:
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
'@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)':
dependencies:
@@ -12376,27 +11795,27 @@ snapshots:
'@babel/core': 7.28.5
'@babel/helper-plugin-utils': 7.27.1
- '@babel/runtime@7.28.4': {}
+ '@babel/runtime@7.28.6': {}
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
'@babel/traverse@7.28.5':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.5
'@babel/helper-globals': 7.28.0
- '@babel/parser': 7.28.5
+ '@babel/parser': 7.29.0
'@babel/template': 7.27.2
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies:
- supports-color
- '@babel/types@7.28.5':
+ '@babel/types@7.29.0':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
@@ -12477,35 +11896,35 @@ snapshots:
'@cropper/utils@2.1.0': {}
- '@csstools/color-helpers@5.1.0':
+ '@csstools/color-helpers@6.0.1':
optional: true
- '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ '@csstools/css-calc@3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
dependencies:
- '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
- '@csstools/css-tokenizer': 3.0.4
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
optional: true
- '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ '@csstools/css-color-parser@4.0.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
dependencies:
- '@csstools/color-helpers': 5.1.0
- '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
- '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
- '@csstools/css-tokenizer': 3.0.4
+ '@csstools/color-helpers': 6.0.1
+ '@csstools/css-calc': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
optional: true
- '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+ '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
dependencies:
- '@csstools/css-tokenizer': 3.0.4
+ '@csstools/css-tokenizer': 4.0.0
optional: true
- '@csstools/css-syntax-patches-for-csstree@1.0.16':
+ '@csstools/css-syntax-patches-for-csstree@1.0.26':
optional: true
- '@csstools/css-tokenizer@3.0.4':
+ '@csstools/css-tokenizer@4.0.0':
optional: true
- '@cypress/request@3.0.9':
+ '@cypress/request@3.0.10':
dependencies:
aws-sign2: 0.7.0
aws4: 1.13.2
@@ -12520,7 +11939,7 @@ snapshots:
json-stringify-safe: 5.0.1
mime-types: 2.1.35
performance-now: 2.1.0
- qs: 6.14.0
+ qs: 6.14.1
safe-buffer: 5.2.1
tough-cookie: 5.1.2
tunnel-agent: 0.6.0
@@ -12552,174 +11971,96 @@ snapshots:
'@epic-web/invariant@1.0.0': {}
- '@esbuild/aix-ppc64@0.25.11':
- optional: true
-
- '@esbuild/aix-ppc64@0.27.1':
- optional: true
-
- '@esbuild/android-arm64@0.25.11':
- optional: true
-
- '@esbuild/android-arm64@0.27.1':
- optional: true
-
- '@esbuild/android-arm@0.25.11':
- optional: true
-
- '@esbuild/android-arm@0.27.1':
- optional: true
-
- '@esbuild/android-x64@0.25.11':
- optional: true
-
- '@esbuild/android-x64@0.27.1':
- optional: true
-
- '@esbuild/darwin-arm64@0.25.11':
- optional: true
-
- '@esbuild/darwin-arm64@0.27.1':
- optional: true
-
- '@esbuild/darwin-x64@0.25.11':
- optional: true
-
- '@esbuild/darwin-x64@0.27.1':
- optional: true
-
- '@esbuild/freebsd-arm64@0.25.11':
- optional: true
-
- '@esbuild/freebsd-arm64@0.27.1':
- optional: true
-
- '@esbuild/freebsd-x64@0.25.11':
- optional: true
-
- '@esbuild/freebsd-x64@0.27.1':
- optional: true
-
- '@esbuild/linux-arm64@0.25.11':
- optional: true
-
- '@esbuild/linux-arm64@0.27.1':
- optional: true
-
- '@esbuild/linux-arm@0.25.11':
- optional: true
-
- '@esbuild/linux-arm@0.27.1':
- optional: true
-
- '@esbuild/linux-ia32@0.25.11':
+ '@esbuild/aix-ppc64@0.27.3':
optional: true
- '@esbuild/linux-ia32@0.27.1':
+ '@esbuild/android-arm64@0.27.3':
optional: true
- '@esbuild/linux-loong64@0.25.11':
+ '@esbuild/android-arm@0.27.3':
optional: true
- '@esbuild/linux-loong64@0.27.1':
+ '@esbuild/android-x64@0.27.3':
optional: true
- '@esbuild/linux-mips64el@0.25.11':
+ '@esbuild/darwin-arm64@0.27.3':
optional: true
- '@esbuild/linux-mips64el@0.27.1':
+ '@esbuild/darwin-x64@0.27.3':
optional: true
- '@esbuild/linux-ppc64@0.25.11':
+ '@esbuild/freebsd-arm64@0.27.3':
optional: true
- '@esbuild/linux-ppc64@0.27.1':
+ '@esbuild/freebsd-x64@0.27.3':
optional: true
- '@esbuild/linux-riscv64@0.25.11':
+ '@esbuild/linux-arm64@0.27.3':
optional: true
- '@esbuild/linux-riscv64@0.27.1':
+ '@esbuild/linux-arm@0.27.3':
optional: true
- '@esbuild/linux-s390x@0.25.11':
+ '@esbuild/linux-ia32@0.27.3':
optional: true
- '@esbuild/linux-s390x@0.27.1':
+ '@esbuild/linux-loong64@0.27.3':
optional: true
- '@esbuild/linux-x64@0.25.11':
+ '@esbuild/linux-mips64el@0.27.3':
optional: true
- '@esbuild/linux-x64@0.27.1':
+ '@esbuild/linux-ppc64@0.27.3':
optional: true
- '@esbuild/netbsd-arm64@0.25.11':
+ '@esbuild/linux-riscv64@0.27.3':
optional: true
- '@esbuild/netbsd-arm64@0.27.1':
+ '@esbuild/linux-s390x@0.27.3':
optional: true
- '@esbuild/netbsd-x64@0.25.11':
+ '@esbuild/linux-x64@0.27.3':
optional: true
- '@esbuild/netbsd-x64@0.27.1':
+ '@esbuild/netbsd-arm64@0.27.3':
optional: true
- '@esbuild/openbsd-arm64@0.25.11':
+ '@esbuild/netbsd-x64@0.27.3':
optional: true
- '@esbuild/openbsd-arm64@0.27.1':
+ '@esbuild/openbsd-arm64@0.27.3':
optional: true
- '@esbuild/openbsd-x64@0.25.11':
+ '@esbuild/openbsd-x64@0.27.3':
optional: true
- '@esbuild/openbsd-x64@0.27.1':
+ '@esbuild/openharmony-arm64@0.27.3':
optional: true
- '@esbuild/openharmony-arm64@0.25.11':
+ '@esbuild/sunos-x64@0.27.3':
optional: true
- '@esbuild/openharmony-arm64@0.27.1':
+ '@esbuild/win32-arm64@0.27.3':
optional: true
- '@esbuild/sunos-x64@0.25.11':
+ '@esbuild/win32-ia32@0.27.3':
optional: true
- '@esbuild/sunos-x64@0.27.1':
+ '@esbuild/win32-x64@0.27.3':
optional: true
- '@esbuild/win32-arm64@0.25.11':
- optional: true
-
- '@esbuild/win32-arm64@0.27.1':
- optional: true
-
- '@esbuild/win32-ia32@0.25.11':
- optional: true
-
- '@esbuild/win32-ia32@0.27.1':
- optional: true
-
- '@esbuild/win32-x64@0.25.11':
- optional: true
-
- '@esbuild/win32-x64@0.27.1':
- optional: true
-
- '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)':
+ '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3)':
dependencies:
- eslint: 9.39.1
+ eslint: 9.39.3
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
- '@eslint/compat@1.4.0(eslint@9.39.1)':
+ '@eslint/compat@1.4.0(eslint@9.39.3)':
dependencies:
'@eslint/core': 0.16.0
optionalDependencies:
- eslint: 9.39.1
+ eslint: 9.39.3
'@eslint/config-array@0.21.1':
dependencies:
@@ -12755,7 +12096,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/js@9.39.1': {}
+ '@eslint/js@9.39.3': {}
'@eslint/object-schema@2.1.7': {}
@@ -12773,8 +12114,8 @@ snapshots:
'@fastify/ajv-compiler@4.0.5':
dependencies:
- ajv: 8.17.1
- ajv-formats: 3.0.1(ajv@8.17.1)
+ ajv: 8.18.0
+ ajv-formats: 3.0.1(ajv@8.18.0)
fast-uri: 3.1.0
'@fastify/busboy@3.2.0': {}
@@ -12788,7 +12129,7 @@ snapshots:
'@fastify/error@4.2.0': {}
- '@fastify/express@4.0.2':
+ '@fastify/express@4.0.4':
dependencies:
express: 4.21.2
fastify-plugin: 5.1.0
@@ -12801,12 +12142,12 @@ snapshots:
'@fastify/forwarded@3.0.1': {}
- '@fastify/http-proxy@11.4.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
+ '@fastify/http-proxy@11.4.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)':
dependencies:
'@fastify/reply-from': 12.5.0
fast-querystring: 1.1.2
fastify-plugin: 5.1.0
- ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -12815,7 +12156,7 @@ snapshots:
dependencies:
dequal: 2.0.3
- '@fastify/multipart@9.3.0':
+ '@fastify/multipart@9.4.0':
dependencies:
'@fastify/busboy': 3.2.0
'@fastify/deepmerge': 3.1.0
@@ -12843,17 +12184,17 @@ snapshots:
'@lukeed/ms': 2.0.2
escape-html: 1.0.3
fast-decode-uri-component: 1.0.1
- http-errors: 2.0.0
+ http-errors: 2.0.1
mime: 3.0.0
- '@fastify/static@8.3.0':
+ '@fastify/static@9.0.0':
dependencies:
'@fastify/accept-negotiator': 2.0.1
'@fastify/send': 4.1.0
- content-disposition: 0.5.4
+ content-disposition: 1.0.1
fastify-plugin: 5.1.0
- fastq: 1.19.1
- glob: 11.1.0
+ fastq: 1.20.1
+ glob: 13.0.1
'@file-type/xml@0.4.4':
dependencies:
@@ -12988,39 +12329,33 @@ snapshots:
'@inquirer/ansi@1.0.2': {}
- '@inquirer/confirm@5.1.21(@types/node@24.10.2)':
+ '@inquirer/confirm@5.1.21(@types/node@24.10.13)':
dependencies:
- '@inquirer/core': 10.3.2(@types/node@24.10.2)
- '@inquirer/type': 3.0.10(@types/node@24.10.2)
+ '@inquirer/core': 10.3.2(@types/node@24.10.13)
+ '@inquirer/type': 3.0.10(@types/node@24.10.13)
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
- '@inquirer/core@10.3.2(@types/node@24.10.2)':
+ '@inquirer/core@10.3.2(@types/node@24.10.13)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/figures': 1.0.15
- '@inquirer/type': 3.0.10(@types/node@24.10.2)
+ '@inquirer/type': 3.0.10(@types/node@24.10.13)
cli-width: 4.1.0
mute-stream: 2.0.0
signal-exit: 4.1.0
wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.3
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@inquirer/figures@1.0.15': {}
- '@inquirer/type@3.0.10(@types/node@24.10.2)':
+ '@inquirer/type@3.0.10(@types/node@24.10.13)':
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
- '@ioredis/commands@1.4.0': {}
-
- '@isaacs/balanced-match@4.0.1': {}
-
- '@isaacs/brace-expansion@5.0.0':
- dependencies:
- '@isaacs/balanced-match': 4.0.1
+ '@ioredis/commands@1.5.0': {}
'@isaacs/cliui@8.0.2':
dependencies:
@@ -13031,6 +12366,8 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
+ '@isaacs/cliui@9.0.0': {}
+
'@isaacs/fs-minipass@4.0.1':
dependencies:
minipass: 7.1.2
@@ -13048,7 +12385,7 @@ snapshots:
'@jest/console@29.7.0':
dependencies:
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
chalk: 4.1.2
jest-message-util: 29.7.0
jest-util: 29.7.0
@@ -13061,14 +12398,14 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.9.0
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
- jest-config: 29.7.0(@types/node@24.10.2)
+ jest-config: 29.7.0(@types/node@24.10.13)
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@@ -13097,7 +12434,7 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
jest-mock: 29.7.0
'@jest/expect-utils@29.7.0':
@@ -13115,7 +12452,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -13131,7 +12468,7 @@ snapshots:
'@jest/pattern@30.0.1':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
jest-regex-util: 30.0.1
'@jest/reporters@29.7.0':
@@ -13142,7 +12479,7 @@ snapshots:
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.31
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
chalk: 4.1.2
collect-v8-coverage: 1.0.3
exit: 0.1.2
@@ -13216,7 +12553,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/yargs': 17.0.34
chalk: 4.1.2
@@ -13226,16 +12563,15 @@ snapshots:
'@jest/schemas': 30.0.5
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/yargs': 17.0.34
chalk: 4.1.2
- '@joshwooding/vite-plugin-react-docgen-typescript@0.6.1(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))':
+ '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))':
dependencies:
- glob: 10.5.0
- magic-string: 0.30.21
+ glob: 13.0.1
react-docgen-typescript: 2.4.0(typescript@5.9.3)
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
optionalDependencies:
typescript: 5.9.3
@@ -13265,13 +12601,13 @@ snapshots:
'@keyv/serialize@1.1.1': {}
- '@kitajs/html@4.2.11':
+ '@kitajs/html@4.2.13':
dependencies:
csstype: 3.2.3
- '@kitajs/ts-html-plugin@4.1.3(@kitajs/html@4.2.11)(typescript@5.9.3)':
+ '@kitajs/ts-html-plugin@4.1.4(@kitajs/html@4.2.13)(typescript@5.9.3)':
dependencies:
- '@kitajs/html': 4.2.11
+ '@kitajs/html': 4.2.13
chalk: 5.6.2
tslib: 2.8.1
typescript: 5.9.3
@@ -13294,51 +12630,41 @@ snapshots:
nopt: 5.0.0
npmlog: 5.0.1
rimraf: 3.0.2
- semver: 7.7.3
+ semver: 7.7.4
tar: 6.2.1
transitivePeerDependencies:
- encoding
- supports-color
optional: true
- '@mcaptcha/core-glue@0.1.0-rc1': {}
-
- '@mcaptcha/vanilla-glue@0.1.0-rc2(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
- dependencies:
- '@mcaptcha/core-glue': 0.1.0-rc1
- jest-environment-jsdom: 29.7.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
- transitivePeerDependencies:
- - bufferutil
- - canvas
- - supports-color
- - utf-8-validate
+ '@mcaptcha/core-glue@0.1.0-alpha-5': {}
- '@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.1)':
+ '@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.4)':
dependencies:
'@types/mdx': 2.0.13
'@types/react': 19.2.2
- react: 19.2.1
+ react: 19.2.4
- '@microsoft/api-extractor-model@7.32.2(@types/node@24.10.2)':
+ '@microsoft/api-extractor-model@7.33.1(@types/node@24.10.13)':
dependencies:
'@microsoft/tsdoc': 0.16.0
'@microsoft/tsdoc-config': 0.18.0
- '@rushstack/node-core-library': 5.19.1(@types/node@24.10.2)
+ '@rushstack/node-core-library': 5.20.1(@types/node@24.10.13)
transitivePeerDependencies:
- '@types/node'
- '@microsoft/api-extractor@7.55.2(@types/node@24.10.2)':
+ '@microsoft/api-extractor@7.57.2(@types/node@24.10.13)':
dependencies:
- '@microsoft/api-extractor-model': 7.32.2(@types/node@24.10.2)
+ '@microsoft/api-extractor-model': 7.33.1(@types/node@24.10.13)
'@microsoft/tsdoc': 0.16.0
'@microsoft/tsdoc-config': 0.18.0
- '@rushstack/node-core-library': 5.19.1(@types/node@24.10.2)
- '@rushstack/rig-package': 0.6.0
- '@rushstack/terminal': 0.19.5(@types/node@24.10.2)
- '@rushstack/ts-command-line': 5.1.5(@types/node@24.10.2)
+ '@rushstack/node-core-library': 5.20.1(@types/node@24.10.13)
+ '@rushstack/rig-package': 0.7.1
+ '@rushstack/terminal': 0.22.1(@types/node@24.10.13)
+ '@rushstack/ts-command-line': 5.3.1(@types/node@24.10.13)
diff: 8.0.2
- lodash: 4.17.21
- minimatch: 10.0.3
+ lodash: 4.17.23
+ minimatch: 10.2.1
resolve: 1.22.11
semver: 7.5.4
source-map: 0.6.1
@@ -13357,16 +12683,15 @@ snapshots:
'@misskey-dev/browser-image-resizer@2024.1.0': {}
- '@misskey-dev/eslint-plugin@2.2.0(@eslint/compat@1.4.0(eslint@9.39.1))(@eslint/js@9.39.1)(@stylistic/eslint-plugin@5.5.0(eslint@9.39.1))(@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3))(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1))(eslint@9.39.1)(globals@16.5.0)':
+ '@misskey-dev/eslint-plugin@2.1.0(@eslint/compat@1.4.0(eslint@9.39.3))(@stylistic/eslint-plugin@5.5.0(eslint@9.39.3))(@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3))(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3))(eslint@9.39.3)(globals@17.3.0)':
dependencies:
- '@eslint/compat': 1.4.0(eslint@9.39.1)
- '@eslint/js': 9.39.1
- '@stylistic/eslint-plugin': 5.5.0(eslint@9.39.1)
- '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)
- '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
- eslint: 9.39.1
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)
- globals: 16.5.0
+ '@eslint/compat': 1.4.0(eslint@9.39.3)
+ '@stylistic/eslint-plugin': 5.5.0(eslint@9.39.3)
+ '@typescript-eslint/eslint-plugin': 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
+ eslint: 9.39.3
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)
+ globals: 17.3.0
'@misskey-dev/sharp-read-bmp@1.2.0':
dependencies:
@@ -13378,7 +12703,7 @@ snapshots:
dependencies:
cheerio: 1.1.2
escape-regexp: 0.0.1
- got: 14.6.5
+ got: 14.6.6
html-entities: 2.6.0
iconv-lite: 0.7.0
jschardet: 3.1.4
@@ -13402,7 +12727,7 @@ snapshots:
'@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3':
optional: true
- '@mswjs/interceptors@0.40.0':
+ '@mswjs/interceptors@0.41.2':
dependencies:
'@open-draft/deferred-promise': 2.2.0
'@open-draft/logger': 0.3.0
@@ -13411,48 +12736,52 @@ snapshots:
outvariant: 1.4.3
strict-event-emitter: 0.5.1
- '@napi-rs/canvas-android-arm64@0.1.84':
+ '@napi-rs/canvas-android-arm64@0.1.94':
+ optional: true
+
+ '@napi-rs/canvas-darwin-arm64@0.1.94':
optional: true
- '@napi-rs/canvas-darwin-arm64@0.1.84':
+ '@napi-rs/canvas-darwin-x64@0.1.94':
optional: true
- '@napi-rs/canvas-darwin-x64@0.1.84':
+ '@napi-rs/canvas-linux-arm-gnueabihf@0.1.94':
optional: true
- '@napi-rs/canvas-linux-arm-gnueabihf@0.1.84':
+ '@napi-rs/canvas-linux-arm64-gnu@0.1.94':
optional: true
- '@napi-rs/canvas-linux-arm64-gnu@0.1.84':
+ '@napi-rs/canvas-linux-arm64-musl@0.1.94':
optional: true
- '@napi-rs/canvas-linux-arm64-musl@0.1.84':
+ '@napi-rs/canvas-linux-riscv64-gnu@0.1.94':
optional: true
- '@napi-rs/canvas-linux-riscv64-gnu@0.1.84':
+ '@napi-rs/canvas-linux-x64-gnu@0.1.94':
optional: true
- '@napi-rs/canvas-linux-x64-gnu@0.1.84':
+ '@napi-rs/canvas-linux-x64-musl@0.1.94':
optional: true
- '@napi-rs/canvas-linux-x64-musl@0.1.84':
+ '@napi-rs/canvas-win32-arm64-msvc@0.1.94':
optional: true
- '@napi-rs/canvas-win32-x64-msvc@0.1.84':
+ '@napi-rs/canvas-win32-x64-msvc@0.1.94':
optional: true
- '@napi-rs/canvas@0.1.84':
+ '@napi-rs/canvas@0.1.94':
optionalDependencies:
- '@napi-rs/canvas-android-arm64': 0.1.84
- '@napi-rs/canvas-darwin-arm64': 0.1.84
- '@napi-rs/canvas-darwin-x64': 0.1.84
- '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.84
- '@napi-rs/canvas-linux-arm64-gnu': 0.1.84
- '@napi-rs/canvas-linux-arm64-musl': 0.1.84
- '@napi-rs/canvas-linux-riscv64-gnu': 0.1.84
- '@napi-rs/canvas-linux-x64-gnu': 0.1.84
- '@napi-rs/canvas-linux-x64-musl': 0.1.84
- '@napi-rs/canvas-win32-x64-msvc': 0.1.84
+ '@napi-rs/canvas-android-arm64': 0.1.94
+ '@napi-rs/canvas-darwin-arm64': 0.1.94
+ '@napi-rs/canvas-darwin-x64': 0.1.94
+ '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.94
+ '@napi-rs/canvas-linux-arm64-gnu': 0.1.94
+ '@napi-rs/canvas-linux-arm64-musl': 0.1.94
+ '@napi-rs/canvas-linux-riscv64-gnu': 0.1.94
+ '@napi-rs/canvas-linux-x64-gnu': 0.1.94
+ '@napi-rs/canvas-linux-x64-musl': 0.1.94
+ '@napi-rs/canvas-win32-arm64-msvc': 0.1.94
+ '@napi-rs/canvas-win32-x64-msvc': 0.1.94
'@napi-rs/nice-android-arm-eabi@1.1.1':
optional: true
@@ -13526,9 +12855,9 @@ snapshots:
'@napi-rs/nice-win32-x64-msvc': 1.1.1
optional: true
- '@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2)':
+ '@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
- file-type: 21.1.0
+ file-type: 21.3.0
iterare: 1.2.1
load-esm: 1.0.3
reflect-metadata: 0.2.2
@@ -13538,9 +12867,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@nestjs/core@11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
+ '@nestjs/core@11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
- '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/common': 11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nuxt/opencollective': 0.4.1
fast-safe-stringify: 2.1.1
iterare: 1.2.1
@@ -13550,30 +12879,32 @@ snapshots:
tslib: 2.8.1
uid: 2.0.2
optionalDependencies:
- '@nestjs/platform-express': 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)
+ '@nestjs/platform-express': 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)
- '@nestjs/platform-express@11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)':
+ '@nestjs/platform-express@11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)':
dependencies:
- '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2)
- '@nestjs/core': 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
- cors: 2.8.5
- express: 5.1.0
+ '@nestjs/common': 11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ cors: 2.8.6
+ express: 5.2.1
multer: 2.0.2
path-to-regexp: 8.3.0
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
- '@nestjs/testing@11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/platform-express@11.1.9)':
+ '@nestjs/testing@11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-express@11.1.14)':
dependencies:
- '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2)
- '@nestjs/core': 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/common': 11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2)
+ '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)
tslib: 2.8.1
optionalDependencies:
- '@nestjs/platform-express': 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)
+ '@nestjs/platform-express': 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)
'@noble/hashes@1.8.0': {}
+ '@noble/hashes@2.0.1': {}
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -13584,21 +12915,21 @@ snapshots:
'@nodelib/fs.walk@1.2.8':
dependencies:
'@nodelib/fs.scandir': 2.1.5
- fastq: 1.19.1
+ fastq: 1.20.1
- '@npmcli/agent@3.0.0':
+ '@npmcli/agent@4.0.0':
dependencies:
agent-base: 7.1.4
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6(supports-color@10.2.2)
- lru-cache: 10.4.3
+ lru-cache: 11.2.5
socks-proxy-agent: 8.0.5
transitivePeerDependencies:
- supports-color
- '@npmcli/fs@4.0.0':
+ '@npmcli/fs@5.0.0':
dependencies:
- semver: 7.7.3
+ semver: 7.7.4
'@nuxt/opencollective@0.4.1':
dependencies:
@@ -13615,236 +12946,255 @@ snapshots:
'@open-draft/until@2.1.0': {}
- '@opentelemetry/api-logs@0.208.0':
+ '@opentelemetry/api-logs@0.207.0':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/api-logs@0.211.0':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/api@1.9.0': {}
- '@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/semantic-conventions': 1.39.0
- '@opentelemetry/instrumentation-amqplib@0.55.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-connect@0.52.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
'@types/connect': 3.4.38
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-dataloader@0.26.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-dataloader@0.28.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-express@0.57.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-fs@0.28.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-generic-pool@0.52.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-generic-pool@0.54.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-graphql@0.56.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-graphql@0.58.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-hapi@0.55.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-http@0.208.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-http@0.211.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
forwarded-parse: 2.1.2
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-ioredis@0.56.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-ioredis@0.59.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
'@opentelemetry/redis-common': 0.38.2
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-kafkajs@0.18.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-kafkajs@0.20.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-knex@0.53.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-knex@0.55.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-koa@0.57.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-lru-memoizer@0.53.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-lru-memoizer@0.55.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-mongodb@0.61.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-mongodb@0.64.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-mongoose@0.55.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-mysql2@0.55.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-mysql2@0.57.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
'@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-mysql@0.54.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-mysql@0.57.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
'@types/mysql': 2.15.27
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-pg@0.61.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
'@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
'@types/pg': 8.15.6
- '@types/pg-pool': 2.0.6
+ '@types/pg-pool': 2.0.7
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-redis@0.57.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-redis@0.59.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
'@opentelemetry/redis-common': 0.38.2
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-tedious@0.27.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-tedious@0.30.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
'@types/tedious': 4.0.14
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation-undici@0.19.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/instrumentation@0.207.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/api-logs': 0.208.0
- import-in-the-middle: 2.0.0
+ '@opentelemetry/api-logs': 0.207.0
+ import-in-the-middle: 2.0.6
+ require-in-the-middle: 8.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ import-in-the-middle: 2.0.6
require-in-the-middle: 8.0.1
transitivePeerDependencies:
- supports-color
'@opentelemetry/redis-common@0.38.2': {}
- '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
- '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)':
+ '@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
- '@opentelemetry/semantic-conventions@1.38.0': {}
+ '@opentelemetry/semantic-conventions@1.39.0': {}
'@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
'@paralleldrive/cuid2@2.3.1':
dependencies:
@@ -14018,31 +13368,31 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
- '@prisma/instrumentation@6.19.0(@opentelemetry/api@1.9.0)':
+ '@prisma/instrumentation@7.2.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.207.0(@opentelemetry/api@1.9.0)
transitivePeerDependencies:
- supports-color
- '@readme/better-ajv-errors@2.4.0(ajv@8.17.1)':
+ '@readme/better-ajv-errors@2.4.0(ajv@8.18.0)':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
'@humanwhocodes/momoa': 2.0.4
- ajv: 8.17.1
+ ajv: 8.18.0
jsonpointer: 5.0.1
leven: 3.1.0
picocolors: 1.1.1
- '@readme/openapi-parser@5.2.1(openapi-types@12.1.3)':
+ '@readme/openapi-parser@5.5.0(openapi-types@12.1.3)':
dependencies:
'@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15)
- '@readme/better-ajv-errors': 2.4.0(ajv@8.17.1)
+ '@readme/better-ajv-errors': 2.4.0(ajv@8.18.0)
'@readme/openapi-schemas': 3.1.0
'@types/json-schema': 7.0.15
- ajv: 8.17.1
- ajv-draft-04: 1.0.0(ajv@8.17.1)
+ ajv: 8.18.0
+ ajv-draft-04: 1.0.0(ajv@8.18.0)
openapi-types: 12.1.3
'@readme/openapi-schemas@3.1.0': {}
@@ -14056,7 +13406,7 @@ snapshots:
'@redocly/config@0.22.2': {}
- '@redocly/openapi-core@1.34.5(supports-color@10.2.2)':
+ '@redocly/openapi-core@1.34.6(supports-color@10.2.2)':
dependencies:
'@redocly/ajv': 8.17.1
'@redocly/config': 0.22.2
@@ -14070,98 +13420,107 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@rolldown/pluginutils@1.0.0-beta.50': {}
+ '@rolldown/pluginutils@1.0.0-rc.2': {}
- '@rollup/plugin-json@6.1.0(rollup@4.53.3)':
+ '@rollup/plugin-json@6.1.0(rollup@4.59.0)':
dependencies:
- '@rollup/pluginutils': 5.3.0(rollup@4.53.3)
+ '@rollup/pluginutils': 5.3.0(rollup@4.59.0)
optionalDependencies:
- rollup: 4.53.3
+ rollup: 4.59.0
- '@rollup/plugin-replace@6.0.3(rollup@4.53.3)':
+ '@rollup/plugin-replace@6.0.3(rollup@4.59.0)':
dependencies:
- '@rollup/pluginutils': 5.3.0(rollup@4.53.3)
+ '@rollup/pluginutils': 5.3.0(rollup@4.59.0)
magic-string: 0.30.21
optionalDependencies:
- rollup: 4.53.3
+ rollup: 4.59.0
- '@rollup/pluginutils@5.3.0(rollup@4.53.3)':
+ '@rollup/pluginutils@5.3.0(rollup@4.59.0)':
dependencies:
'@types/estree': 1.0.8
estree-walker: 2.0.2
picomatch: 4.0.3
optionalDependencies:
- rollup: 4.53.3
+ rollup: 4.59.0
+
+ '@rollup/rollup-android-arm-eabi@4.59.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.59.0':
+ optional: true
- '@rollup/rollup-android-arm-eabi@4.53.3':
+ '@rollup/rollup-darwin-arm64@4.59.0':
optional: true
- '@rollup/rollup-android-arm64@4.53.3':
+ '@rollup/rollup-darwin-x64@4.59.0':
optional: true
- '@rollup/rollup-darwin-arm64@4.53.3':
+ '@rollup/rollup-freebsd-arm64@4.59.0':
optional: true
- '@rollup/rollup-darwin-x64@4.53.3':
+ '@rollup/rollup-freebsd-x64@4.59.0':
optional: true
- '@rollup/rollup-freebsd-arm64@4.53.3':
+ '@rollup/rollup-linux-arm-gnueabihf@4.59.0':
optional: true
- '@rollup/rollup-freebsd-x64@4.53.3':
+ '@rollup/rollup-linux-arm-musleabihf@4.59.0':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.53.3':
+ '@rollup/rollup-linux-arm64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.53.3':
+ '@rollup/rollup-linux-arm64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.53.3':
+ '@rollup/rollup-linux-loong64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.53.3':
+ '@rollup/rollup-linux-loong64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-loong64-gnu@4.53.3':
+ '@rollup/rollup-linux-ppc64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-ppc64-gnu@4.53.3':
+ '@rollup/rollup-linux-ppc64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.53.3':
+ '@rollup/rollup-linux-riscv64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.53.3':
+ '@rollup/rollup-linux-riscv64-musl@4.59.0':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.53.3':
+ '@rollup/rollup-linux-s390x-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.53.3':
+ '@rollup/rollup-linux-x64-gnu@4.59.0':
optional: true
- '@rollup/rollup-linux-x64-musl@4.53.3':
+ '@rollup/rollup-linux-x64-musl@4.59.0':
optional: true
- '@rollup/rollup-openharmony-arm64@4.53.3':
+ '@rollup/rollup-openbsd-x64@4.59.0':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.53.3':
+ '@rollup/rollup-openharmony-arm64@4.59.0':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.53.3':
+ '@rollup/rollup-win32-arm64-msvc@4.59.0':
optional: true
- '@rollup/rollup-win32-x64-gnu@4.53.3':
+ '@rollup/rollup-win32-ia32-msvc@4.59.0':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.53.3':
+ '@rollup/rollup-win32-x64-gnu@4.59.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.59.0':
optional: true
'@rtsao/scc@1.1.0': {}
- '@rushstack/node-core-library@5.19.1(@types/node@24.10.2)':
+ '@rushstack/node-core-library@5.20.1(@types/node@24.10.13)':
dependencies:
ajv: 8.13.0
ajv-draft-04: 1.0.0(ajv@8.13.0)
@@ -14172,28 +13531,28 @@ snapshots:
resolve: 1.22.11
semver: 7.5.4
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
- '@rushstack/problem-matcher@0.1.1(@types/node@24.10.2)':
+ '@rushstack/problem-matcher@0.2.1(@types/node@24.10.13)':
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
- '@rushstack/rig-package@0.6.0':
+ '@rushstack/rig-package@0.7.1':
dependencies:
resolve: 1.22.11
strip-json-comments: 3.1.1
- '@rushstack/terminal@0.19.5(@types/node@24.10.2)':
+ '@rushstack/terminal@0.22.1(@types/node@24.10.13)':
dependencies:
- '@rushstack/node-core-library': 5.19.1(@types/node@24.10.2)
- '@rushstack/problem-matcher': 0.1.1(@types/node@24.10.2)
+ '@rushstack/node-core-library': 5.20.1(@types/node@24.10.13)
+ '@rushstack/problem-matcher': 0.2.1(@types/node@24.10.13)
supports-color: 8.1.1
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
- '@rushstack/ts-command-line@5.1.5(@types/node@24.10.2)':
+ '@rushstack/ts-command-line@5.3.1(@types/node@24.10.13)':
dependencies:
- '@rushstack/terminal': 0.19.5(@types/node@24.10.2)
+ '@rushstack/terminal': 0.22.1(@types/node@24.10.13)
'@types/argparse': 1.0.38
argparse: 1.0.10
string-argv: 0.3.2
@@ -14202,145 +13561,146 @@ snapshots:
'@sec-ant/readable-stream@0.4.1': {}
- '@sentry-internal/browser-utils@10.29.0':
+ '@sentry-internal/browser-utils@10.39.0':
dependencies:
- '@sentry/core': 10.29.0
+ '@sentry/core': 10.39.0
- '@sentry-internal/feedback@10.29.0':
+ '@sentry-internal/feedback@10.39.0':
dependencies:
- '@sentry/core': 10.29.0
+ '@sentry/core': 10.39.0
'@sentry-internal/node-cpu-profiler@2.2.0':
dependencies:
detect-libc: 2.1.2
node-abi: 3.85.0
- '@sentry-internal/replay-canvas@10.29.0':
+ '@sentry-internal/replay-canvas@10.39.0':
dependencies:
- '@sentry-internal/replay': 10.29.0
- '@sentry/core': 10.29.0
+ '@sentry-internal/replay': 10.39.0
+ '@sentry/core': 10.39.0
- '@sentry-internal/replay@10.29.0':
+ '@sentry-internal/replay@10.39.0':
dependencies:
- '@sentry-internal/browser-utils': 10.29.0
- '@sentry/core': 10.29.0
+ '@sentry-internal/browser-utils': 10.39.0
+ '@sentry/core': 10.39.0
- '@sentry/browser@10.29.0':
+ '@sentry/browser@10.39.0':
dependencies:
- '@sentry-internal/browser-utils': 10.29.0
- '@sentry-internal/feedback': 10.29.0
- '@sentry-internal/replay': 10.29.0
- '@sentry-internal/replay-canvas': 10.29.0
- '@sentry/core': 10.29.0
+ '@sentry-internal/browser-utils': 10.39.0
+ '@sentry-internal/feedback': 10.39.0
+ '@sentry-internal/replay': 10.39.0
+ '@sentry-internal/replay-canvas': 10.39.0
+ '@sentry/core': 10.39.0
- '@sentry/core@10.29.0': {}
+ '@sentry/core@10.39.0': {}
- '@sentry/node-core@10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)':
+ '@sentry/node-core@10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)':
dependencies:
'@apm-js-collab/tracing-hooks': 0.3.1
+ '@sentry/core': 10.39.0
+ '@sentry/opentelemetry': 10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)
+ import-in-the-middle: 2.0.6
+ optionalDependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
- '@sentry/core': 10.29.0
- '@sentry/opentelemetry': 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)
- import-in-the-middle: 2.0.0
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
transitivePeerDependencies:
- supports-color
- '@sentry/node@10.29.0':
+ '@sentry/node@10.39.0':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-amqplib': 0.55.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-connect': 0.52.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-dataloader': 0.26.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-express': 0.57.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-fs': 0.28.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-generic-pool': 0.52.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-graphql': 0.56.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-hapi': 0.55.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-http': 0.208.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-ioredis': 0.56.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-kafkajs': 0.18.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-knex': 0.53.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-koa': 0.57.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-lru-memoizer': 0.53.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-mongodb': 0.61.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-mongoose': 0.55.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-mysql': 0.54.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-mysql2': 0.55.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-pg': 0.61.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-redis': 0.57.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-tedious': 0.27.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation-undici': 0.19.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
- '@prisma/instrumentation': 6.19.0(@opentelemetry/api@1.9.0)
- '@sentry/core': 10.29.0
- '@sentry/node-core': 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)
- '@sentry/opentelemetry': 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)
- import-in-the-middle: 2.0.0
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-dataloader': 0.28.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-express': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-fs': 0.30.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-generic-pool': 0.54.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-graphql': 0.58.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-hapi': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-http': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-ioredis': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-kafkajs': 0.20.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-knex': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-koa': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-lru-memoizer': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mongodb': 0.64.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mongoose': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mysql': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mysql2': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-pg': 0.63.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@prisma/instrumentation': 7.2.0(@opentelemetry/api@1.9.0)
+ '@sentry/core': 10.39.0
+ '@sentry/node-core': 10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)
+ '@sentry/opentelemetry': 10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)
+ import-in-the-middle: 2.0.6
minimatch: 9.0.5
transitivePeerDependencies:
- supports-color
- '@sentry/opentelemetry@10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)':
+ '@sentry/opentelemetry@10.39.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)':
dependencies:
'@opentelemetry/api': 1.9.0
- '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.38.0
- '@sentry/core': 10.29.0
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@sentry/core': 10.39.0
- '@sentry/profiling-node@10.29.0':
+ '@sentry/profiling-node@10.39.0':
dependencies:
'@sentry-internal/node-cpu-profiler': 2.2.0
- '@sentry/core': 10.29.0
- '@sentry/node': 10.29.0
+ '@sentry/core': 10.39.0
+ '@sentry/node': 10.39.0
transitivePeerDependencies:
- supports-color
- '@sentry/vue@10.29.0(vue@3.5.25(typescript@5.9.3))':
+ '@sentry/vue@10.39.0(vue@3.5.28(typescript@5.9.3))':
dependencies:
- '@sentry/browser': 10.29.0
- '@sentry/core': 10.29.0
- vue: 3.5.25(typescript@5.9.3)
+ '@sentry/browser': 10.39.0
+ '@sentry/core': 10.39.0
+ vue: 3.5.28(typescript@5.9.3)
- '@shikijs/core@3.19.0':
+ '@shikijs/core@3.22.0':
dependencies:
- '@shikijs/types': 3.19.0
+ '@shikijs/types': 3.22.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
- '@shikijs/engine-javascript@3.19.0':
+ '@shikijs/engine-javascript@3.22.0':
dependencies:
- '@shikijs/types': 3.19.0
+ '@shikijs/types': 3.22.0
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 4.3.4
- '@shikijs/engine-oniguruma@3.19.0':
+ '@shikijs/engine-oniguruma@3.22.0':
dependencies:
- '@shikijs/types': 3.19.0
+ '@shikijs/types': 3.22.0
'@shikijs/vscode-textmate': 10.0.2
- '@shikijs/langs@3.19.0':
+ '@shikijs/langs@3.22.0':
dependencies:
- '@shikijs/types': 3.19.0
+ '@shikijs/types': 3.22.0
- '@shikijs/themes@3.19.0':
+ '@shikijs/themes@3.22.0':
dependencies:
- '@shikijs/types': 3.19.0
+ '@shikijs/types': 3.22.0
- '@shikijs/types@3.19.0':
+ '@shikijs/types@3.22.0':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
@@ -14394,7 +13754,7 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
- '@sinonjs/fake-timers@15.0.0':
+ '@sinonjs/fake-timers@15.1.0':
dependencies:
'@sinonjs/commons': 3.0.1
@@ -14405,9 +13765,9 @@ snapshots:
'@sinonjs/text-encoding@0.7.3': {}
- '@smithy/abort-controller@4.2.5':
+ '@smithy/abort-controller@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
'@smithy/chunked-blob-reader-native@4.2.1':
@@ -14419,97 +13779,97 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/config-resolver@4.4.3':
+ '@smithy/config-resolver@4.4.6':
dependencies:
- '@smithy/node-config-provider': 4.3.5
- '@smithy/types': 4.9.0
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/types': 4.12.0
'@smithy/util-config-provider': 4.2.0
- '@smithy/util-endpoints': 3.2.5
- '@smithy/util-middleware': 4.2.5
+ '@smithy/util-endpoints': 3.2.8
+ '@smithy/util-middleware': 4.2.8
tslib: 2.8.1
- '@smithy/core@3.18.7':
+ '@smithy/core@3.23.2':
dependencies:
- '@smithy/middleware-serde': 4.2.6
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@smithy/middleware-serde': 4.2.9
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
'@smithy/util-base64': 4.3.0
'@smithy/util-body-length-browser': 4.2.0
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-stream': 4.5.6
+ '@smithy/util-middleware': 4.2.8
+ '@smithy/util-stream': 4.5.12
'@smithy/util-utf8': 4.2.0
'@smithy/uuid': 1.1.0
tslib: 2.8.1
- '@smithy/credential-provider-imds@4.2.5':
+ '@smithy/credential-provider-imds@4.2.8':
dependencies:
- '@smithy/node-config-provider': 4.3.5
- '@smithy/property-provider': 4.2.5
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/property-provider': 4.2.8
+ '@smithy/types': 4.12.0
+ '@smithy/url-parser': 4.2.8
tslib: 2.8.1
- '@smithy/eventstream-codec@4.2.5':
+ '@smithy/eventstream-codec@4.2.8':
dependencies:
'@aws-crypto/crc32': 5.2.0
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
'@smithy/util-hex-encoding': 4.2.0
tslib: 2.8.1
- '@smithy/eventstream-serde-browser@4.2.5':
+ '@smithy/eventstream-serde-browser@4.2.8':
dependencies:
- '@smithy/eventstream-serde-universal': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/eventstream-serde-universal': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/eventstream-serde-config-resolver@4.3.5':
+ '@smithy/eventstream-serde-config-resolver@4.3.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/eventstream-serde-node@4.2.5':
+ '@smithy/eventstream-serde-node@4.2.8':
dependencies:
- '@smithy/eventstream-serde-universal': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/eventstream-serde-universal': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/eventstream-serde-universal@4.2.5':
+ '@smithy/eventstream-serde-universal@4.2.8':
dependencies:
- '@smithy/eventstream-codec': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/eventstream-codec': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/fetch-http-handler@5.3.6':
+ '@smithy/fetch-http-handler@5.3.9':
dependencies:
- '@smithy/protocol-http': 5.3.5
- '@smithy/querystring-builder': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/querystring-builder': 4.2.8
+ '@smithy/types': 4.12.0
'@smithy/util-base64': 4.3.0
tslib: 2.8.1
- '@smithy/hash-blob-browser@4.2.6':
+ '@smithy/hash-blob-browser@4.2.9':
dependencies:
'@smithy/chunked-blob-reader': 5.2.0
'@smithy/chunked-blob-reader-native': 4.2.1
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/hash-node@4.2.5':
+ '@smithy/hash-node@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
'@smithy/util-buffer-from': 4.2.0
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
- '@smithy/hash-stream-node@4.2.5':
+ '@smithy/hash-stream-node@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
- '@smithy/invalid-dependency@4.2.5':
+ '@smithy/invalid-dependency@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
'@smithy/is-array-buffer@2.2.0':
@@ -14520,126 +13880,126 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/md5-js@4.2.5':
+ '@smithy/md5-js@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
- '@smithy/middleware-content-length@4.2.5':
+ '@smithy/middleware-content-length@4.2.8':
dependencies:
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/middleware-endpoint@4.3.14':
+ '@smithy/middleware-endpoint@4.4.16':
dependencies:
- '@smithy/core': 3.18.7
- '@smithy/middleware-serde': 4.2.6
- '@smithy/node-config-provider': 4.3.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
- '@smithy/url-parser': 4.2.5
- '@smithy/util-middleware': 4.2.5
+ '@smithy/core': 3.23.2
+ '@smithy/middleware-serde': 4.2.9
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
+ '@smithy/url-parser': 4.2.8
+ '@smithy/util-middleware': 4.2.8
tslib: 2.8.1
- '@smithy/middleware-retry@4.4.14':
+ '@smithy/middleware-retry@4.4.33':
dependencies:
- '@smithy/node-config-provider': 4.3.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/service-error-classification': 4.2.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
- '@smithy/util-middleware': 4.2.5
- '@smithy/util-retry': 4.2.5
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/service-error-classification': 4.2.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
+ '@smithy/util-middleware': 4.2.8
+ '@smithy/util-retry': 4.2.8
'@smithy/uuid': 1.1.0
tslib: 2.8.1
- '@smithy/middleware-serde@4.2.6':
+ '@smithy/middleware-serde@4.2.9':
dependencies:
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/middleware-stack@4.2.5':
+ '@smithy/middleware-stack@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/node-config-provider@4.3.5':
+ '@smithy/node-config-provider@4.3.8':
dependencies:
- '@smithy/property-provider': 4.2.5
- '@smithy/shared-ini-file-loader': 4.4.0
- '@smithy/types': 4.9.0
+ '@smithy/property-provider': 4.2.8
+ '@smithy/shared-ini-file-loader': 4.4.3
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/node-http-handler@4.4.5':
+ '@smithy/node-http-handler@4.4.10':
dependencies:
- '@smithy/abort-controller': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/querystring-builder': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/abort-controller': 4.2.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/querystring-builder': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/property-provider@4.2.5':
+ '@smithy/property-provider@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/protocol-http@5.3.5':
+ '@smithy/protocol-http@5.3.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/querystring-builder@4.2.5':
+ '@smithy/querystring-builder@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
'@smithy/util-uri-escape': 4.2.0
tslib: 2.8.1
- '@smithy/querystring-parser@4.2.5':
+ '@smithy/querystring-parser@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/service-error-classification@4.2.5':
+ '@smithy/service-error-classification@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
- '@smithy/shared-ini-file-loader@4.4.0':
+ '@smithy/shared-ini-file-loader@4.4.3':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/signature-v4@5.3.5':
+ '@smithy/signature-v4@5.3.8':
dependencies:
'@smithy/is-array-buffer': 4.2.0
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
'@smithy/util-hex-encoding': 4.2.0
- '@smithy/util-middleware': 4.2.5
+ '@smithy/util-middleware': 4.2.8
'@smithy/util-uri-escape': 4.2.0
'@smithy/util-utf8': 4.2.0
tslib: 2.8.1
- '@smithy/smithy-client@4.9.10':
+ '@smithy/smithy-client@4.11.5':
dependencies:
- '@smithy/core': 3.18.7
- '@smithy/middleware-endpoint': 4.3.14
- '@smithy/middleware-stack': 4.2.5
- '@smithy/protocol-http': 5.3.5
- '@smithy/types': 4.9.0
- '@smithy/util-stream': 4.5.6
+ '@smithy/core': 3.23.2
+ '@smithy/middleware-endpoint': 4.4.16
+ '@smithy/middleware-stack': 4.2.8
+ '@smithy/protocol-http': 5.3.8
+ '@smithy/types': 4.12.0
+ '@smithy/util-stream': 4.5.12
tslib: 2.8.1
- '@smithy/types@4.9.0':
+ '@smithy/types@4.12.0':
dependencies:
tslib: 2.8.1
- '@smithy/url-parser@4.2.5':
+ '@smithy/url-parser@4.2.8':
dependencies:
- '@smithy/querystring-parser': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/querystring-parser': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
'@smithy/util-base64@4.3.0':
@@ -14670,49 +14030,49 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/util-defaults-mode-browser@4.3.13':
+ '@smithy/util-defaults-mode-browser@4.3.32':
dependencies:
- '@smithy/property-provider': 4.2.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
+ '@smithy/property-provider': 4.2.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/util-defaults-mode-node@4.2.16':
+ '@smithy/util-defaults-mode-node@4.2.35':
dependencies:
- '@smithy/config-resolver': 4.4.3
- '@smithy/credential-provider-imds': 4.2.5
- '@smithy/node-config-provider': 4.3.5
- '@smithy/property-provider': 4.2.5
- '@smithy/smithy-client': 4.9.10
- '@smithy/types': 4.9.0
+ '@smithy/config-resolver': 4.4.6
+ '@smithy/credential-provider-imds': 4.2.8
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/property-provider': 4.2.8
+ '@smithy/smithy-client': 4.11.5
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/util-endpoints@3.2.5':
+ '@smithy/util-endpoints@3.2.8':
dependencies:
- '@smithy/node-config-provider': 4.3.5
- '@smithy/types': 4.9.0
+ '@smithy/node-config-provider': 4.3.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
'@smithy/util-hex-encoding@4.2.0':
dependencies:
tslib: 2.8.1
- '@smithy/util-middleware@4.2.5':
+ '@smithy/util-middleware@4.2.8':
dependencies:
- '@smithy/types': 4.9.0
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/util-retry@4.2.5':
+ '@smithy/util-retry@4.2.8':
dependencies:
- '@smithy/service-error-classification': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/service-error-classification': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
- '@smithy/util-stream@4.5.6':
+ '@smithy/util-stream@4.5.12':
dependencies:
- '@smithy/fetch-http-handler': 5.3.6
- '@smithy/node-http-handler': 4.4.5
- '@smithy/types': 4.9.0
+ '@smithy/fetch-http-handler': 5.3.9
+ '@smithy/node-http-handler': 4.4.10
+ '@smithy/types': 4.12.0
'@smithy/util-base64': 4.3.0
'@smithy/util-buffer-from': 4.2.0
'@smithy/util-hex-encoding': 4.2.0
@@ -14733,10 +14093,10 @@ snapshots:
'@smithy/util-buffer-from': 4.2.0
tslib: 2.8.1
- '@smithy/util-waiter@4.2.5':
+ '@smithy/util-waiter@4.2.8':
dependencies:
- '@smithy/abort-controller': 4.2.5
- '@smithy/types': 4.9.0
+ '@smithy/abort-controller': 4.2.8
+ '@smithy/types': 4.12.0
tslib: 2.8.1
'@smithy/uuid@1.1.0':
@@ -14747,303 +14107,299 @@ snapshots:
'@standard-schema/spec@1.0.0': {}
- '@storybook/addon-actions@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-actions@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
'@types/uuid': 9.0.8
dequal: 2.0.3
polished: 4.3.1
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
uuid: 9.0.1
- '@storybook/addon-backgrounds@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-backgrounds@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
memoizerific: 1.11.3
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
- '@storybook/addon-controls@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-controls@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
dequal: 2.0.3
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
- '@storybook/addon-docs@8.6.14(@types/react@19.2.2)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-docs@8.6.17(@types/react@19.2.2)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.2.1)
- '@storybook/blocks': 8.6.14(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/csf-plugin': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/react-dom-shim': 8.6.14(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.2.4)
+ '@storybook/blocks': 8.6.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/csf-plugin': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/react-dom-shim': 8.6.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ react: 19.2.4
+ react-dom: 19.2.4(react@19.2.4)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
transitivePeerDependencies:
- '@types/react'
- '@storybook/addon-essentials@8.6.14(@types/react@19.2.2)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-essentials@8.6.17(@types/react@19.2.2)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- '@storybook/addon-actions': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-backgrounds': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-controls': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-docs': 8.6.14(@types/react@19.2.2)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-highlight': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-measure': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-outline': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-toolbars': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/addon-viewport': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ '@storybook/addon-actions': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-backgrounds': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-controls': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-docs': 8.6.17(@types/react@19.2.2)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-highlight': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-measure': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-outline': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-toolbars': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/addon-viewport': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
transitivePeerDependencies:
- '@types/react'
- '@storybook/addon-highlight@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-highlight@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/addon-interactions@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-interactions@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
- '@storybook/instrumenter': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/test': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ '@storybook/instrumenter': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/test': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
polished: 4.3.1
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
- '@storybook/addon-links@10.1.5(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-links@10.2.10(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
optionalDependencies:
- react: 19.2.1
+ react: 19.2.4
- '@storybook/addon-mdx-gfm@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-mdx-gfm@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
remark-gfm: 4.0.1
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
transitivePeerDependencies:
- supports-color
- '@storybook/addon-measure@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-measure@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
tiny-invariant: 1.3.3
- '@storybook/addon-outline@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-outline@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
- '@storybook/addon-storysource@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-storysource@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- '@storybook/source-loader': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ '@storybook/source-loader': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
estraverse: 5.3.0
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
tiny-invariant: 1.3.3
- '@storybook/addon-toolbars@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-toolbars@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/addon-viewport@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/addon-viewport@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
memoizerific: 1.11.3
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/blocks@8.6.14(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/blocks@8.6.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- '@storybook/icons': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ '@storybook/icons': 1.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
optionalDependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.4
+ react-dom: 19.2.4(react@19.2.4)
- '@storybook/builder-vite@10.1.5(esbuild@0.27.1)(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))':
+ '@storybook/builder-vite@10.2.10(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))':
dependencies:
- '@storybook/csf-plugin': 10.1.5(esbuild@0.27.1)(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
- '@vitest/mocker': 3.2.4(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ '@storybook/csf-plugin': 10.2.10(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
ts-dedent: 2.2.0
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
transitivePeerDependencies:
- esbuild
- - msw
- rollup
- webpack
- '@storybook/components@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/components@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/core-events@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/core-events@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/csf-plugin@10.1.5(esbuild@0.27.1)(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))':
+ '@storybook/csf-plugin@10.2.10(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
unplugin: 2.3.10
optionalDependencies:
- esbuild: 0.27.1
- rollup: 4.53.3
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ esbuild: 0.27.3
+ rollup: 4.59.0
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
- '@storybook/csf-plugin@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/csf-plugin@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
unplugin: 1.16.1
'@storybook/global@5.0.0': {}
- '@storybook/icons@1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@storybook/icons@1.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.4
+ react-dom: 19.2.4(react@19.2.4)
- '@storybook/icons@2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@storybook/icons@2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.4
+ react-dom: 19.2.4(react@19.2.4)
- '@storybook/instrumenter@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/instrumenter@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
'@vitest/utils': 2.1.9
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/manager-api@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/manager-api@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/preview-api@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/preview-api@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/react-dom-shim@10.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/react-dom-shim@10.2.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ react: 19.2.4
+ react-dom: 19.2.4(react@19.2.4)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/react-dom-shim@8.6.14(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/react-dom-shim@8.6.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ react: 19.2.4
+ react-dom: 19.2.4(react@19.2.4)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/react-vite@10.1.5(esbuild@0.27.1)(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))':
+ '@storybook/react-vite@10.2.10(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))':
dependencies:
- '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.1(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
- '@rollup/pluginutils': 5.3.0(rollup@4.53.3)
- '@storybook/builder-vite': 10.1.5(esbuild@0.27.1)(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
- '@storybook/react': 10.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(typescript@5.9.3)
+ '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.4(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
+ '@rollup/pluginutils': 5.3.0(rollup@4.59.0)
+ '@storybook/builder-vite': 10.2.10(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
+ '@storybook/react': 10.2.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(typescript@5.9.3)
empathic: 2.0.0
magic-string: 0.30.21
- react: 19.2.1
+ react: 19.2.4
react-docgen: 8.0.2
- react-dom: 19.2.1(react@19.2.1)
+ react-dom: 19.2.4(react@19.2.4)
resolve: 1.22.11
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
tsconfig-paths: 4.2.0
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
transitivePeerDependencies:
- esbuild
- - msw
- rollup
- supports-color
- typescript
- webpack
- '@storybook/react@10.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(typescript@5.9.3)':
+ '@storybook/react@10.2.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(typescript@5.9.3)':
dependencies:
'@storybook/global': 5.0.0
- '@storybook/react-dom-shim': 10.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- react: 19.2.1
+ '@storybook/react-dom-shim': 10.2.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ react: 19.2.4
react-docgen: 8.0.2
- react-dom: 19.2.1(react@19.2.1)
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ react-dom: 19.2.4(react@19.2.4)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
optionalDependencies:
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@storybook/source-loader@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/source-loader@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
es-toolkit: 1.42.0
estraverse: 5.3.0
- prettier: 3.7.4
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ prettier: 3.8.1
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/test@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/test@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
'@storybook/global': 5.0.0
- '@storybook/instrumenter': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ '@storybook/instrumenter': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
'@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: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/theming@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/theming@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/types@8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))':
+ '@storybook/types@8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))':
dependencies:
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
- '@storybook/vue3-vite@10.1.5(esbuild@0.27.1)(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))(vue@3.5.25(typescript@5.9.3))':
+ '@storybook/vue3-vite@10.2.10(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))(vue@3.5.28(typescript@5.9.3))':
dependencies:
- '@storybook/builder-vite': 10.1.5(esbuild@0.27.1)(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(rollup@4.53.3)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
- '@storybook/vue3': 10.1.5(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vue@3.5.25(typescript@5.9.3))
+ '@storybook/builder-vite': 10.2.10(esbuild@0.27.3)(rollup@4.59.0)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
+ '@storybook/vue3': 10.2.10(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vue@3.5.28(typescript@5.9.3))
magic-string: 0.30.21
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
typescript: 5.9.3
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
vue-component-meta: 2.2.12(typescript@5.9.3)
- vue-docgen-api: 4.79.2(vue@3.5.25(typescript@5.9.3))
+ vue-docgen-api: 4.79.2(vue@3.5.28(typescript@5.9.3))
transitivePeerDependencies:
- esbuild
- - msw
- rollup
- vue
- webpack
- '@storybook/vue3@10.1.5(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))(vue@3.5.25(typescript@5.9.3))':
+ '@storybook/vue3@10.2.10(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))(vue@3.5.28(typescript@5.9.3))':
dependencies:
'@storybook/global': 5.0.0
- storybook: 10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5)
+ storybook: 10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6)
type-fest: 2.19.0
- vue: 3.5.25(typescript@5.9.3)
- vue-component-type-helpers: 3.1.8
+ vue: 3.5.28(typescript@5.9.3)
+ vue-component-type-helpers: 3.2.4
- '@stylistic/eslint-plugin@5.5.0(eslint@9.39.1)':
+ '@stylistic/eslint-plugin@5.5.0(eslint@9.39.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
- '@typescript-eslint/types': 8.49.0
- eslint: 9.39.1
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3)
+ '@typescript-eslint/types': 8.56.0
+ eslint: 9.39.3
eslint-visitor-keys: 4.2.1
espree: 10.4.0
estraverse: 5.3.0
picomatch: 4.0.3
- '@swc/cli@0.7.9(@swc/core@1.15.3)(chokidar@5.0.0)':
+ '@swc/cli@0.8.0(@swc/core@1.15.11)(chokidar@5.0.0)':
dependencies:
- '@swc/core': 1.15.3
+ '@swc/core': 1.15.11
'@swc/counter': 0.1.3
'@xhmikosr/bin-wrapper': 13.2.0
commander: 8.3.0
minimatch: 9.0.5
piscina: 4.9.2
- semver: 7.7.3
+ semver: 7.7.4
slash: 3.0.0
source-map: 0.7.6
tinyglobby: 0.2.15
@@ -15059,10 +14415,10 @@ snapshots:
'@swc/wasm': 1.2.130
optional: true
- '@swc/core-darwin-arm64@1.15.3':
+ '@swc/core-darwin-arm64@1.15.11':
optional: true
- '@swc/core-darwin-x64@1.15.3':
+ '@swc/core-darwin-x64@1.15.11':
optional: true
'@swc/core-freebsd-x64@1.3.11':
@@ -15070,52 +14426,52 @@ snapshots:
'@swc/wasm': 1.2.130
optional: true
- '@swc/core-linux-arm-gnueabihf@1.15.3':
+ '@swc/core-linux-arm-gnueabihf@1.15.11':
optional: true
- '@swc/core-linux-arm64-gnu@1.15.3':
+ '@swc/core-linux-arm64-gnu@1.15.11':
optional: true
- '@swc/core-linux-arm64-musl@1.15.3':
+ '@swc/core-linux-arm64-musl@1.15.11':
optional: true
- '@swc/core-linux-x64-gnu@1.15.3':
+ '@swc/core-linux-x64-gnu@1.15.11':
optional: true
- '@swc/core-linux-x64-musl@1.15.3':
+ '@swc/core-linux-x64-musl@1.15.11':
optional: true
- '@swc/core-win32-arm64-msvc@1.15.3':
+ '@swc/core-win32-arm64-msvc@1.15.11':
optional: true
- '@swc/core-win32-ia32-msvc@1.15.3':
+ '@swc/core-win32-ia32-msvc@1.15.11':
optional: true
- '@swc/core-win32-x64-msvc@1.15.3':
+ '@swc/core-win32-x64-msvc@1.15.11':
optional: true
- '@swc/core@1.15.3':
+ '@swc/core@1.15.11':
dependencies:
'@swc/counter': 0.1.3
'@swc/types': 0.1.25
optionalDependencies:
- '@swc/core-darwin-arm64': 1.15.3
- '@swc/core-darwin-x64': 1.15.3
- '@swc/core-linux-arm-gnueabihf': 1.15.3
- '@swc/core-linux-arm64-gnu': 1.15.3
- '@swc/core-linux-arm64-musl': 1.15.3
- '@swc/core-linux-x64-gnu': 1.15.3
- '@swc/core-linux-x64-musl': 1.15.3
- '@swc/core-win32-arm64-msvc': 1.15.3
- '@swc/core-win32-ia32-msvc': 1.15.3
- '@swc/core-win32-x64-msvc': 1.15.3
+ '@swc/core-darwin-arm64': 1.15.11
+ '@swc/core-darwin-x64': 1.15.11
+ '@swc/core-linux-arm-gnueabihf': 1.15.11
+ '@swc/core-linux-arm64-gnu': 1.15.11
+ '@swc/core-linux-arm64-musl': 1.15.11
+ '@swc/core-linux-x64-gnu': 1.15.11
+ '@swc/core-linux-x64-musl': 1.15.11
+ '@swc/core-win32-arm64-msvc': 1.15.11
+ '@swc/core-win32-ia32-msvc': 1.15.11
+ '@swc/core-win32-x64-msvc': 1.15.11
'@swc/counter@0.1.3': {}
- '@swc/jest@0.2.39(@swc/core@1.15.3)':
+ '@swc/jest@0.2.39(@swc/core@1.15.11)':
dependencies:
'@jest/create-cache-key-function': 30.2.0
- '@swc/core': 1.15.3
+ '@swc/core': 1.15.11
'@swc/counter': 0.1.3
jsonc-parser: 3.3.1
@@ -15132,7 +14488,7 @@ snapshots:
stringz: 2.1.0
uuid: 9.0.1
- '@syuilo/aiscript@1.2.0':
+ '@syuilo/aiscript@1.2.1':
dependencies:
seedrandom: 3.0.5
stringz: 2.1.0
@@ -15229,7 +14585,7 @@ snapshots:
'@testing-library/dom@10.4.0':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
'@types/aria-query': 5.0.4
aria-query: 5.3.0
chalk: 4.1.2
@@ -15240,7 +14596,7 @@ snapshots:
'@testing-library/dom@9.3.4':
dependencies:
'@babel/code-frame': 7.27.1
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
'@types/aria-query': 5.0.4
aria-query: 5.1.3
chalk: 4.1.2
@@ -15255,7 +14611,7 @@ snapshots:
chalk: 3.0.0
css.escape: 1.5.1
dom-accessibility-api: 0.6.3
- lodash: 4.17.21
+ lodash: 4.17.23
redent: 3.0.0
'@testing-library/jest-dom@6.9.1':
@@ -15275,14 +14631,14 @@ snapshots:
dependencies:
'@testing-library/dom': 10.4.0
- '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.25)(vue@3.5.25(typescript@5.9.3))':
+ '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.5.28)(vue@3.5.28(typescript@5.9.3))':
dependencies:
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
'@testing-library/dom': 9.3.4
'@vue/test-utils': 2.4.6
- vue: 3.5.25(typescript@5.9.3)
+ vue: 3.5.28(typescript@5.9.3)
optionalDependencies:
- '@vue/compiler-sfc': 3.5.25
+ '@vue/compiler-sfc': 3.5.28
'@tokenizer/inflate@0.2.7':
dependencies:
@@ -15292,14 +14648,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@tokenizer/inflate@0.3.1':
- dependencies:
- debug: 4.4.3(supports-color@10.2.2)
- fflate: 0.8.2
- token-types: 6.1.1
- transitivePeerDependencies:
- - supports-color
-
'@tokenizer/inflate@0.4.1':
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@@ -15309,15 +14657,13 @@ snapshots:
'@tokenizer/token@0.3.0': {}
- '@tootallnate/once@2.0.0': {}
-
'@tsd/typescript@5.9.3': {}
'@twemoji/parser@16.0.0': {}
'@types/accepts@1.3.7':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/archiver@7.0.0':
dependencies:
@@ -15329,29 +14675,29 @@ snapshots:
'@types/babel__core@7.20.5':
dependencies:
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
'@types/babel__generator': 7.27.0
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.28.0
'@types/babel__generator@7.27.0':
dependencies:
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
'@types/babel__template@7.4.4':
dependencies:
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
'@types/babel__traverse@7.28.0':
dependencies:
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
'@types/body-parser@1.19.6':
dependencies:
'@types/connect': 3.4.38
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/braces@3.0.5': {}
@@ -15370,7 +14716,7 @@ snapshots:
'@types/connect@3.4.38':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/content-disposition@0.5.9': {}
@@ -15403,7 +14749,7 @@ snapshots:
'@types/express-serve-static-core@5.1.0':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/qs': 6.14.0
'@types/range-parser': 1.2.7
'@types/send': 1.2.1
@@ -15416,11 +14762,11 @@ snapshots:
'@types/fluent-ffmpeg@2.1.28':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/graceful-fs@4.1.9':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/hammerjs@2.0.46': {}
@@ -15434,7 +14780,9 @@ snapshots:
'@types/http-link-header@1.0.7':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
+
+ '@types/insert-text-at-cursor@0.3.2': {}
'@types/istanbul-lib-coverage@2.0.6': {}
@@ -15453,12 +14801,6 @@ snapshots:
'@types/js-yaml@4.0.9': {}
- '@types/jsdom@20.0.1':
- dependencies:
- '@types/node': 24.10.2
- '@types/tough-cookie': 4.0.5
- parse5: 7.3.0
-
'@types/json-schema@7.0.15': {}
'@types/json5@0.0.29': {}
@@ -15491,27 +14833,20 @@ snapshots:
'@types/mysql@2.15.27':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/node-fetch@2.6.13':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
form-data: 4.0.5
- '@types/node@20.19.25':
- dependencies:
- undici-types: 6.21.0
-
- '@types/node@24.10.2':
+ '@types/node@24.10.13':
dependencies:
undici-types: 7.16.0
- '@types/nodemailer@7.0.4':
+ '@types/nodemailer@7.0.11':
dependencies:
- '@aws-sdk/client-sesv2': 3.938.0
- '@types/node': 24.10.2
- transitivePeerDependencies:
- - aws-crt
+ '@types/node': 24.10.13
'@types/normalize-package-data@2.4.4': {}
@@ -15522,27 +14857,33 @@ snapshots:
'@types/oauth2orize@1.11.5':
dependencies:
'@types/express': 5.0.4
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/offscreencanvas@2019.3.0': {}
'@types/offscreencanvas@2019.7.3': {}
- '@types/pg-pool@2.0.6':
+ '@types/pg-pool@2.0.7':
dependencies:
- '@types/pg': 8.15.6
+ '@types/pg': 8.16.0
'@types/pg@8.15.6':
dependencies:
- '@types/node': 24.10.2
- pg-protocol: 1.10.3
+ '@types/node': 24.10.13
+ pg-protocol: 1.11.0
+ pg-types: 2.2.0
+
+ '@types/pg@8.16.0':
+ dependencies:
+ '@types/node': 24.10.13
+ pg-protocol: 1.11.0
pg-types: 2.2.0
'@types/punycode@2.1.4': {}
'@types/qrcode@1.5.6':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/qs@6.14.0': {}
@@ -15558,9 +14899,7 @@ snapshots:
'@types/readdir-glob@1.1.5':
dependencies:
- '@types/node': 24.10.2
-
- '@types/redis-info@3.0.3': {}
+ '@types/node': 24.10.13
'@types/rename@1.0.7': {}
@@ -15579,16 +14918,16 @@ snapshots:
'@types/send@0.17.6':
dependencies:
'@types/mime': 1.3.5
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/send@1.2.1':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/serve-static@1.15.10':
dependencies:
'@types/http-errors': 2.0.5
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/send': 0.17.6
'@types/serviceworker@0.0.74': {}
@@ -15613,7 +14952,7 @@ snapshots:
dependencies:
'@types/cookiejar': 2.1.5
'@types/methods': 1.1.4
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
form-data: 4.0.5
'@types/supertest@6.0.3':
@@ -15623,7 +14962,9 @@ snapshots:
'@types/tedious@4.0.14':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
+
+ '@types/textarea-caret@3.0.4': {}
'@types/throttle-debounce@5.0.2': {}
@@ -15631,29 +14972,27 @@ snapshots:
'@types/tmp@0.2.6': {}
- '@types/tough-cookie@4.0.5': {}
-
'@types/unist@3.0.3': {}
'@types/uuid@9.0.8': {}
'@types/vary@1.1.3':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/wawoff2@1.0.2':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/web-push@3.6.4':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/whatwg-mimetype@3.0.2': {}
'@types/ws@8.18.1':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
'@types/yargs-parser@21.0.3': {}
@@ -15663,124 +15002,152 @@ snapshots:
'@types/yauzl@2.10.3':
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
optional: true
- '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
- '@typescript-eslint/scope-manager': 8.49.0
- '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
- '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.49.0
- eslint: 9.39.1
+ '@typescript-eslint/parser': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
+ '@typescript-eslint/scope-manager': 8.56.0
+ '@typescript-eslint/type-utils': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.56.0
+ eslint: 9.39.3
ignore: 7.0.5
natural-compare: 1.4.0
- ts-api-utils: 2.1.0(typescript@5.9.3)
+ ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3)':
+ '@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/scope-manager': 8.49.0
- '@typescript-eslint/types': 8.49.0
- '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
- '@typescript-eslint/visitor-keys': 8.49.0
+ '@typescript-eslint/scope-manager': 8.56.0
+ '@typescript-eslint/types': 8.56.0
+ '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3)
+ '@typescript-eslint/visitor-keys': 8.56.0
debug: 4.4.3(supports-color@10.2.2)
- eslint: 9.39.1
+ eslint: 9.39.3
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)':
+ '@typescript-eslint/project-service@8.56.0(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3)
- '@typescript-eslint/types': 8.49.0
+ '@typescript-eslint/tsconfig-utils': 8.56.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.56.0
debug: 4.4.3(supports-color@10.2.2)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.49.0':
+ '@typescript-eslint/scope-manager@8.56.0':
dependencies:
- '@typescript-eslint/types': 8.49.0
- '@typescript-eslint/visitor-keys': 8.49.0
+ '@typescript-eslint/types': 8.56.0
+ '@typescript-eslint/visitor-keys': 8.56.0
- '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)':
+ '@typescript-eslint/tsconfig-utils@8.56.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
- '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)':
+ '@typescript-eslint/type-utils@8.56.0(eslint@9.39.3)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/types': 8.49.0
- '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
- '@typescript-eslint/utils': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ '@typescript-eslint/types': 8.56.0
+ '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
debug: 4.4.3(supports-color@10.2.2)
- eslint: 9.39.1
- ts-api-utils: 2.1.0(typescript@5.9.3)
+ eslint: 9.39.3
+ ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@8.49.0': {}
+ '@typescript-eslint/types@8.56.0': {}
- '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)':
+ '@typescript-eslint/typescript-estree@8.56.0(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3)
- '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3)
- '@typescript-eslint/types': 8.49.0
- '@typescript-eslint/visitor-keys': 8.49.0
+ '@typescript-eslint/project-service': 8.56.0(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.56.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.56.0
+ '@typescript-eslint/visitor-keys': 8.56.0
debug: 4.4.3(supports-color@10.2.2)
minimatch: 9.0.5
- semver: 7.7.3
+ semver: 7.7.4
tinyglobby: 0.2.15
- ts-api-utils: 2.1.0(typescript@5.9.3)
+ ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.49.0(eslint@9.39.1)(typescript@5.9.3)':
+ '@typescript-eslint/utils@8.56.0(eslint@9.39.3)(typescript@5.9.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
- '@typescript-eslint/scope-manager': 8.49.0
- '@typescript-eslint/types': 8.49.0
- '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
- eslint: 9.39.1
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3)
+ '@typescript-eslint/scope-manager': 8.56.0
+ '@typescript-eslint/types': 8.56.0
+ '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3)
+ eslint: 9.39.3
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@8.49.0':
+ '@typescript-eslint/visitor-keys@8.56.0':
dependencies:
- '@typescript-eslint/types': 8.49.0
- eslint-visitor-keys: 4.2.1
+ '@typescript-eslint/types': 8.56.0
+ eslint-visitor-keys: 5.0.1
+
+ '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260116.1':
+ optional: true
+
+ '@typescript/native-preview-darwin-x64@7.0.0-dev.20260116.1':
+ optional: true
+
+ '@typescript/native-preview-linux-arm64@7.0.0-dev.20260116.1':
+ optional: true
+
+ '@typescript/native-preview-linux-arm@7.0.0-dev.20260116.1':
+ optional: true
+
+ '@typescript/native-preview-linux-x64@7.0.0-dev.20260116.1':
+ optional: true
+
+ '@typescript/native-preview-win32-arm64@7.0.0-dev.20260116.1':
+ optional: true
+
+ '@typescript/native-preview-win32-x64@7.0.0-dev.20260116.1':
+ optional: true
+
+ '@typescript/native-preview@7.0.0-dev.20260116.1':
+ optionalDependencies:
+ '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260116.1
+ '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260116.1
+ '@typescript/native-preview-linux-arm': 7.0.0-dev.20260116.1
+ '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260116.1
+ '@typescript/native-preview-linux-x64': 7.0.0-dev.20260116.1
+ '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260116.1
+ '@typescript/native-preview-win32-x64': 7.0.0-dev.20260116.1
'@ungap/structured-clone@1.3.0': {}
- '@vitejs/plugin-vue@6.0.2(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))(vue@3.5.25(typescript@5.9.3))':
+ '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))(vue@3.5.28(typescript@5.9.3))':
dependencies:
- '@rolldown/pluginutils': 1.0.0-beta.50
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
- vue: 3.5.25(typescript@5.9.3)
+ '@rolldown/pluginutils': 1.0.0-rc.2
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
+ vue: 3.5.28(typescript@5.9.3)
- '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))':
+ '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))':
dependencies:
'@bcoe/v8-coverage': 1.0.2
- '@vitest/utils': 4.0.15
- ast-v8-to-istanbul: 0.3.8
+ '@vitest/utils': 4.0.18
+ ast-v8-to-istanbul: 0.3.10
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
- istanbul-lib-source-maps: 5.0.6
istanbul-reports: 3.2.0
magicast: 0.5.1
obug: 2.1.1
std-env: 3.10.0
tinyrainbow: 3.0.3
- vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
- transitivePeerDependencies:
- - supports-color
+ vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
'@vitest/expect@2.0.5':
dependencies:
@@ -15797,32 +15164,23 @@ snapshots:
chai: 5.3.3
tinyrainbow: 2.0.0
- '@vitest/expect@4.0.15':
+ '@vitest/expect@4.0.18':
dependencies:
'@standard-schema/spec': 1.0.0
'@types/chai': 5.2.3
- '@vitest/spy': 4.0.15
- '@vitest/utils': 4.0.15
+ '@vitest/spy': 4.0.18
+ '@vitest/utils': 4.0.18
chai: 6.2.1
tinyrainbow: 3.0.3
- '@vitest/mocker@3.2.4(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))':
+ '@vitest/mocker@4.0.18(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))':
dependencies:
- '@vitest/spy': 3.2.4
+ '@vitest/spy': 4.0.18
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- msw: 2.12.4(@types/node@24.10.2)(typescript@5.9.3)
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
-
- '@vitest/mocker@4.0.15(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))':
- dependencies:
- '@vitest/spy': 4.0.15
- estree-walker: 3.0.3
- magic-string: 0.30.21
- optionalDependencies:
- msw: 2.12.4(@types/node@24.10.2)(typescript@5.9.3)
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ msw: 2.12.10(@types/node@24.10.13)(typescript@5.9.3)
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
'@vitest/pretty-format@2.0.5':
dependencies:
@@ -15836,18 +15194,18 @@ snapshots:
dependencies:
tinyrainbow: 2.0.0
- '@vitest/pretty-format@4.0.15':
+ '@vitest/pretty-format@4.0.18':
dependencies:
tinyrainbow: 3.0.3
- '@vitest/runner@4.0.15':
+ '@vitest/runner@4.0.18':
dependencies:
- '@vitest/utils': 4.0.15
+ '@vitest/utils': 4.0.18
pathe: 2.0.3
- '@vitest/snapshot@4.0.15':
+ '@vitest/snapshot@4.0.18':
dependencies:
- '@vitest/pretty-format': 4.0.15
+ '@vitest/pretty-format': 4.0.18
magic-string: 0.30.21
pathe: 2.0.3
@@ -15859,7 +15217,7 @@ snapshots:
dependencies:
tinyspy: 4.0.4
- '@vitest/spy@4.0.15': {}
+ '@vitest/spy@4.0.18': {}
'@vitest/utils@2.0.5':
dependencies:
@@ -15880,22 +15238,22 @@ snapshots:
loupe: 3.2.1
tinyrainbow: 2.0.0
- '@vitest/utils@4.0.15':
+ '@vitest/utils@4.0.18':
dependencies:
- '@vitest/pretty-format': 4.0.15
+ '@vitest/pretty-format': 4.0.18
tinyrainbow: 3.0.3
'@volar/language-core@2.4.15':
dependencies:
'@volar/source-map': 2.4.15
- '@volar/language-core@2.4.26':
+ '@volar/language-core@2.4.27':
dependencies:
- '@volar/source-map': 2.4.26
+ '@volar/source-map': 2.4.27
'@volar/source-map@2.4.15': {}
- '@volar/source-map@2.4.26': {}
+ '@volar/source-map@2.4.27': {}
'@volar/typescript@2.4.15':
dependencies:
@@ -15903,41 +15261,41 @@ snapshots:
path-browserify: 1.0.1
vscode-uri: 3.1.0
- '@volar/typescript@2.4.26':
+ '@volar/typescript@2.4.27':
dependencies:
- '@volar/language-core': 2.4.26
+ '@volar/language-core': 2.4.27
path-browserify: 1.0.1
vscode-uri: 3.1.0
- '@vue/compiler-core@3.5.25':
+ '@vue/compiler-core@3.5.28':
dependencies:
- '@babel/parser': 7.28.5
- '@vue/shared': 3.5.25
- entities: 4.5.0
+ '@babel/parser': 7.29.0
+ '@vue/shared': 3.5.28
+ entities: 7.0.1
estree-walker: 2.0.2
source-map-js: 1.2.1
- '@vue/compiler-dom@3.5.25':
+ '@vue/compiler-dom@3.5.28':
dependencies:
- '@vue/compiler-core': 3.5.25
- '@vue/shared': 3.5.25
+ '@vue/compiler-core': 3.5.28
+ '@vue/shared': 3.5.28
- '@vue/compiler-sfc@3.5.25':
+ '@vue/compiler-sfc@3.5.28':
dependencies:
- '@babel/parser': 7.28.5
- '@vue/compiler-core': 3.5.25
- '@vue/compiler-dom': 3.5.25
- '@vue/compiler-ssr': 3.5.25
- '@vue/shared': 3.5.25
+ '@babel/parser': 7.29.0
+ '@vue/compiler-core': 3.5.28
+ '@vue/compiler-dom': 3.5.28
+ '@vue/compiler-ssr': 3.5.28
+ '@vue/shared': 3.5.28
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.6
source-map-js: 1.2.1
- '@vue/compiler-ssr@3.5.25':
+ '@vue/compiler-ssr@3.5.28':
dependencies:
- '@vue/compiler-dom': 3.5.25
- '@vue/shared': 3.5.25
+ '@vue/compiler-dom': 3.5.28
+ '@vue/shared': 3.5.28
'@vue/compiler-vue2@2.7.16':
dependencies:
@@ -15947,9 +15305,9 @@ snapshots:
'@vue/language-core@2.2.12(typescript@5.9.3)':
dependencies:
'@volar/language-core': 2.4.15
- '@vue/compiler-dom': 3.5.25
+ '@vue/compiler-dom': 3.5.28
'@vue/compiler-vue2': 2.7.16
- '@vue/shared': 3.5.25
+ '@vue/shared': 3.5.28
alien-signals: 1.0.13
minimatch: 9.0.5
muggle-string: 0.4.1
@@ -15957,41 +15315,39 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
- '@vue/language-core@3.1.8(typescript@5.9.3)':
+ '@vue/language-core@3.2.4':
dependencies:
- '@volar/language-core': 2.4.26
- '@vue/compiler-dom': 3.5.25
- '@vue/shared': 3.5.25
- alien-signals: 3.1.0
+ '@volar/language-core': 2.4.27
+ '@vue/compiler-dom': 3.5.28
+ '@vue/shared': 3.5.28
+ alien-signals: 3.1.2
muggle-string: 0.4.1
path-browserify: 1.0.1
picomatch: 4.0.3
- optionalDependencies:
- typescript: 5.9.3
- '@vue/reactivity@3.5.25':
+ '@vue/reactivity@3.5.28':
dependencies:
- '@vue/shared': 3.5.25
+ '@vue/shared': 3.5.28
- '@vue/runtime-core@3.5.25':
+ '@vue/runtime-core@3.5.28':
dependencies:
- '@vue/reactivity': 3.5.25
- '@vue/shared': 3.5.25
+ '@vue/reactivity': 3.5.28
+ '@vue/shared': 3.5.28
- '@vue/runtime-dom@3.5.25':
+ '@vue/runtime-dom@3.5.28':
dependencies:
- '@vue/reactivity': 3.5.25
- '@vue/runtime-core': 3.5.25
- '@vue/shared': 3.5.25
+ '@vue/reactivity': 3.5.28
+ '@vue/runtime-core': 3.5.28
+ '@vue/shared': 3.5.28
csstype: 3.2.3
- '@vue/server-renderer@3.5.25(vue@3.5.25(typescript@5.9.3))':
+ '@vue/server-renderer@3.5.28(vue@3.5.28(typescript@5.9.3))':
dependencies:
- '@vue/compiler-ssr': 3.5.25
- '@vue/shared': 3.5.25
- vue: 3.5.25(typescript@5.9.3)
+ '@vue/compiler-ssr': 3.5.28
+ '@vue/shared': 3.5.28
+ vue: 3.5.28(typescript@5.9.3)
- '@vue/shared@3.5.25': {}
+ '@vue/shared@3.5.28': {}
'@vue/test-utils@2.4.6':
dependencies:
@@ -16098,14 +15454,12 @@ snapshots:
'@xmldom/xmldom@0.9.8':
optional: true
- abab@2.0.6: {}
-
abbrev@1.1.1:
optional: true
abbrev@2.0.0: {}
- abbrev@3.0.1: {}
+ abbrev@4.0.0: {}
abort-controller@3.0.0:
dependencies:
@@ -16123,26 +15477,17 @@ snapshots:
mime-types: 3.0.2
negotiator: 1.0.0
- acorn-globals@7.0.1:
+ acorn-import-attributes@1.9.5(acorn@8.16.0):
dependencies:
- acorn: 8.15.0
- acorn-walk: 8.3.4
+ acorn: 8.16.0
- acorn-import-attributes@1.9.5(acorn@8.15.0):
+ acorn-jsx@5.3.2(acorn@8.16.0):
dependencies:
- acorn: 8.15.0
-
- acorn-jsx@5.3.2(acorn@8.15.0):
- dependencies:
- acorn: 8.15.0
-
- acorn-walk@8.3.4:
- dependencies:
- acorn: 8.15.0
+ acorn: 8.16.0
acorn@7.4.1: {}
- acorn@8.15.0: {}
+ acorn@8.16.0: {}
adm-zip@0.5.16:
optional: true
@@ -16157,6 +15502,7 @@ snapshots:
debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies:
- supports-color
+ optional: true
agent-base@7.1.4: {}
@@ -16173,17 +15519,17 @@ snapshots:
optionalDependencies:
ajv: 8.13.0
- ajv-draft-04@1.0.0(ajv@8.17.1):
+ ajv-draft-04@1.0.0(ajv@8.18.0):
optionalDependencies:
- ajv: 8.17.1
+ ajv: 8.18.0
ajv-formats@3.0.1(ajv@8.13.0):
optionalDependencies:
ajv: 8.13.0
- ajv-formats@3.0.1(ajv@8.17.1):
+ ajv-formats@3.0.1(ajv@8.18.0):
optionalDependencies:
- ajv: 8.17.1
+ ajv: 8.18.0
ajv@6.12.6:
dependencies:
@@ -16206,7 +15552,7 @@ snapshots:
require-from-string: 2.0.2
uri-js: 4.4.1
- ajv@8.17.1:
+ ajv@8.18.0:
dependencies:
fast-deep-equal: 3.1.3
fast-uri: 3.1.0
@@ -16215,7 +15561,7 @@ snapshots:
alien-signals@1.0.13: {}
- alien-signals@3.1.0: {}
+ alien-signals@3.1.2: {}
analytics-utils@1.1.1(@types/dlv@1.1.5):
dependencies:
@@ -16272,7 +15618,7 @@ snapshots:
graceful-fs: 4.2.11
is-stream: 2.0.1
lazystream: 1.0.1
- lodash: 4.17.21
+ lodash: 4.17.23
normalize-path: 3.0.0
readable-stream: 4.7.0
@@ -16398,7 +15744,7 @@ snapshots:
dependencies:
tslib: 2.8.1
- ast-v8-to-istanbul@0.3.8:
+ ast-v8-to-istanbul@0.3.10:
dependencies:
'@jridgewell/trace-mapping': 0.3.31
estree-walker: 3.0.3
@@ -16431,7 +15777,7 @@ snapshots:
avvio@9.1.0:
dependencies:
'@fastify/error': 4.2.0
- fastq: 1.19.1
+ fastq: 1.20.1
aws-sdk-client-mock@4.1.0:
dependencies:
@@ -16485,7 +15831,7 @@ snapshots:
babel-plugin-jest-hoist@29.6.3:
dependencies:
'@babel/template': 7.27.2
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
'@types/babel__core': 7.20.5
'@types/babel__traverse': 7.28.0
@@ -16516,12 +15862,16 @@ snapshots:
babel-walk@3.0.0-canary-5:
dependencies:
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
bail@2.0.2: {}
balanced-match@1.0.2: {}
+ balanced-match@4.0.2:
+ dependencies:
+ jackspeak: 4.2.3
+
bare-events@2.8.1: {}
base64-js@1.5.1: {}
@@ -16542,7 +15892,7 @@ snapshots:
bin-version-check@5.1.0:
dependencies:
bin-version: 6.0.0
- semver: 7.7.3
+ semver: 7.7.4
semver-truncate: 3.0.0
bin-version@6.0.0:
@@ -16575,15 +15925,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- body-parser@2.2.1:
+ body-parser@2.2.2:
dependencies:
bytes: 3.1.2
content-type: 1.0.5
debug: 4.4.3(supports-color@10.2.2)
- http-errors: 2.0.0
+ http-errors: 2.0.1
iconv-lite: 0.7.0
on-finished: 2.4.1
- qs: 6.14.0
+ qs: 6.14.1
raw-body: 3.0.1
type-is: 2.0.1
transitivePeerDependencies:
@@ -16602,13 +15952,17 @@ snapshots:
dependencies:
balanced-match: 1.0.2
+ brace-expansion@5.0.2:
+ dependencies:
+ balanced-match: 4.0.2
+
braces@3.0.3:
dependencies:
fill-range: 7.1.1
- broadcast-channel@7.2.0:
+ broadcast-channel@7.3.0:
dependencies:
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
oblivious-set: 2.0.0
p-queue: 6.6.2
unload: 2.4.1
@@ -16648,23 +16002,27 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
- bufferutil@4.0.9:
+ bufferutil@4.1.0:
dependencies:
node-gyp-build: 4.8.4
optional: true
- bullmq@5.65.1:
+ bullmq@5.69.4:
dependencies:
cron-parser: 4.9.0
- ioredis: 5.8.2
+ ioredis: 5.9.2
msgpackr: 1.11.5
node-abort-controller: 3.1.1
- semver: 7.7.3
+ semver: 7.7.4
tslib: 2.8.1
uuid: 11.1.0
transitivePeerDependencies:
- supports-color
+ bundle-name@4.1.0:
+ dependencies:
+ run-applescript: 7.1.0
+
buraha@0.0.1: {}
busboy@1.6.0:
@@ -16675,20 +16033,19 @@ snapshots:
bytes@3.1.2: {}
- cacache@19.0.1:
+ cacache@20.0.3:
dependencies:
- '@npmcli/fs': 4.0.0
+ '@npmcli/fs': 5.0.0
fs-minipass: 3.0.3
- glob: 10.5.0
- lru-cache: 10.4.3
+ glob: 13.0.1
+ lru-cache: 11.2.5
minipass: 7.1.2
minipass-collect: 2.0.1
minipass-flush: 1.0.5
minipass-pipeline: 1.2.4
p-map: 7.0.3
- ssri: 12.0.0
- tar: 7.5.2
- unique-filename: 4.0.0
+ ssri: 13.0.0
+ unique-filename: 5.0.0
cacheable-lookup@7.0.0: {}
@@ -16877,7 +16234,7 @@ snapshots:
chownr@3.0.0: {}
- chromatic@13.3.4: {}
+ chromatic@15.1.1: {}
ci-info@3.9.0: {}
@@ -16885,6 +16242,8 @@ snapshots:
cjs-module-lexer@1.4.3: {}
+ cjs-module-lexer@2.2.0: {}
+
clean-stack@2.2.0: {}
cli-cursor@3.1.0:
@@ -17023,8 +16382,8 @@ snapshots:
constantinople@4.0.1:
dependencies:
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
content-disposition@0.5.4:
dependencies:
@@ -17052,7 +16411,7 @@ snapshots:
core-util-is@1.0.3: {}
- cors@2.8.5:
+ cors@2.8.6:
dependencies:
object-assign: 4.1.1
vary: 1.1.2
@@ -17064,13 +16423,13 @@ snapshots:
crc-32: 1.2.2
readable-stream: 4.7.0
- create-jest@29.7.0(@types/node@24.10.2):
+ create-jest@29.7.0(@types/node@24.10.13):
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@24.10.2)
+ jest-config: 29.7.0(@types/node@24.10.13)
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@@ -17175,26 +16534,19 @@ snapshots:
dependencies:
css-tree: 2.2.1
- cssom@0.3.8: {}
-
- cssom@0.5.0: {}
-
- cssstyle@2.3.0:
- dependencies:
- cssom: 0.3.8
-
- cssstyle@5.3.3:
+ cssstyle@5.3.7:
dependencies:
- '@asamuzakjp/css-color': 4.0.5
- '@csstools/css-syntax-patches-for-csstree': 1.0.16
+ '@asamuzakjp/css-color': 4.1.2
+ '@csstools/css-syntax-patches-for-csstree': 1.0.26
css-tree: 3.1.0
+ lru-cache: 11.2.5
optional: true
csstype@3.2.3: {}
- cypress@15.7.1:
+ cypress@15.10.0:
dependencies:
- '@cypress/request': 3.0.9
+ '@cypress/request': 3.0.10
'@cypress/xvfb': 1.2.4(supports-color@8.1.1)
'@types/sinonjs__fake-timers': 8.1.1
'@types/sizzle': 2.3.10
@@ -17222,7 +16574,7 @@ snapshots:
hasha: 5.2.2
is-installed-globally: 0.4.0
listr2: 3.14.0(enquirer@2.4.1)
- lodash: 4.17.21
+ lodash: 4.17.23
log-symbols: 4.1.0
minimist: 1.2.8
ospath: 1.2.2
@@ -17231,7 +16583,7 @@ snapshots:
proxy-from-env: 1.0.0
request-progress: 3.0.0
supports-color: 8.1.1
- systeminformation: 5.27.7
+ systeminformation: 5.31.1
tmp: 0.2.5
tree-kill: 1.2.2
untildify: 4.0.0
@@ -17243,15 +16595,9 @@ snapshots:
data-uri-to-buffer@4.0.1: {}
- data-urls@3.0.2:
+ data-urls@6.0.1:
dependencies:
- abab: 2.0.6
- whatwg-mimetype: 3.0.0
- whatwg-url: 11.0.0
-
- data-urls@6.0.0:
- dependencies:
- whatwg-mimetype: 4.0.0
+ whatwg-mimetype: 5.0.0
whatwg-url: 15.1.0
optional: true
@@ -17314,7 +16660,8 @@ snapshots:
decamelize@1.2.0: {}
- decimal.js@10.6.0: {}
+ decimal.js@10.6.0:
+ optional: true
decode-bmp@0.2.1:
dependencies:
@@ -17377,6 +16724,13 @@ snapshots:
deepmerge@4.3.1: {}
+ default-browser-id@5.0.1: {}
+
+ default-browser@5.4.0:
+ dependencies:
+ bundle-name: 4.1.0
+ default-browser-id: 5.0.1
+
defaults@2.0.2: {}
defer-to-connect@2.0.1: {}
@@ -17387,6 +16741,8 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
+ define-lazy-prop@3.0.0: {}
+
define-properties@1.2.1:
dependencies:
define-data-property: 1.1.4
@@ -17454,12 +16810,6 @@ snapshots:
dom-accessibility-api@0.6.3: {}
- dom-serializer@1.4.1:
- dependencies:
- domelementtype: 2.3.0
- domhandler: 4.3.1
- entities: 2.2.0
-
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
@@ -17468,28 +16818,10 @@ snapshots:
domelementtype@2.3.0: {}
- domexception@4.0.0:
- dependencies:
- webidl-conversions: 7.0.0
-
- domhandler@3.3.0:
- dependencies:
- domelementtype: 2.3.0
-
- domhandler@4.3.1:
- dependencies:
- domelementtype: 2.3.0
-
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
- domutils@2.8.0:
- dependencies:
- dom-serializer: 1.4.1
- domelementtype: 2.3.0
- domhandler: 4.3.1
-
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
@@ -17522,7 +16854,7 @@ snapshots:
'@one-ini/wasm': 0.1.1
commander: 10.0.1
minimatch: 9.0.1
- semver: 7.7.3
+ semver: 7.7.4
ee-first@1.1.1: {}
@@ -17567,7 +16899,7 @@ snapshots:
entities@6.0.1: {}
- entities@7.0.0: {}
+ entities@7.0.1: {}
env-paths@2.2.1: {}
@@ -17683,63 +17015,41 @@ snapshots:
es6-promise: 4.2.8
optional: true
- esbuild@0.25.11:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.11
- '@esbuild/android-arm': 0.25.11
- '@esbuild/android-arm64': 0.25.11
- '@esbuild/android-x64': 0.25.11
- '@esbuild/darwin-arm64': 0.25.11
- '@esbuild/darwin-x64': 0.25.11
- '@esbuild/freebsd-arm64': 0.25.11
- '@esbuild/freebsd-x64': 0.25.11
- '@esbuild/linux-arm': 0.25.11
- '@esbuild/linux-arm64': 0.25.11
- '@esbuild/linux-ia32': 0.25.11
- '@esbuild/linux-loong64': 0.25.11
- '@esbuild/linux-mips64el': 0.25.11
- '@esbuild/linux-ppc64': 0.25.11
- '@esbuild/linux-riscv64': 0.25.11
- '@esbuild/linux-s390x': 0.25.11
- '@esbuild/linux-x64': 0.25.11
- '@esbuild/netbsd-arm64': 0.25.11
- '@esbuild/netbsd-x64': 0.25.11
- '@esbuild/openbsd-arm64': 0.25.11
- '@esbuild/openbsd-x64': 0.25.11
- '@esbuild/openharmony-arm64': 0.25.11
- '@esbuild/sunos-x64': 0.25.11
- '@esbuild/win32-arm64': 0.25.11
- '@esbuild/win32-ia32': 0.25.11
- '@esbuild/win32-x64': 0.25.11
+ esbuild-plugin-swc@1.0.1:
+ dependencies:
+ '@swc/core': 1.15.11
+ deepmerge: 4.3.1
+ transitivePeerDependencies:
+ - '@swc/helpers'
- esbuild@0.27.1:
+ esbuild@0.27.3:
optionalDependencies:
- '@esbuild/aix-ppc64': 0.27.1
- '@esbuild/android-arm': 0.27.1
- '@esbuild/android-arm64': 0.27.1
- '@esbuild/android-x64': 0.27.1
- '@esbuild/darwin-arm64': 0.27.1
- '@esbuild/darwin-x64': 0.27.1
- '@esbuild/freebsd-arm64': 0.27.1
- '@esbuild/freebsd-x64': 0.27.1
- '@esbuild/linux-arm': 0.27.1
- '@esbuild/linux-arm64': 0.27.1
- '@esbuild/linux-ia32': 0.27.1
- '@esbuild/linux-loong64': 0.27.1
- '@esbuild/linux-mips64el': 0.27.1
- '@esbuild/linux-ppc64': 0.27.1
- '@esbuild/linux-riscv64': 0.27.1
- '@esbuild/linux-s390x': 0.27.1
- '@esbuild/linux-x64': 0.27.1
- '@esbuild/netbsd-arm64': 0.27.1
- '@esbuild/netbsd-x64': 0.27.1
- '@esbuild/openbsd-arm64': 0.27.1
- '@esbuild/openbsd-x64': 0.27.1
- '@esbuild/openharmony-arm64': 0.27.1
- '@esbuild/sunos-x64': 0.27.1
- '@esbuild/win32-arm64': 0.27.1
- '@esbuild/win32-ia32': 0.27.1
- '@esbuild/win32-x64': 0.27.1
+ '@esbuild/aix-ppc64': 0.27.3
+ '@esbuild/android-arm': 0.27.3
+ '@esbuild/android-arm64': 0.27.3
+ '@esbuild/android-x64': 0.27.3
+ '@esbuild/darwin-arm64': 0.27.3
+ '@esbuild/darwin-x64': 0.27.3
+ '@esbuild/freebsd-arm64': 0.27.3
+ '@esbuild/freebsd-x64': 0.27.3
+ '@esbuild/linux-arm': 0.27.3
+ '@esbuild/linux-arm64': 0.27.3
+ '@esbuild/linux-ia32': 0.27.3
+ '@esbuild/linux-loong64': 0.27.3
+ '@esbuild/linux-mips64el': 0.27.3
+ '@esbuild/linux-ppc64': 0.27.3
+ '@esbuild/linux-riscv64': 0.27.3
+ '@esbuild/linux-s390x': 0.27.3
+ '@esbuild/linux-x64': 0.27.3
+ '@esbuild/netbsd-arm64': 0.27.3
+ '@esbuild/netbsd-x64': 0.27.3
+ '@esbuild/openbsd-arm64': 0.27.3
+ '@esbuild/openbsd-x64': 0.27.3
+ '@esbuild/openharmony-arm64': 0.27.3
+ '@esbuild/sunos-x64': 0.27.3
+ '@esbuild/win32-arm64': 0.27.3
+ '@esbuild/win32-ia32': 0.27.3
+ '@esbuild/win32-x64': 0.27.3
escalade@3.2.0: {}
@@ -17757,14 +17067,6 @@ snapshots:
escape-string-regexp@5.0.0: {}
- escodegen@2.1.0:
- dependencies:
- esprima: 4.0.1
- estraverse: 5.3.0
- esutils: 2.0.3
- optionalDependencies:
- source-map: 0.6.1
-
eslint-formatter-pretty@4.1.0:
dependencies:
'@types/eslint': 7.29.0
@@ -17784,17 +17086,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.3):
dependencies:
debug: 3.2.7(supports-color@8.1.1)
optionalDependencies:
- '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
- eslint: 9.39.1
+ '@typescript-eslint/parser': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
+ eslint: 9.39.3
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1):
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -17803,9 +17105,9 @@ snapshots:
array.prototype.flatmap: 1.3.3
debug: 3.2.7(supports-color@8.1.1)
doctrine: 2.1.0
- eslint: 9.39.1
+ eslint: 9.39.3
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1)
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.3)
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -17817,25 +17119,25 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-vue@10.6.2(@stylistic/eslint-plugin@5.5.0(eslint@9.39.1))(@typescript-eslint/parser@8.49.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(vue-eslint-parser@10.2.0(eslint@9.39.1)):
+ eslint-plugin-vue@10.8.0(@stylistic/eslint-plugin@5.5.0(eslint@9.39.3))(@typescript-eslint/parser@8.56.0(eslint@9.39.3)(typescript@5.9.3))(eslint@9.39.3)(vue-eslint-parser@10.4.0(eslint@9.39.3)):
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
- eslint: 9.39.1
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3)
+ eslint: 9.39.3
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 7.1.0
- semver: 7.7.3
- vue-eslint-parser: 10.2.0(eslint@9.39.1)
+ semver: 7.7.4
+ vue-eslint-parser: 10.4.0(eslint@9.39.3)
xml-name-validator: 4.0.0
optionalDependencies:
- '@stylistic/eslint-plugin': 5.5.0(eslint@9.39.1)
- '@typescript-eslint/parser': 8.49.0(eslint@9.39.1)(typescript@5.9.3)
+ '@stylistic/eslint-plugin': 5.5.0(eslint@9.39.3)
+ '@typescript-eslint/parser': 8.56.0(eslint@9.39.3)(typescript@5.9.3)
eslint-rule-docs@1.1.235: {}
@@ -17848,15 +17150,17 @@ snapshots:
eslint-visitor-keys@4.2.1: {}
- eslint@9.39.1:
+ eslint-visitor-keys@5.0.1: {}
+
+ eslint@9.39.3:
dependencies:
- '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1)
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3)
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.1
'@eslint/config-helpers': 0.4.2
'@eslint/core': 0.17.0
'@eslint/eslintrc': 3.3.1
- '@eslint/js': 9.39.1
+ '@eslint/js': 9.39.3
'@eslint/plugin-kit': 0.4.1
'@humanfs/node': 0.16.7
'@humanwhocodes/module-importer': 1.0.1
@@ -17891,8 +17195,8 @@ snapshots:
espree@10.4.0:
dependencies:
- acorn: 8.15.0
- acorn-jsx: 5.3.2(acorn@8.15.0)
+ acorn: 8.16.0
+ acorn-jsx: 5.3.2(acorn@8.16.0)
eslint-visitor-keys: 4.2.1
esprima@4.0.1: {}
@@ -17933,7 +17237,7 @@ snapshots:
eventemitter3@4.0.7: {}
- eventemitter3@5.0.1: {}
+ eventemitter3@5.0.4: {}
events-universal@1.0.1:
dependencies:
@@ -17998,7 +17302,7 @@ snapshots:
dependencies:
pify: 2.3.0
- exifreader@4.33.1:
+ exifreader@4.36.2:
optionalDependencies:
'@xmldom/xmldom': 0.9.8
@@ -18052,28 +17356,29 @@ snapshots:
transitivePeerDependencies:
- supports-color
- express@5.1.0:
+ express@5.2.1:
dependencies:
accepts: 2.0.0
- body-parser: 2.2.1
+ body-parser: 2.2.2
content-disposition: 1.0.1
content-type: 1.0.5
cookie: 0.7.1
cookie-signature: 1.2.2
debug: 4.4.3(supports-color@10.2.2)
+ depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 2.1.0
fresh: 2.0.0
- http-errors: 2.0.0
+ http-errors: 2.0.1
merge-descriptors: 2.0.0
mime-types: 3.0.2
on-finished: 2.4.1
once: 1.4.0
parseurl: 1.3.3
proxy-addr: 2.0.7
- qs: 6.14.0
+ qs: 6.14.1
range-parser: 1.2.1
router: 2.2.0
send: 1.2.0
@@ -18128,8 +17433,8 @@ snapshots:
fast-json-stringify@6.1.1:
dependencies:
'@fastify/merge-json-schemas': 0.2.1
- ajv: 8.17.1
- ajv-formats: 3.0.1(ajv@8.17.1)
+ ajv: 8.18.0
+ ajv-formats: 3.0.1(ajv@8.18.0)
fast-uri: 3.1.0
json-schema-ref-resolver: 3.0.0
rfdc: 1.4.1
@@ -18144,9 +17449,9 @@ snapshots:
fast-uri@3.1.0: {}
- fast-xml-parser@5.2.5:
+ fast-xml-parser@5.3.6:
dependencies:
- strnum: 2.1.1
+ strnum: 2.1.2
fastify-plugin@5.1.0: {}
@@ -18156,7 +17461,7 @@ snapshots:
raw-body: 3.0.1
secure-json-parse: 2.7.0
- fastify@5.6.2:
+ fastify@5.7.4:
dependencies:
'@fastify/ajv-compiler': 4.0.5
'@fastify/error': 4.2.0
@@ -18171,10 +17476,10 @@ snapshots:
process-warning: 5.0.0
rfdc: 1.4.1
secure-json-parse: 4.1.0
- semver: 7.7.3
+ semver: 7.7.4
toad-cache: 3.7.0
- fastq@1.19.1:
+ fastq@1.20.1:
dependencies:
reusify: 1.1.0
@@ -18190,7 +17495,7 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
- feed@5.1.0:
+ feed@5.2.0:
dependencies:
xml-js: 1.6.11
@@ -18222,16 +17527,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- file-type@21.1.0:
- dependencies:
- '@tokenizer/inflate': 0.3.1
- strtok3: 10.3.4
- token-types: 6.1.1
- uint8array-extras: 1.5.0
- transitivePeerDependencies:
- - supports-color
-
- file-type@21.1.1:
+ file-type@21.3.0:
dependencies:
'@tokenizer/inflate': 0.4.1
strtok3: 10.3.4
@@ -18293,10 +17589,10 @@ snapshots:
dependencies:
semver-regex: 4.0.5
- fkill@10.0.1:
+ fkill@10.0.3:
dependencies:
execa: 9.6.1
- pid-port: 2.0.0
+ pid-port: 2.0.1
process-exists: 5.0.0
ps-list: 9.0.0
taskkill: 5.0.0
@@ -18489,13 +17785,10 @@ snapshots:
package-json-from-dist: 1.0.1
path-scurry: 1.11.1
- glob@11.1.0:
+ glob@13.0.1:
dependencies:
- foreground-child: 3.3.1
- jackspeak: 4.1.1
- minimatch: 10.1.1
+ minimatch: 10.2.4
minipass: 7.1.2
- package-json-from-dist: 1.0.1
path-scurry: 2.0.1
glob@7.2.3:
@@ -18513,7 +17806,7 @@ snapshots:
globals@14.0.0: {}
- globals@16.5.0: {}
+ globals@17.3.0: {}
globalthis@1.0.4:
dependencies:
@@ -18548,7 +17841,7 @@ snapshots:
p-cancelable: 3.0.0
responselike: 3.0.0
- got@14.6.5:
+ got@14.6.6:
dependencies:
'@sindresorhus/is': 7.1.1
byte-counter: 0.1.0
@@ -18569,15 +17862,21 @@ snapshots:
hammerjs@2.0.8: {}
- happy-dom@20.0.11:
+ happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6):
dependencies:
- '@types/node': 20.19.25
+ '@types/node': 24.10.13
'@types/whatwg-mimetype': 3.0.2
+ '@types/ws': 8.18.1
+ entities: 7.0.1
whatwg-mimetype: 3.0.0
+ ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
hard-rejection@2.1.0: {}
- harfbuzzjs@0.4.13: {}
+ harfbuzzjs@0.8.0: {}
has-bigints@1.1.0: {}
@@ -18645,10 +17944,6 @@ snapshots:
hpagent@1.2.0: {}
- html-encoding-sniffer@3.0.0:
- dependencies:
- whatwg-encoding: 2.0.0
-
html-encoding-sniffer@4.0.0:
dependencies:
whatwg-encoding: 3.1.1
@@ -18667,13 +17962,6 @@ snapshots:
domutils: 3.2.2
entities: 6.0.1
- htmlparser2@5.0.1:
- dependencies:
- domelementtype: 2.3.0
- domhandler: 3.3.0
- domutils: 2.8.0
- entities: 2.2.0
-
htmlparser2@8.0.2:
dependencies:
domelementtype: 2.3.0
@@ -18698,15 +17986,15 @@ snapshots:
statuses: 2.0.1
toidentifier: 1.0.1
- http-link-header@1.1.3: {}
-
- http-proxy-agent@5.0.0:
+ http-errors@2.0.1:
dependencies:
- '@tootallnate/once': 2.0.0
- agent-base: 6.0.2
- debug: 4.4.3(supports-color@10.2.2)
- transitivePeerDependencies:
- - supports-color
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.2
+ toidentifier: 1.0.1
+
+ http-link-header@1.1.3: {}
http-proxy-agent@7.0.2:
dependencies:
@@ -18742,6 +18030,7 @@ snapshots:
debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies:
- supports-color
+ optional: true
https-proxy-agent@7.0.6(supports-color@10.2.2):
dependencies:
@@ -18778,7 +18067,7 @@ snapshots:
ignore-walk@8.0.0:
dependencies:
- minimatch: 10.1.1
+ minimatch: 10.2.4
ignore@5.3.2: {}
@@ -18791,11 +18080,11 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
- import-in-the-middle@2.0.0:
+ import-in-the-middle@2.0.6:
dependencies:
- acorn: 8.15.0
- acorn-import-attributes: 1.9.5(acorn@8.15.0)
- cjs-module-lexer: 1.4.3
+ acorn: 8.16.0
+ acorn-import-attributes: 1.9.5(acorn@8.16.0)
+ cjs-module-lexer: 2.2.0
module-details-from-path: 1.0.4
import-lazy@4.0.0: {}
@@ -18838,9 +18127,23 @@ snapshots:
intersection-observer@0.12.2: {}
- ioredis@5.8.2:
+ ioredis@5.9.2:
dependencies:
- '@ioredis/commands': 1.4.0
+ '@ioredis/commands': 1.5.0
+ cluster-key-slot: 1.1.2
+ debug: 4.4.3(supports-color@10.2.2)
+ denque: 2.1.0
+ lodash.defaults: 4.2.0
+ lodash.isarguments: 3.1.0
+ redis-errors: 1.2.0
+ redis-parser: 3.0.0
+ standard-as-callback: 2.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ ioredis@5.9.3:
+ dependencies:
+ '@ioredis/commands': 1.5.0
cluster-key-slot: 1.1.2
debug: 4.4.3(supports-color@10.2.2)
denque: 2.1.0
@@ -18922,6 +18225,8 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
+ is-docker@3.0.0: {}
+
is-expression@4.0.0:
dependencies:
acorn: 7.4.1
@@ -18951,6 +18256,10 @@ snapshots:
dependencies:
is-extglob: 2.1.1
+ is-inside-container@1.0.0:
+ dependencies:
+ is-docker: 3.0.0
+
is-installed-globally@0.4.0:
dependencies:
global-dirs: 3.0.1
@@ -18977,7 +18286,8 @@ snapshots:
is-plain-object@5.0.0: {}
- is-potential-custom-element-name@1.0.1: {}
+ is-potential-custom-element-name@1.0.1:
+ optional: true
is-promise@2.2.2: {}
@@ -19038,6 +18348,10 @@ snapshots:
call-bound: 1.0.4
get-intrinsic: 1.3.0
+ is-wsl@3.1.0:
+ dependencies:
+ is-inside-container: 1.0.0
+
isarray@1.0.0: {}
isarray@2.0.5: {}
@@ -19053,7 +18367,7 @@ snapshots:
istanbul-lib-instrument@5.2.1:
dependencies:
'@babel/core': 7.28.5
- '@babel/parser': 7.28.5
+ '@babel/parser': 7.29.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
@@ -19063,10 +18377,10 @@ snapshots:
istanbul-lib-instrument@6.0.3:
dependencies:
'@babel/core': 7.28.5
- '@babel/parser': 7.28.5
+ '@babel/parser': 7.29.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
- semver: 7.7.3
+ semver: 7.7.4
transitivePeerDependencies:
- supports-color
@@ -19084,14 +18398,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- istanbul-lib-source-maps@5.0.6:
- dependencies:
- '@jridgewell/trace-mapping': 0.3.31
- debug: 4.4.3(supports-color@10.2.2)
- istanbul-lib-coverage: 3.2.2
- transitivePeerDependencies:
- - supports-color
-
istanbul-reports@3.2.0:
dependencies:
html-escaper: 2.0.2
@@ -19105,9 +18411,9 @@ snapshots:
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
- jackspeak@4.1.1:
+ jackspeak@4.2.3:
dependencies:
- '@isaacs/cliui': 8.0.2
+ '@isaacs/cliui': 9.0.0
jest-changed-files@29.7.0:
dependencies:
@@ -19121,7 +18427,7 @@ snapshots:
'@jest/expect': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
chalk: 4.1.2
co: 4.6.0
dedent: 1.7.0
@@ -19141,16 +18447,16 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-cli@29.7.0(@types/node@24.10.2):
+ jest-cli@29.7.0(@types/node@24.10.13):
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@24.10.2)
+ create-jest: 29.7.0(@types/node@24.10.13)
exit: 0.1.2
import-local: 3.2.0
- jest-config: 29.7.0(@types/node@24.10.2)
+ jest-config: 29.7.0(@types/node@24.10.13)
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@@ -19160,7 +18466,7 @@ snapshots:
- supports-color
- ts-node
- jest-config@29.7.0(@types/node@24.10.2):
+ jest-config@29.7.0(@types/node@24.10.13):
dependencies:
'@babel/core': 7.28.5
'@jest/test-sequencer': 29.7.0
@@ -19185,7 +18491,7 @@ snapshots:
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -19209,27 +18515,12 @@ snapshots:
jest-util: 29.7.0
pretty-format: 29.7.0
- jest-environment-jsdom@29.7.0(bufferutil@4.0.9)(utf-8-validate@6.0.5):
- dependencies:
- '@jest/environment': 29.7.0
- '@jest/fake-timers': 29.7.0
- '@jest/types': 29.6.3
- '@types/jsdom': 20.0.1
- '@types/node': 24.10.2
- jest-mock: 29.7.0
- jest-util: 29.7.0
- jsdom: 20.0.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
- transitivePeerDependencies:
- - bufferutil
- - supports-color
- - utf-8-validate
-
jest-environment-node@29.7.0:
dependencies:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -19239,7 +18530,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -19278,7 +18569,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
jest-util: 29.7.0
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -19315,7 +18606,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@@ -19343,7 +18634,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
chalk: 4.1.2
cjs-module-lexer: 1.4.3
collect-v8-coverage: 1.0.3
@@ -19367,7 +18658,7 @@ snapshots:
'@babel/generator': 7.28.5
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5)
'@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5)
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
'@jest/expect-utils': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
@@ -19382,14 +18673,14 @@ snapshots:
jest-util: 29.7.0
natural-compare: 1.4.0
pretty-format: 29.7.0
- semver: 7.7.3
+ semver: 7.7.4
transitivePeerDependencies:
- supports-color
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -19408,7 +18699,7 @@ snapshots:
dependencies:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@@ -19417,17 +18708,17 @@ snapshots:
jest-worker@29.7.0:
dependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
- jest@29.7.0(@types/node@24.10.2):
+ jest@29.7.0(@types/node@24.10.13):
dependencies:
'@jest/core': 29.7.0
'@jest/types': 29.6.3
import-local: 3.2.0
- jest-cli: 29.7.0(@types/node@24.10.2)
+ jest-cli: 29.7.0(@types/node@24.10.13)
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@@ -19487,45 +18778,12 @@ snapshots:
jschardet@3.1.4: {}
- jsdom@20.0.3(bufferutil@4.0.9)(utf-8-validate@6.0.5):
+ jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6):
dependencies:
- abab: 2.0.6
- acorn: 8.15.0
- acorn-globals: 7.0.1
- cssom: 0.5.0
- cssstyle: 2.3.0
- data-urls: 3.0.2
- decimal.js: 10.6.0
- domexception: 4.0.0
- escodegen: 2.1.0
- form-data: 4.0.5
- html-encoding-sniffer: 3.0.0
- http-proxy-agent: 5.0.0
- https-proxy-agent: 5.0.1
- is-potential-custom-element-name: 1.0.1
- nwsapi: 2.2.23
- parse5: 7.3.0
- saxes: 6.0.0
- symbol-tree: 3.2.4
- tough-cookie: 4.1.4
- w3c-xmlserializer: 4.0.0
- webidl-conversions: 7.0.0
- whatwg-encoding: 2.0.0
- whatwg-mimetype: 3.0.0
- whatwg-url: 11.0.0
- ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
- xml-name-validator: 4.0.0
- transitivePeerDependencies:
- - bufferutil
- - supports-color
- - utf-8-validate
-
- jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5):
- dependencies:
- '@acemir/cssom': 0.9.23
- '@asamuzakjp/dom-selector': 6.7.4
- cssstyle: 5.3.3
- data-urls: 6.0.0
+ '@acemir/cssom': 0.9.31
+ '@asamuzakjp/dom-selector': 6.7.8
+ cssstyle: 5.3.7
+ data-urls: 6.0.1
decimal.js: 10.6.0
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
@@ -19536,11 +18794,11 @@ snapshots:
symbol-tree: 3.2.4
tough-cookie: 6.0.0
w3c-xmlserializer: 5.0.0
- webidl-conversions: 8.0.0
+ webidl-conversions: 8.0.1
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 15.1.0
- ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
@@ -19620,14 +18878,14 @@ snapshots:
is-promise: 2.2.2
promise: 7.3.1
- juice@11.0.3:
+ juice@11.1.1:
dependencies:
cheerio: 1.0.0
commander: 12.1.0
- entities: 7.0.0
+ entities: 7.0.1
mensch: 0.3.4
slick: 1.12.2
- web-resource-inliner: 7.0.0
+ web-resource-inliner: 8.0.0
just-extend@6.2.0: {}
@@ -19714,7 +18972,7 @@ snapshots:
lodash.uniq@4.5.0: {}
- lodash@4.17.21: {}
+ lodash@4.17.23: {}
log-symbols@4.1.0:
dependencies:
@@ -19738,7 +18996,7 @@ snapshots:
lru-cache@10.4.3: {}
- lru-cache@11.2.2: {}
+ lru-cache@11.2.5: {}
lru-cache@5.1.1:
dependencies:
@@ -19760,8 +19018,8 @@ snapshots:
magicast@0.5.1:
dependencies:
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
source-map-js: 1.2.1
mailcheck@1.1.1: {}
@@ -19773,21 +19031,21 @@ snapshots:
make-dir@4.0.0:
dependencies:
- semver: 7.7.3
+ semver: 7.7.4
- make-fetch-happen@14.0.3:
+ make-fetch-happen@15.0.3:
dependencies:
- '@npmcli/agent': 3.0.0
- cacache: 19.0.1
+ '@npmcli/agent': 4.0.0
+ cacache: 20.0.3
http-cache-semantics: 4.2.0
minipass: 7.1.2
- minipass-fetch: 4.0.1
+ minipass-fetch: 5.0.0
minipass-flush: 1.0.5
minipass-pipeline: 1.2.4
negotiator: 1.0.0
- proc-log: 5.0.0
+ proc-log: 6.1.0
promise-retry: 2.0.1
- ssri: 12.0.0
+ ssri: 13.0.0
transitivePeerDependencies:
- supports-color
@@ -19931,12 +19189,12 @@ snapshots:
media-typer@1.1.0: {}
- mediabunny@1.25.8:
+ mediabunny@1.34.4:
dependencies:
'@types/dom-mediacapture-transform': 0.1.11
'@types/dom-webcodecs': 0.1.13
- meilisearch@0.54.0: {}
+ meilisearch@0.55.0: {}
memoizerific@1.11.3:
dependencies:
@@ -20199,13 +19457,13 @@ snapshots:
minimalistic-assert@1.0.1: {}
- minimatch@10.0.3:
+ minimatch@10.2.1:
dependencies:
- '@isaacs/brace-expansion': 5.0.0
+ brace-expansion: 5.0.2
- minimatch@10.1.1:
+ minimatch@10.2.4:
dependencies:
- '@isaacs/brace-expansion': 5.0.0
+ brace-expansion: 5.0.2
minimatch@3.1.2:
dependencies:
@@ -20235,7 +19493,7 @@ snapshots:
dependencies:
minipass: 7.1.2
- minipass-fetch@4.0.1:
+ minipass-fetch@5.0.0:
dependencies:
minipass: 7.1.2
minipass-sized: 1.0.3
@@ -20307,15 +19565,15 @@ snapshots:
optionalDependencies:
msgpackr-extract: 3.0.3
- msw-storybook-addon@2.0.6(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3)):
+ msw-storybook-addon@2.0.6(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3)):
dependencies:
is-node-process: 1.2.0
- msw: 2.12.4(@types/node@24.10.2)(typescript@5.9.3)
+ msw: 2.12.10(@types/node@24.10.13)(typescript@5.9.3)
- msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3):
+ msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3):
dependencies:
- '@inquirer/confirm': 5.1.21(@types/node@24.10.2)
- '@mswjs/interceptors': 0.40.0
+ '@inquirer/confirm': 5.1.21(@types/node@24.10.13)
+ '@mswjs/interceptors': 0.41.2
'@open-draft/deferred-promise': 2.2.0
'@types/statuses': 2.0.6
cookie: 1.0.2
@@ -20325,7 +19583,7 @@ snapshots:
outvariant: 1.4.3
path-to-regexp: 6.3.0
picocolors: 1.1.1
- rettime: 0.7.0
+ rettime: 0.10.1
statuses: 2.0.2
strict-event-emitter: 0.5.1
tough-cookie: 6.0.0
@@ -20353,7 +19611,7 @@ snapshots:
mylas@2.1.14: {}
- nan@2.23.1: {}
+ nan@2.25.0: {}
nanoid@3.3.11: {}
@@ -20389,7 +19647,7 @@ snapshots:
node-abi@3.85.0:
dependencies:
- semver: 7.7.3
+ semver: 7.7.4
node-abort-controller@3.1.1: {}
@@ -20425,22 +19683,22 @@ snapshots:
node-gyp-build@4.8.4:
optional: true
- node-gyp@11.5.0:
+ node-gyp@12.2.0:
dependencies:
env-paths: 2.2.1
exponential-backoff: 3.1.3
graceful-fs: 4.2.11
- make-fetch-happen: 14.0.3
- nopt: 8.1.0
- proc-log: 5.0.0
- semver: 7.7.3
- tar: 7.5.2
+ make-fetch-happen: 15.0.3
+ nopt: 9.0.0
+ proc-log: 6.1.0
+ semver: 7.7.4
+ tar: 7.5.9
tinyglobby: 0.2.15
- which: 5.0.0
+ which: 6.0.0
transitivePeerDependencies:
- supports-color
- node-html-parser@7.0.1:
+ node-html-parser@7.0.2:
dependencies:
css-select: 5.2.2
he: 1.2.0
@@ -20449,16 +19707,16 @@ snapshots:
node-releases@2.0.27: {}
- nodemailer@7.0.11: {}
+ nodemailer@8.0.1: {}
- nodemon@3.1.11:
+ nodemon@3.1.14:
dependencies:
chokidar: 5.0.0
debug: 4.4.3(supports-color@5.5.0)
ignore-by-default: 1.0.1
- minimatch: 3.1.2
+ minimatch: 10.2.4
pstree.remy: 1.1.8
- semver: 7.7.3
+ semver: 7.7.4
simple-update-notifier: 2.0.0
supports-color: 5.5.0
touch: 3.1.1
@@ -20475,9 +19733,9 @@ snapshots:
dependencies:
abbrev: 2.0.0
- nopt@8.1.0:
+ nopt@9.0.0:
dependencies:
- abbrev: 3.0.1
+ abbrev: 4.0.0
normalize-package-data@2.5.0:
dependencies:
@@ -20490,7 +19748,7 @@ snapshots:
dependencies:
hosted-git-info: 4.1.0
is-core-module: 2.16.1
- semver: 7.7.3
+ semver: 7.7.4
validate-npm-package-license: 3.0.4
normalize-path@3.0.0: {}
@@ -20527,8 +19785,6 @@ snapshots:
dependencies:
boolbase: 1.0.0
- nwsapi@2.2.23: {}
-
oauth2orize-pkce@0.1.2: {}
oauth2orize@1.12.0:
@@ -20609,11 +19865,18 @@ snapshots:
regex: 6.0.1
regex-recursion: 6.0.2
+ open@10.2.0:
+ dependencies:
+ default-browser: 5.4.0
+ define-lazy-prop: 3.0.0
+ is-inside-container: 1.0.0
+ wsl-utils: 0.1.0
+
openapi-types@12.1.3: {}
- openapi-typescript@7.10.1(typescript@5.9.3):
+ openapi-typescript@7.13.0(typescript@5.9.3):
dependencies:
- '@redocly/openapi-core': 1.34.5(supports-color@10.2.2)
+ '@redocly/openapi-core': 1.34.6(supports-color@10.2.2)
ansi-colors: 4.1.3
change-case: 5.4.4
parse-json: 8.3.0
@@ -20634,9 +19897,9 @@ snapshots:
ospath@1.2.2: {}
- otpauth@9.4.1:
+ otpauth@9.5.0:
dependencies:
- '@noble/hashes': 1.8.0
+ '@noble/hashes': 2.0.1
outvariant@1.4.3: {}
@@ -20747,7 +20010,7 @@ snapshots:
path-scurry@2.0.1:
dependencies:
- lru-cache: 11.2.2
+ lru-cache: 11.2.5
minipass: 7.1.2
path-to-regexp@0.1.12: {}
@@ -20770,18 +20033,18 @@ snapshots:
performance-now@2.1.0: {}
- pg-cloudflare@1.2.7:
+ pg-cloudflare@1.3.0:
optional: true
- pg-connection-string@2.9.1: {}
+ pg-connection-string@2.11.0: {}
pg-int8@1.0.1: {}
- pg-pool@3.10.1(pg@8.16.3):
+ pg-pool@3.11.0(pg@8.18.0):
dependencies:
- pg: 8.16.3
+ pg: 8.18.0
- pg-protocol@1.10.3: {}
+ pg-protocol@1.11.0: {}
pg-types@2.2.0:
dependencies:
@@ -20791,15 +20054,15 @@ snapshots:
postgres-date: 1.0.7
postgres-interval: 1.2.0
- pg@8.16.3:
+ pg@8.18.0:
dependencies:
- pg-connection-string: 2.9.1
- pg-pool: 3.10.1(pg@8.16.3)
- pg-protocol: 1.10.3
+ pg-connection-string: 2.11.0
+ pg-pool: 3.11.0(pg@8.18.0)
+ pg-protocol: 1.11.0
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
- pg-cloudflare: 1.2.7
+ pg-cloudflare: 1.3.0
pgpass@1.0.5:
dependencies:
@@ -20813,7 +20076,7 @@ snapshots:
picomatch@4.0.3: {}
- pid-port@2.0.0:
+ pid-port@2.0.1:
dependencies:
execa: 9.6.1
@@ -20845,7 +20108,7 @@ snapshots:
optionalDependencies:
'@napi-rs/nice': 1.1.1
- pkce-challenge@5.0.1: {}
+ pkce-challenge@6.0.0: {}
pkg-dir@4.2.0:
dependencies:
@@ -20863,11 +20126,11 @@ snapshots:
pngjs@5.0.0: {}
- pnpm@10.25.0: {}
+ pnpm@10.30.1: {}
polished@4.3.1:
dependencies:
- '@babel/runtime': 7.28.4
+ '@babel/runtime': 7.28.6
possible-typed-array-names@1.1.0: {}
@@ -21045,7 +20308,7 @@ snapshots:
prelude-ls@1.2.1: {}
- prettier@3.7.4: {}
+ prettier@3.8.1: {}
pretty-bytes@5.6.0: {}
@@ -21080,7 +20343,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- proc-log@5.0.0: {}
+ proc-log@6.1.0: {}
process-exists@5.0.0:
dependencies:
@@ -21134,10 +20397,6 @@ snapshots:
dependencies:
event-stream: 3.3.4
- psl@1.15.0:
- dependencies:
- punycode: 2.3.1
-
pstree.remy@1.1.8: {}
pug-attrs@3.0.0:
@@ -21244,12 +20503,10 @@ snapshots:
dependencies:
side-channel: 1.1.0
- qs@6.14.0:
+ qs@6.14.1:
dependencies:
side-channel: 1.1.0
- querystringify@2.2.0: {}
-
queue-lit@1.5.2: {}
queue-microtask@1.2.3: {}
@@ -21286,11 +20543,11 @@ snapshots:
dependencies:
setimmediate: 1.0.5
- re2@1.22.3:
+ re2@1.23.3:
dependencies:
install-artifact-from-github: 1.4.0
- nan: 2.23.1
- node-gyp: 11.5.0
+ nan: 2.25.0
+ node-gyp: 12.2.0
transitivePeerDependencies:
- supports-color
@@ -21302,7 +20559,7 @@ snapshots:
dependencies:
'@babel/core': 7.28.5
'@babel/traverse': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/types': 7.29.0
'@types/babel__core': 7.20.5
'@types/babel__traverse': 7.28.0
'@types/doctrine': 0.0.9
@@ -21313,16 +20570,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
- react-dom@19.2.1(react@19.2.1):
+ react-dom@19.2.4(react@19.2.4):
dependencies:
- react: 19.2.1
+ react: 19.2.4
scheduler: 0.27.0
react-is@17.0.2: {}
react-is@18.3.1: {}
- react@19.2.1: {}
+ react@19.2.4: {}
read-pkg-up@7.0.1:
dependencies:
@@ -21386,10 +20643,6 @@ snapshots:
redis-errors@1.2.0: {}
- redis-info@3.1.0:
- dependencies:
- lodash: 4.17.21
-
redis-parser@3.0.0:
dependencies:
redis-errors: 1.2.0
@@ -21477,8 +20730,6 @@ snapshots:
require-main-filename@2.0.0: {}
- requires-port@1.0.0: {}
-
resolve-alpn@1.2.1: {}
resolve-cwd@3.0.0:
@@ -21516,7 +20767,7 @@ snapshots:
retry@0.12.0: {}
- rettime@0.7.0: {}
+ rettime@0.10.1: {}
reusify@1.1.0: {}
@@ -21532,32 +20783,35 @@ snapshots:
glob: 7.2.3
optional: true
- rollup@4.53.3:
+ rollup@4.59.0:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.53.3
- '@rollup/rollup-android-arm64': 4.53.3
- '@rollup/rollup-darwin-arm64': 4.53.3
- '@rollup/rollup-darwin-x64': 4.53.3
- '@rollup/rollup-freebsd-arm64': 4.53.3
- '@rollup/rollup-freebsd-x64': 4.53.3
- '@rollup/rollup-linux-arm-gnueabihf': 4.53.3
- '@rollup/rollup-linux-arm-musleabihf': 4.53.3
- '@rollup/rollup-linux-arm64-gnu': 4.53.3
- '@rollup/rollup-linux-arm64-musl': 4.53.3
- '@rollup/rollup-linux-loong64-gnu': 4.53.3
- '@rollup/rollup-linux-ppc64-gnu': 4.53.3
- '@rollup/rollup-linux-riscv64-gnu': 4.53.3
- '@rollup/rollup-linux-riscv64-musl': 4.53.3
- '@rollup/rollup-linux-s390x-gnu': 4.53.3
- '@rollup/rollup-linux-x64-gnu': 4.53.3
- '@rollup/rollup-linux-x64-musl': 4.53.3
- '@rollup/rollup-openharmony-arm64': 4.53.3
- '@rollup/rollup-win32-arm64-msvc': 4.53.3
- '@rollup/rollup-win32-ia32-msvc': 4.53.3
- '@rollup/rollup-win32-x64-gnu': 4.53.3
- '@rollup/rollup-win32-x64-msvc': 4.53.3
+ '@rollup/rollup-android-arm-eabi': 4.59.0
+ '@rollup/rollup-android-arm64': 4.59.0
+ '@rollup/rollup-darwin-arm64': 4.59.0
+ '@rollup/rollup-darwin-x64': 4.59.0
+ '@rollup/rollup-freebsd-arm64': 4.59.0
+ '@rollup/rollup-freebsd-x64': 4.59.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.59.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.59.0
+ '@rollup/rollup-linux-arm64-gnu': 4.59.0
+ '@rollup/rollup-linux-arm64-musl': 4.59.0
+ '@rollup/rollup-linux-loong64-gnu': 4.59.0
+ '@rollup/rollup-linux-loong64-musl': 4.59.0
+ '@rollup/rollup-linux-ppc64-gnu': 4.59.0
+ '@rollup/rollup-linux-ppc64-musl': 4.59.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.59.0
+ '@rollup/rollup-linux-riscv64-musl': 4.59.0
+ '@rollup/rollup-linux-s390x-gnu': 4.59.0
+ '@rollup/rollup-linux-x64-gnu': 4.59.0
+ '@rollup/rollup-linux-x64-musl': 4.59.0
+ '@rollup/rollup-openbsd-x64': 4.59.0
+ '@rollup/rollup-openharmony-arm64': 4.59.0
+ '@rollup/rollup-win32-arm64-msvc': 4.59.0
+ '@rollup/rollup-win32-ia32-msvc': 4.59.0
+ '@rollup/rollup-win32-x64-gnu': 4.59.0
+ '@rollup/rollup-win32-x64-msvc': 4.59.0
fsevents: 2.3.3
router@2.2.0:
@@ -21575,6 +20829,8 @@ snapshots:
entities: 2.2.0
xml2js: 0.5.0
+ run-applescript@7.1.0: {}
+
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@@ -21614,7 +20870,7 @@ snapshots:
safer-buffer@2.1.2: {}
- sanitize-html@2.17.0:
+ sanitize-html@2.17.1:
dependencies:
deepmerge: 4.3.1
escape-string-regexp: 4.0.0
@@ -21623,7 +20879,7 @@ snapshots:
parse-srcset: 1.0.2
postcss: 8.5.6
- sass@1.95.1:
+ sass@1.97.3:
dependencies:
chokidar: 5.0.0
immutable: 5.1.4
@@ -21636,6 +20892,7 @@ snapshots:
saxes@6.0.0:
dependencies:
xmlchars: 2.2.0
+ optional: true
scheduler@0.27.0: {}
@@ -21653,7 +20910,7 @@ snapshots:
semver-truncate@3.0.0:
dependencies:
- semver: 7.7.3
+ semver: 7.7.4
semver@5.7.2: {}
@@ -21663,7 +20920,7 @@ snapshots:
dependencies:
lru-cache: 6.0.0
- semver@7.7.3: {}
+ semver@7.7.4: {}
send@0.19.0:
dependencies:
@@ -21690,7 +20947,7 @@ snapshots:
escape-html: 1.0.3
etag: 1.8.1
fresh: 2.0.0
- http-errors: 2.0.0
+ http-errors: 2.0.1
mime-types: 3.0.2
ms: 2.1.3
on-finished: 2.4.1
@@ -21757,7 +21014,7 @@ snapshots:
dependencies:
color: 4.2.3
detect-libc: 2.1.2
- semver: 7.7.3
+ semver: 7.7.4
optionalDependencies:
'@img/sharp-darwin-arm64': 0.33.5
'@img/sharp-darwin-x64': 0.33.5
@@ -21785,14 +21042,14 @@ snapshots:
shebang-regex@3.0.0: {}
- shiki@3.19.0:
+ shiki@3.22.0:
dependencies:
- '@shikijs/core': 3.19.0
- '@shikijs/engine-javascript': 3.19.0
- '@shikijs/engine-oniguruma': 3.19.0
- '@shikijs/langs': 3.19.0
- '@shikijs/themes': 3.19.0
- '@shikijs/types': 3.19.0
+ '@shikijs/core': 3.22.0
+ '@shikijs/engine-javascript': 3.22.0
+ '@shikijs/engine-oniguruma': 3.22.0
+ '@shikijs/langs': 3.22.0
+ '@shikijs/themes': 3.22.0
+ '@shikijs/types': 3.22.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
@@ -21845,7 +21102,7 @@ snapshots:
simple-update-notifier@2.0.0:
dependencies:
- semver: 7.7.3
+ semver: 7.7.4
sinon@18.0.1:
dependencies:
@@ -21956,8 +21213,6 @@ snapshots:
dependencies:
is-plain-obj: 1.1.0
- sortablejs@1.14.0: {}
-
source-map-js@1.2.1: {}
source-map-support@0.5.13:
@@ -22014,7 +21269,7 @@ snapshots:
safer-buffer: 2.1.2
tweetnacl: 0.14.5
- ssri@12.0.0:
+ ssri@13.0.0:
dependencies:
minipass: 7.1.2
@@ -22050,34 +21305,35 @@ snapshots:
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(62d43c4a6ba7695d11b4f3f5e39354d0):
+ storybook-addon-misskey-theme@https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640(4a35f3b5099288de1d7ef0cf5b586d03):
dependencies:
- '@storybook/blocks': 8.6.14(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/components': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/core-events': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/manager-api': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/preview-api': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/theming': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
- '@storybook/types': 8.6.14(storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5))
+ '@storybook/blocks': 8.6.17(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/components': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/core-events': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/manager-api': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/preview-api': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/theming': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
+ '@storybook/types': 8.6.17(storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6))
optionalDependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.4
+ react-dom: 19.2.4(react@19.2.4)
- storybook@10.1.5(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.7.4)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(utf-8-validate@6.0.5):
+ storybook@10.2.10(@testing-library/dom@10.4.0)(bufferutil@4.1.0)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(utf-8-validate@6.0.6):
dependencies:
'@storybook/global': 5.0.0
- '@storybook/icons': 2.0.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@testing-library/jest-dom': 6.9.1
'@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0)
'@vitest/expect': 3.2.4
'@vitest/spy': 3.2.4
- esbuild: 0.27.1
+ esbuild: 0.27.3
+ open: 10.2.0
recast: 0.23.11
- semver: 7.7.3
- use-sync-external-store: 1.6.0(react@19.2.1)
- ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ semver: 7.7.4
+ use-sync-external-store: 1.6.0(react@19.2.4)
+ ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
optionalDependencies:
- prettier: 3.7.4
+ prettier: 3.8.1
transitivePeerDependencies:
- '@testing-library/dom'
- bufferutil
@@ -22211,7 +21467,7 @@ snapshots:
strip-json-comments@3.1.1: {}
- strnum@2.1.1: {}
+ strnum@2.1.2: {}
strtok3@10.3.4:
dependencies:
@@ -22223,7 +21479,7 @@ snapshots:
postcss: 8.5.6
postcss-selector-parser: 7.1.0
- superagent@10.2.3:
+ superagent@10.3.0:
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
@@ -22233,14 +21489,15 @@ snapshots:
formidable: 3.5.4
methods: 1.1.2
mime: 2.6.0
- qs: 6.14.0
+ qs: 6.14.1
transitivePeerDependencies:
- supports-color
- supertest@7.1.4:
+ supertest@7.2.2:
dependencies:
+ cookie-signature: 1.2.2
methods: 1.1.2
- superagent: 10.2.3
+ superagent: 10.3.0
transitivePeerDependencies:
- supports-color
@@ -22275,11 +21532,10 @@ snapshots:
picocolors: 1.1.1
sax: 1.4.3
- symbol-tree@3.2.4: {}
-
- systeminformation@5.27.14: {}
+ symbol-tree@3.2.4:
+ optional: true
- systeminformation@5.27.7: {}
+ systeminformation@5.31.1: {}
tagged-tag@1.0.0: {}
@@ -22302,7 +21558,7 @@ snapshots:
yallist: 4.0.0
optional: true
- tar@7.5.2:
+ tar@7.5.9:
dependencies:
'@isaacs/fs-minipass': 4.0.1
chownr: 3.0.0
@@ -22314,10 +21570,10 @@ snapshots:
dependencies:
execa: 6.1.0
- terser@5.44.1:
+ terser@5.46.0:
dependencies:
'@jridgewell/source-map': 0.3.11
- acorn: 8.15.0
+ acorn: 8.16.0
commander: 2.20.3
source-map-support: 0.5.21
@@ -22339,7 +21595,7 @@ snapshots:
dependencies:
real-require: 0.2.0
- three@0.181.2: {}
+ three@0.183.1: {}
throttle-debounce@5.0.2: {}
@@ -22412,13 +21668,6 @@ snapshots:
touch@3.1.1: {}
- tough-cookie@4.1.4:
- dependencies:
- psl: 1.15.0
- punycode: 2.3.1
- universalify: 0.2.0
- url-parse: 1.5.10
-
tough-cookie@5.1.2:
dependencies:
tldts: 6.1.86
@@ -22429,10 +21678,6 @@ snapshots:
tr46@0.0.3: {}
- tr46@3.0.0:
- dependencies:
- punycode: 2.3.1
-
tr46@6.0.0:
dependencies:
punycode: 2.3.1
@@ -22446,7 +21691,7 @@ snapshots:
trough@2.2.0: {}
- ts-api-utils@2.1.0(typescript@5.9.3):
+ ts-api-utils@2.4.0(typescript@5.9.3):
dependencies:
typescript: 5.9.3
@@ -22495,7 +21740,7 @@ snapshots:
tsx@4.21.0:
dependencies:
- esbuild: 0.27.1
+ esbuild: 0.27.3
get-tsconfig: 4.13.0
optionalDependencies:
fsevents: 2.3.3
@@ -22580,7 +21825,7 @@ snapshots:
typedarray@0.0.6: {}
- typeorm@0.3.28(ioredis@5.8.2)(pg@8.16.3):
+ typeorm@0.3.28(ioredis@5.9.3)(pg@8.18.0):
dependencies:
'@sqltools/formatter': 1.2.5
ansis: 4.2.0
@@ -22598,8 +21843,8 @@ snapshots:
uuid: 11.1.0
yargs: 17.7.2
optionalDependencies:
- ioredis: 5.8.2
- pg: 8.16.3
+ ioredis: 5.9.3
+ pg: 8.18.0
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@@ -22632,8 +21877,6 @@ snapshots:
undefsafe@2.0.5: {}
- undici-types@6.21.0: {}
-
undici-types@7.16.0: {}
undici@6.22.0: {}
@@ -22652,11 +21895,11 @@ snapshots:
trough: 2.2.0
vfile: 6.0.3
- unique-filename@4.0.0:
+ unique-filename@5.0.0:
dependencies:
- unique-slug: 5.0.0
+ unique-slug: 6.0.0
- unique-slug@5.0.0:
+ unique-slug@6.0.0:
dependencies:
imurmurhash: 0.1.4
@@ -22685,8 +21928,6 @@ snapshots:
universalify@0.1.2: {}
- universalify@0.2.0: {}
-
universalify@2.0.1: {}
unload@2.4.1: {}
@@ -22695,13 +21936,13 @@ snapshots:
unplugin@1.16.1:
dependencies:
- acorn: 8.15.0
+ acorn: 8.16.0
webpack-virtual-modules: 0.6.2
unplugin@2.3.10:
dependencies:
'@jridgewell/remapping': 2.3.5
- acorn: 8.15.0
+ acorn: 8.16.0
picomatch: 4.0.3
webpack-virtual-modules: 0.6.2
@@ -22719,16 +21960,11 @@ snapshots:
dependencies:
punycode: 2.3.1
- url-parse@1.5.10:
- dependencies:
- querystringify: 2.2.0
- requires-port: 1.0.0
-
- use-sync-external-store@1.6.0(react@19.2.1):
+ use-sync-external-store@1.6.0(react@19.2.4):
dependencies:
- react: 19.2.1
+ react: 19.2.4
- utf-8-validate@6.0.5:
+ utf-8-validate@6.0.6:
dependencies:
node-gyp-build: 4.8.4
optional: true
@@ -22745,13 +21981,13 @@ snapshots:
uuid@9.0.1: {}
- v-code-diff@1.13.1(vue@3.5.25(typescript@5.9.3)):
+ v-code-diff@1.13.1(vue@3.5.28(typescript@5.9.3)):
dependencies:
diff: 5.2.0
diff-match-patch: 1.0.5
highlight.js: 11.11.1
- vue: 3.5.25(typescript@5.9.3)
- vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3))
+ vue: 3.5.28(typescript@5.9.3)
+ vue-demi: 0.14.10(vue@3.5.28(typescript@5.9.3))
v8-to-istanbul@9.3.0:
dependencies:
@@ -22784,49 +22020,49 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
- vite-plugin-glsl@1.5.5(@rollup/pluginutils@5.3.0(rollup@4.53.3))(esbuild@0.27.1)(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)):
+ vite-plugin-glsl@1.5.5(@rollup/pluginutils@5.3.0(rollup@4.59.0))(esbuild@0.27.3)(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)):
dependencies:
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
optionalDependencies:
- '@rollup/pluginutils': 5.3.0(rollup@4.53.3)
- esbuild: 0.27.1
+ '@rollup/pluginutils': 5.3.0(rollup@4.59.0)
+ esbuild: 0.27.3
vite-plugin-turbosnap@1.0.3: {}
- vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0):
+ vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0):
dependencies:
- esbuild: 0.25.11
+ esbuild: 0.27.3
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
- rollup: 4.53.3
+ rollup: 4.59.0
tinyglobby: 0.2.15
optionalDependencies:
- '@types/node': 24.10.2
+ '@types/node': 24.10.13
fsevents: 2.3.3
- sass: 1.95.1
- terser: 5.44.1
+ sass: 1.97.3
+ terser: 5.46.0
tsx: 4.21.0
- vitest-fetch-mock@0.4.5(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)):
+ vitest-fetch-mock@0.4.5(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)):
dependencies:
- vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
- vitest-websocket-mock@0.5.0(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)):
+ vitest-websocket-mock@0.5.0(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)):
dependencies:
'@vitest/utils': 3.2.4
mock-socket: 9.3.1
- vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
- vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.2)(happy-dom@20.0.11)(jsdom@27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0):
+ vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.10.13)(happy-dom@20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(jsdom@27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0):
dependencies:
- '@vitest/expect': 4.0.15
- '@vitest/mocker': 4.0.15(msw@2.12.4(@types/node@24.10.2)(typescript@5.9.3))(vite@7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0))
- '@vitest/pretty-format': 4.0.15
- '@vitest/runner': 4.0.15
- '@vitest/snapshot': 4.0.15
- '@vitest/spy': 4.0.15
- '@vitest/utils': 4.0.15
+ '@vitest/expect': 4.0.18
+ '@vitest/mocker': 4.0.18(msw@2.12.10(@types/node@24.10.13)(typescript@5.9.3))(vite@7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0))
+ '@vitest/pretty-format': 4.0.18
+ '@vitest/runner': 4.0.18
+ '@vitest/snapshot': 4.0.18
+ '@vitest/spy': 4.0.18
+ '@vitest/utils': 4.0.18
es-module-lexer: 1.7.0
expect-type: 1.2.2
magic-string: 0.30.21
@@ -22838,13 +22074,13 @@ snapshots:
tinyexec: 1.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
- vite: 7.2.7(@types/node@24.10.2)(sass@1.95.1)(terser@5.44.1)(tsx@4.21.0)
+ vite: 7.3.1(@types/node@24.10.13)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@opentelemetry/api': 1.9.0
- '@types/node': 24.10.2
- happy-dom: 20.0.11
- jsdom: 27.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
+ '@types/node': 24.10.13
+ happy-dom: 20.7.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
+ jsdom: 27.2.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
transitivePeerDependencies:
- jiti
- less
@@ -22865,7 +22101,7 @@ snapshots:
vscode-languageclient@9.0.1:
dependencies:
minimatch: 5.1.6
- semver: 7.7.3
+ semver: 7.7.4
vscode-languageserver-protocol: 3.17.5
vscode-languageserver-protocol@3.17.5:
@@ -22888,18 +22124,18 @@ snapshots:
vue-component-type-helpers@2.2.12: {}
- vue-component-type-helpers@3.1.8: {}
+ vue-component-type-helpers@3.2.4: {}
- vue-demi@0.14.10(vue@3.5.25(typescript@5.9.3)):
+ vue-demi@0.14.10(vue@3.5.28(typescript@5.9.3)):
dependencies:
- vue: 3.5.25(typescript@5.9.3)
+ vue: 3.5.28(typescript@5.9.3)
- vue-docgen-api@4.79.2(vue@3.5.25(typescript@5.9.3)):
+ vue-docgen-api@4.79.2(vue@3.5.28(typescript@5.9.3)):
dependencies:
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
- '@vue/compiler-dom': 3.5.25
- '@vue/compiler-sfc': 3.5.25
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+ '@vue/compiler-dom': 3.5.28
+ '@vue/compiler-sfc': 3.5.28
ast-types: 0.16.1
esm-resolve: 1.0.11
hash-sum: 2.0.0
@@ -22907,50 +22143,41 @@ snapshots:
pug: 3.0.3
recast: 0.23.11
ts-map: 1.0.3
- vue: 3.5.25(typescript@5.9.3)
- vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.25(typescript@5.9.3))
+ vue: 3.5.28(typescript@5.9.3)
+ vue-inbrowser-compiler-independent-utils: 4.71.1(vue@3.5.28(typescript@5.9.3))
- vue-eslint-parser@10.2.0(eslint@9.39.1):
+ vue-eslint-parser@10.4.0(eslint@9.39.3):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
- eslint: 9.39.1
+ eslint: 9.39.3
eslint-scope: 8.4.0
- eslint-visitor-keys: 4.2.1
+ eslint-visitor-keys: 5.0.1
espree: 10.4.0
esquery: 1.6.0
- semver: 7.7.3
+ semver: 7.7.4
transitivePeerDependencies:
- supports-color
- vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.25(typescript@5.9.3)):
+ vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.5.28(typescript@5.9.3)):
dependencies:
- vue: 3.5.25(typescript@5.9.3)
+ vue: 3.5.28(typescript@5.9.3)
- vue-tsc@3.1.8(typescript@5.9.3):
+ vue-tsc@3.2.4(typescript@5.9.3):
dependencies:
- '@volar/typescript': 2.4.26
- '@vue/language-core': 3.1.8(typescript@5.9.3)
+ '@volar/typescript': 2.4.27
+ '@vue/language-core': 3.2.4
typescript: 5.9.3
- vue@3.5.25(typescript@5.9.3):
+ vue@3.5.28(typescript@5.9.3):
dependencies:
- '@vue/compiler-dom': 3.5.25
- '@vue/compiler-sfc': 3.5.25
- '@vue/runtime-dom': 3.5.25
- '@vue/server-renderer': 3.5.25(vue@3.5.25(typescript@5.9.3))
- '@vue/shared': 3.5.25
+ '@vue/compiler-dom': 3.5.28
+ '@vue/compiler-sfc': 3.5.28
+ '@vue/runtime-dom': 3.5.28
+ '@vue/server-renderer': 3.5.28(vue@3.5.28(typescript@5.9.3))
+ '@vue/shared': 3.5.28
optionalDependencies:
typescript: 5.9.3
- vuedraggable@4.1.0(vue@3.5.25(typescript@5.9.3)):
- dependencies:
- sortablejs: 1.14.0
- vue: 3.5.25(typescript@5.9.3)
-
- w3c-xmlserializer@4.0.0:
- dependencies:
- xml-name-validator: 4.0.0
-
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
@@ -22960,7 +22187,7 @@ snapshots:
dependencies:
axios: 1.13.2(debug@4.4.3)
joi: 18.0.1
- lodash: 4.17.21
+ lodash: 4.17.23
minimist: 1.2.8
rxjs: 7.8.2
transitivePeerDependencies:
@@ -22986,11 +22213,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- web-resource-inliner@7.0.0:
+ web-resource-inliner@8.0.0:
dependencies:
ansi-colors: 4.1.3
escape-goat: 3.0.0
- htmlparser2: 5.0.1
+ htmlparser2: 9.1.0
mime: 2.6.0
valid-data-url: 3.0.1
@@ -22998,17 +22225,11 @@ snapshots:
webidl-conversions@3.0.1: {}
- webidl-conversions@7.0.0: {}
-
- webidl-conversions@8.0.0:
+ webidl-conversions@8.0.1:
optional: true
webpack-virtual-modules@0.6.2: {}
- whatwg-encoding@2.0.0:
- dependencies:
- iconv-lite: 0.6.3
-
whatwg-encoding@3.1.1:
dependencies:
iconv-lite: 0.6.3
@@ -23017,15 +22238,13 @@ snapshots:
whatwg-mimetype@4.0.0: {}
- whatwg-url@11.0.0:
- dependencies:
- tr46: 3.0.0
- webidl-conversions: 7.0.0
+ whatwg-mimetype@5.0.0:
+ optional: true
whatwg-url@15.1.0:
dependencies:
tr46: 6.0.0
- webidl-conversions: 8.0.0
+ webidl-conversions: 8.0.1
optional: true
whatwg-url@5.0.0:
@@ -23084,7 +22303,7 @@ snapshots:
dependencies:
isexe: 2.0.0
- which@5.0.0:
+ which@6.0.0:
dependencies:
isexe: 3.1.1
@@ -23100,8 +22319,8 @@ snapshots:
with@7.0.2:
dependencies:
- '@babel/parser': 7.28.5
- '@babel/types': 7.28.5
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
assert-never: 1.4.0
babel-walk: 3.0.0-canary-5
@@ -23138,10 +22357,14 @@ snapshots:
imurmurhash: 0.1.4
signal-exit: 3.0.7
- ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5):
+ ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6):
optionalDependencies:
- bufferutil: 4.0.9
- utf-8-validate: 6.0.5
+ bufferutil: 4.1.0
+ utf-8-validate: 6.0.6
+
+ wsl-utils@0.1.0:
+ dependencies:
+ is-wsl: 3.1.0
xev@3.0.2: {}
@@ -23161,7 +22384,8 @@ snapshots:
xmlbuilder@11.0.1: {}
- xmlchars@2.2.0: {}
+ xmlchars@2.2.0:
+ optional: true
xtend@4.0.2: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index a9443da0b3..bf8a163120 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -35,4 +35,6 @@ ignorePatchFailures: false
minimumReleaseAge: 10080 # delay 7days to mitigate supply-chain attack
minimumReleaseAgeExclude:
- '@syuilo/aiscript'
- - systeminformation # 脆弱性対応。そのうち消すこと
+ - 'minimatch' # 脆弱性対応 そのうち消す
+ - 'rollup' # 脆弱性対応 そのうち消す
+ - '@rollup/rollup-*' # 脆弱性対応 そのうち消す
diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs
index 0cfce02fef..1086d5a25a 100644
--- a/scripts/build-assets.mjs
+++ b/scripts/build-assets.mjs
@@ -7,7 +7,7 @@ import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as yaml from 'js-yaml';
-import buildTarball from './tarball.mjs';
+import { buildTarball } from './tarball.mjs';
const configDir = fileURLToPath(new URL('../.config', import.meta.url));
const configPath = process.env.MISSKEY_CONFIG_YML
diff --git a/scripts/build-pre.js b/scripts/build-pre.mjs
index a90d53c75d..23c2d08042 100644
--- a/scripts/build-pre.js
+++ b/scripts/build-pre.mjs
@@ -3,7 +3,10 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-const fs = require('fs');
+import * as fs from 'node:fs';
+
+const __dirname = import.meta.dirname;
+
const packageJsonPath = __dirname + '/../package.json'
function build() {
diff --git a/scripts/changelog-checker/package-lock.json b/scripts/changelog-checker/package-lock.json
index 466cf21d0b..10a19bfbfe 100644
--- a/scripts/changelog-checker/package-lock.json
+++ b/scripts/changelog-checker/package-lock.json
@@ -9,16 +9,16 @@
"version": "1.0.0",
"devDependencies": {
"@types/mdast": "4.0.4",
- "@types/node": "24.10.2",
- "@vitest/coverage-v8": "4.0.15",
+ "@types/node": "24.10.12",
+ "@vitest/coverage-v8": "4.0.18",
"mdast-util-to-string": "4.0.0",
"remark": "15.0.1",
"remark-parse": "11.0.0",
"typescript": "5.9.3",
"unified": "11.0.5",
- "vite": "7.2.7",
- "vite-node": "5.2.0",
- "vitest": "4.0.15"
+ "vite": "7.3.1",
+ "vite-node": "5.3.0",
+ "vitest": "4.0.18"
}
},
"node_modules/@babel/helper-string-parser": {
@@ -82,9 +82,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
- "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
@@ -99,9 +99,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
- "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
@@ -116,9 +116,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
- "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
@@ -133,9 +133,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
- "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
@@ -150,9 +150,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
- "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
@@ -167,9 +167,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
- "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
@@ -184,9 +184,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
- "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
@@ -201,9 +201,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
- "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
@@ -218,9 +218,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
- "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
@@ -235,9 +235,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
- "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
@@ -252,9 +252,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
- "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
@@ -269,9 +269,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
- "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
@@ -286,9 +286,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
- "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
@@ -303,9 +303,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
- "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
@@ -320,9 +320,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
- "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
@@ -337,9 +337,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
- "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
@@ -354,9 +354,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
- "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
@@ -371,9 +371,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
- "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"cpu": [
"arm64"
],
@@ -388,9 +388,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
- "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
@@ -405,9 +405,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
- "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"cpu": [
"arm64"
],
@@ -422,9 +422,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
- "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
@@ -438,10 +438,27 @@
"node": ">=18"
}
},
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/sunos-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
- "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
@@ -456,9 +473,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
- "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
@@ -473,9 +490,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
- "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
@@ -490,9 +507,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
- "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
@@ -507,10 +524,11 @@
}
},
"node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
- "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6.0.0"
}
@@ -899,9 +917,9 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "24.10.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz",
- "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==",
+ "version": "24.10.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.12.tgz",
+ "integrity": "sha512-68e+T28EbdmLSTkPgs3+UacC6rzmqrcWFPQs1C8mwJhI/r5Uxr0yEuQotczNRROd1gq30NGxee+fo0rSIxpyAw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -915,18 +933,17 @@
"dev": true
},
"node_modules/@vitest/coverage-v8": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.15.tgz",
- "integrity": "sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz",
+ "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@bcoe/v8-coverage": "^1.0.2",
- "@vitest/utils": "4.0.15",
- "ast-v8-to-istanbul": "^0.3.8",
+ "@vitest/utils": "4.0.18",
+ "ast-v8-to-istanbul": "^0.3.10",
"istanbul-lib-coverage": "^3.2.2",
"istanbul-lib-report": "^3.0.1",
- "istanbul-lib-source-maps": "^5.0.6",
"istanbul-reports": "^3.2.0",
"magicast": "^0.5.1",
"obug": "^2.1.1",
@@ -937,8 +954,8 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "@vitest/browser": "4.0.15",
- "vitest": "4.0.15"
+ "@vitest/browser": "4.0.18",
+ "vitest": "4.0.18"
},
"peerDependenciesMeta": {
"@vitest/browser": {
@@ -947,16 +964,16 @@
}
},
"node_modules/@vitest/expect": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.15.tgz",
- "integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
+ "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@types/chai": "^5.2.2",
- "@vitest/spy": "4.0.15",
- "@vitest/utils": "4.0.15",
+ "@vitest/spy": "4.0.18",
+ "@vitest/utils": "4.0.18",
"chai": "^6.2.1",
"tinyrainbow": "^3.0.3"
},
@@ -965,13 +982,13 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz",
- "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
+ "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "4.0.15",
+ "@vitest/spy": "4.0.18",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@@ -992,9 +1009,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.15.tgz",
- "integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
+ "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1005,13 +1022,13 @@
}
},
"node_modules/@vitest/runner": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.15.tgz",
- "integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
+ "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "4.0.15",
+ "@vitest/utils": "4.0.18",
"pathe": "^2.0.3"
},
"funding": {
@@ -1019,13 +1036,13 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.15.tgz",
- "integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
+ "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.0.15",
+ "@vitest/pretty-format": "4.0.18",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -1034,9 +1051,9 @@
}
},
"node_modules/@vitest/spy": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz",
- "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
+ "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
"dev": true,
"license": "MIT",
"funding": {
@@ -1044,13 +1061,13 @@
}
},
"node_modules/@vitest/utils": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.15.tgz",
- "integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
+ "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.0.15",
+ "@vitest/pretty-format": "4.0.18",
"tinyrainbow": "^3.0.3"
},
"funding": {
@@ -1068,9 +1085,9 @@
}
},
"node_modules/ast-v8-to-istanbul": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.8.tgz",
- "integrity": "sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==",
+ "version": "0.3.10",
+ "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz",
+ "integrity": "sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1099,9 +1116,9 @@
}
},
"node_modules/chai": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz",
- "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1179,9 +1196,9 @@
"license": "MIT"
},
"node_modules/esbuild": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
- "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1192,31 +1209,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.0",
- "@esbuild/android-arm": "0.25.0",
- "@esbuild/android-arm64": "0.25.0",
- "@esbuild/android-x64": "0.25.0",
- "@esbuild/darwin-arm64": "0.25.0",
- "@esbuild/darwin-x64": "0.25.0",
- "@esbuild/freebsd-arm64": "0.25.0",
- "@esbuild/freebsd-x64": "0.25.0",
- "@esbuild/linux-arm": "0.25.0",
- "@esbuild/linux-arm64": "0.25.0",
- "@esbuild/linux-ia32": "0.25.0",
- "@esbuild/linux-loong64": "0.25.0",
- "@esbuild/linux-mips64el": "0.25.0",
- "@esbuild/linux-ppc64": "0.25.0",
- "@esbuild/linux-riscv64": "0.25.0",
- "@esbuild/linux-s390x": "0.25.0",
- "@esbuild/linux-x64": "0.25.0",
- "@esbuild/netbsd-arm64": "0.25.0",
- "@esbuild/netbsd-x64": "0.25.0",
- "@esbuild/openbsd-arm64": "0.25.0",
- "@esbuild/openbsd-x64": "0.25.0",
- "@esbuild/sunos-x64": "0.25.0",
- "@esbuild/win32-arm64": "0.25.0",
- "@esbuild/win32-ia32": "0.25.0",
- "@esbuild/win32-x64": "0.25.0"
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/estree-walker": {
@@ -1328,21 +1346,6 @@
"node": ">=10"
}
},
- "node_modules/istanbul-lib-source-maps": {
- "version": "5.0.6",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
- "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.23",
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/istanbul-reports": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
@@ -2355,13 +2358,13 @@
}
},
"node_modules/vite": {
- "version": "7.2.7",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz",
- "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "^0.25.0",
+ "esbuild": "^0.27.0",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
@@ -2430,17 +2433,17 @@
}
},
"node_modules/vite-node": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-5.2.0.tgz",
- "integrity": "sha512-7UT39YxUukIA97zWPXUGb0SGSiLexEGlavMwU3HDE6+d/HJhKLjLqu4eX2qv6SQiocdhKLRcusroDwXHQ6CnRQ==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-5.3.0.tgz",
+ "integrity": "sha512-8f20COPYJujc3OKPX6OuyBy3ZIv2det4eRRU4GY1y2MjbeGSUmPjedxg1b72KnTagCofwvZ65ThzjxDW2AtQFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"cac": "^6.7.14",
- "es-module-lexer": "^1.7.0",
- "obug": "^2.0.0",
+ "es-module-lexer": "^2.0.0",
+ "obug": "^2.1.1",
"pathe": "^2.0.3",
- "vite": "^7.2.2"
+ "vite": "^7.3.1"
},
"bin": {
"vite-node": "dist/cli.mjs"
@@ -2452,20 +2455,27 @@
"url": "https://opencollective.com/antfu"
}
},
+ "node_modules/vite-node/node_modules/es-module-lexer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vitest": {
- "version": "4.0.15",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.15.tgz",
- "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==",
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
+ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/expect": "4.0.15",
- "@vitest/mocker": "4.0.15",
- "@vitest/pretty-format": "4.0.15",
- "@vitest/runner": "4.0.15",
- "@vitest/snapshot": "4.0.15",
- "@vitest/spy": "4.0.15",
- "@vitest/utils": "4.0.15",
+ "@vitest/expect": "4.0.18",
+ "@vitest/mocker": "4.0.18",
+ "@vitest/pretty-format": "4.0.18",
+ "@vitest/runner": "4.0.18",
+ "@vitest/snapshot": "4.0.18",
+ "@vitest/spy": "4.0.18",
+ "@vitest/utils": "4.0.18",
"es-module-lexer": "^1.7.0",
"expect-type": "^1.2.2",
"magic-string": "^0.30.21",
@@ -2493,10 +2503,10 @@
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
- "@vitest/browser-playwright": "4.0.15",
- "@vitest/browser-preview": "4.0.15",
- "@vitest/browser-webdriverio": "4.0.15",
- "@vitest/ui": "4.0.15",
+ "@vitest/browser-playwright": "4.0.18",
+ "@vitest/browser-preview": "4.0.18",
+ "@vitest/browser-webdriverio": "4.0.18",
+ "@vitest/ui": "4.0.18",
"happy-dom": "*",
"jsdom": "*"
},
diff --git a/scripts/changelog-checker/package.json b/scripts/changelog-checker/package.json
index 2c3761b44a..bbba663ddc 100644
--- a/scripts/changelog-checker/package.json
+++ b/scripts/changelog-checker/package.json
@@ -10,15 +10,15 @@
},
"devDependencies": {
"@types/mdast": "4.0.4",
- "@types/node": "24.10.2",
- "@vitest/coverage-v8": "4.0.15",
+ "@types/node": "24.10.13",
+ "@vitest/coverage-v8": "4.0.18",
"mdast-util-to-string": "4.0.0",
"remark": "15.0.1",
"remark-parse": "11.0.0",
"typescript": "5.9.3",
"unified": "11.0.5",
- "vite": "7.2.7",
- "vite-node": "5.2.0",
- "vitest": "4.0.15"
+ "vite": "7.3.1",
+ "vite-node": "5.3.0",
+ "vitest": "4.0.18"
}
}
diff --git a/scripts/clean-all.js b/scripts/clean-all.mjs
index 839ea3ba1c..dc750af413 100644
--- a/scripts/clean-all.js
+++ b/scripts/clean-all.mjs
@@ -3,11 +3,14 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-const { execSync } = require('child_process');
-const fs = require('fs');
+import { execSync } from 'node:child_process';
+import * as fs from 'node:fs';
+
+const __dirname = import.meta.dirname;
(async () => {
fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true });
+ fs.rmSync(__dirname + '/../packages/backend/src-js', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/backend/node_modules', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend-shared/built', { recursive: true, force: true });
diff --git a/scripts/clean.js b/scripts/clean.mjs
index 5cce8bacab..faa6011ee9 100644
--- a/scripts/clean.js
+++ b/scripts/clean.mjs
@@ -3,10 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-const fs = require('fs');
+import * as fs from 'node:fs';
+
+const __dirname = import.meta.dirname;
(async () => {
fs.rmSync(__dirname + '/../packages/backend/built', { recursive: true, force: true });
+ fs.rmSync(__dirname + '/../packages/backend/src-js', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend-shared/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend/built', { recursive: true, force: true });
fs.rmSync(__dirname + '/../packages/frontend-embed/built', { recursive: true, force: true });
diff --git a/scripts/tarball.mjs b/scripts/tarball.mjs
index d1fe4de4f5..dc6ee07773 100644
--- a/scripts/tarball.mjs
+++ b/scripts/tarball.mjs
@@ -19,7 +19,7 @@ const ignore = [
// Exclude files you don't want to include in the tarball here
];
-export default async function build() {
+export async function buildTarball() {
const mkdirPromise = mkdir(resolve(cwd, 'built', 'tarball'), { recursive: true });
const pack = new Pack({ cwd, gzip: true });
const patterns = await walk({ path: cwd, ignoreFiles: ['.gitignore'] });