diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1631aca53e..3835b4f3f3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,9 @@ "service": "app", "workspaceFolder": "/workspace", "features": { - "ghcr.io/devcontainers-contrib/features/pnpm:2": {}, + "ghcr.io/devcontainers-contrib/features/pnpm:2": { + "version": "8.8.0" + }, "ghcr.io/devcontainers/features/node:1": { "version": "20.5.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 50f3cdaf67..c3a2373da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,28 +12,60 @@ --> +## 2023.10.1 +### General +- Enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に + +### Server +- Fix: フォローしているユーザーからの自分の投稿への返信がタイムラインに含まれない問題を修正 +- Fix: users/notesでセンシティブチャンネルの投稿が含まれる場合がある問題を修正 + ## 2023.10.0 ### NOTE -- muted_noteテーブルは使われなくなったため手動で削除を行ってください。 +- アップデートを行うと、タイムラインが一時的にリセットされます +- ソフトミュート設定はクライアントではなくサーバー側に保存されるようになったため、アップデートを行うとソフトミュートの設定がリセットされます ### Changes - API: users/notes, notes/local-timeline で fileType 指定はできなくなりました -- API: notes/global-timeline は現在常に `[]` を返します +- API: notes/featured でページネーションは他APIと同様 untilId を使って行うようになりました ### General - Feat: ユーザーごとに他ユーザーへの返信をタイムラインに含めるか設定可能になりました - Feat: ユーザーリスト内のメンバーごとに他ユーザーへの返信をユーザーリストタイムラインに含めるか設定可能になりました +- Feat: ユーザーごとのハイライト +- Feat: プライバシーポリシー・運営者情報(Impressum)の指定が可能になりました + - プライバシーポリシーはサーバー登録時に同意確認が入ります +- Feat: タイムラインがリアルタイム更新中に広告を挿入できるようになりました + - デフォルトは無効 + - 頻度はコントロールパネルから設定できます。運営中のサーバーのTLの流速を見て、最適な値を指定してください。 - Enhance: ソフトワードミュートとハードワードミュートは統合されました - Enhance: モデレーションログ機能の強化 - Enhance: ローカリゼーションの更新 -- Fix: ダイレクト投稿をリノートできてしまう +- Enhance: 依存関係の更新 +- Fix: ダイレクト投稿をリノートできてしまう問題を修正 +- Fix: ユーザーリストTLにチャンネル投稿が含まれる問題を修正 ### Client +- Feat: 「ファイルの詳細」ページを追加 + - ドライブのファイルの拡大プレビューができるように + - ファイルが添付されたノートの一覧が表示できるように - Enhance: 二要素認証のバックアップコード一覧をテキストファイルでダウンロード可能に +- Enhance: 動画再生時のデフォルトボリュームを30%に - Fix: リアクションしたユーザ一覧のUIが稀に左上に残ってしまう不具合を修正 ### Server -- Enhance: タイムライン取得時のパフォーマンスを改善 +- Enhance: drive/files/attached-notes がページネーションに対応しました +- Enhance: タイムライン取得時のパフォーマンスを大幅に向上 +- Enhance: ハイライト取得時のパフォーマンスを大幅に向上 +- Enhance: トレンドハッシュタグ取得時のパフォーマンスを大幅に向上 +- Enhance: WebSocket接続が多い場合のパフォーマンスを向上 +- Enhance: 不要なPostgreSQLのインデックスを削除しパフォーマンスを向上 +- Fix: 連合なしアンケートに投票をするとUpdateがリモートに配信されてしまうのを修正 +- Fix: nodeinfoにおいてCORS用のヘッダーが設定されていないのを修正 +- Fix: 同じ種類のTLのストリーミングを複数接続できない問題を修正 +- Fix: アンテナTLを途中までしかページネーションできなくなることがある問題を修正 +- Fix: 「ファイル付きのみ」のTLでファイル無しの新着ノートが流れる問題を修正 +- Fix: プロセスが終了しない、あるいは非常に時間がかかる問題を修正 ## 2023.9.3 ### General diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md index 67b6371001..ccdb3b5bf9 100644 --- a/CHANGELOG_CHERRYPICK.md +++ b/CHANGELOG_CHERRYPICK.md @@ -33,7 +33,7 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE > 문제가 발생하면 '설정 - 캐시 비우기'를 진행하거나, 브라우저 캐시를 삭제하십시오. ## NOTE -- Misskey 2023.10.0 에서 삭제된 노트 편집 기능이 계속 유지됩니다. +- Misskey 2023.10.0 에서 제거된 노트 편집 기능이 계속 유지됩니다. ### Client - Feat: 클라이언트 업데이트 알림 개선 diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index cb98e5a5ac..9669ae33ac 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1198,11 +1198,6 @@ _wordMute: muteWords: "الكلمات المحظورة" muteWordsDescription: "افصل بينهم بمسافة لاستخدام معامل \"و\" أو بسطر لاستخدام معامل \"أو\"." muteWordsDescription2: "احصر الكلمات المفتاحية بين بين شرطتين مائلتين لاستخدامها كتعابير نمطية" - softDescription: "اخف الملاحظات التي تستوف الشروط من الخيط الزمني." - hardDescription: "اخف الملاحظات التي تستوف الشروط من الخيط الزمني.بالإضافة إلى أن هذه الملاحظات ستبقى مخفية حتى وإن تغيرت الشروط." - soft: "لينة" - hard: "قاسية" - mutedNotes: "الملاحظات المكتومة" _instanceMute: instanceMuteDescription: "هذه سيحجب كل ملاحظات الخوادم المحجوبة ومشاركاتها والردود على تلك الملاحظات حتى وإن كانت من خادم غير محجوب." instanceMuteDescription2: "مدخلة لكل سطر" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 9893261779..70d40a4e35 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -1009,11 +1009,6 @@ _wordMute: muteWords: "নিঃশব্দ করা শব্দগুলি" muteWordsDescription: "স্পেস দিয়ে আলাদা করলে AND শর্ত তৈরি হবে এবং আলাদা লাইনে লিখলে OR শর্ত তৈরি হবে।" muteWordsDescription2: "রেগুলার এক্সপ্রেশন ব্যবহার করতে স্ল্যাশ দিয়ে কীওয়ার্ডকে ঘিরে রাখুন।" - softDescription: "টাইমলাইন থেকে নির্দিষ্ট শর্তানুযায়ী নোট লুকিয়ে রাখে।" - hardDescription: "নির্দিষ্ট শর্তানুযায়ী নোটগুলিকে টাইমলাইন থেকে বাদ দেয়। আপনি শর্ত পরিবর্তন করলেও যে নোটগুলি যোগ করা হয়নি সেগুলি বাদ দেওয়া হবে।" - soft: "নমনীয়" - hard: "কঠোর" - mutedNotes: "মিউট করা নোটগুলি" _instanceMute: instanceMuteDescription: "কনফিগার করা ইন্সট্যান্সের সব নোট এবং রিনোট মিউট করুন, মিউট করা ইন্সট্যান্সের ব্যবহারকারীদের উত্তর সহ।" instanceMuteDescription2: "প্রতিটিকে আলাদা লাইনে লিখুন" @@ -1077,9 +1072,6 @@ _theme: infoFg: "তথ্যের পাঠ্য" infoWarnBg: "ওয়ার্নিং এর পটভূমি" infoWarnFg: "ওয়ার্নিং এর পাঠ্য" - cwBg: "CW বাটনের পটভূমি" - cwFg: "CW বাটনের পাঠ্য" - cwHoverBg: "CW বাটনের পটভূমি (হভার)" toastBg: "বিজ্ঞপ্তির পটভূমি" toastFg: "বিজ্ঞপ্তির পাঠ্য" buttonBg: "বাটনের পটভূমি" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 7efef67b9a..7bcf9418c9 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -1559,11 +1559,6 @@ _wordMute: muteWords: "Ztlumená slova" muteWordsDescription: "Podmínku AND oddělujte mezerami, podmínku OR oddělujte řádkovými zlomy." muteWordsDescription2: "Chcete-li použít regulární výrazy, obklopte klíčová slova lomítky." - softDescription: "Skrýt poznámky, které splňují nastavené podmínky, z časové osy." - hardDescription: "Zabrání přidání poznámek splňujících nastavené podmínky na časovou osu. Kromě toho nebudou tyto poznámky přidány na časovou osu, ani když se podmínky změní." - soft: "Měkký" - hard: "Tvrdý" - mutedNotes: "Ztlumené poznámky" _instanceMute: instanceMuteDescription: "Tímhle se ztlumí všechny poznámky/poznámky z uvedených instancí, včetně poznámek uživatelů, kteří odpovídají uživateli ze ztlumené instance." instanceMuteDescription2: "Oddělte novými řádky" @@ -1627,9 +1622,6 @@ _theme: infoFg: "Text informací" infoWarnBg: "Pozadí varování" infoWarnFg: "Text varování" - cwBg: "Pozadí CW tlačítka" - cwFg: "Text CW tlačítka" - cwHoverBg: "Pozadí CW tlačítka (Hover)" toastBg: "Pozadí oznámení" toastFg: "Text oznámení" buttonBg: "Pozadí tlačítka" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 06b6c21cb8..3c19a74c11 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1140,6 +1140,15 @@ edited: "Bearbeitet" notificationRecieveConfig: "Benachrichtigungseinstellungen" mutualFollow: "Gegenseitig gefolgt" fileAttachedOnly: "Nur Notizen mit Dateien" +showRepliesToOthersInTimeline: "Antworten in Chronik anzeigen" +hideRepliesToOthersInTimeline: "Antworten nicht in Chronik anzeigen" +externalServices: "Externe Dienste" +impressum: "Impressum" +impressumUrl: "Impressums-URL" +impressumDescription: "In manchen Ländern, wie Deutschland und dessen Umgebung, ist die Angabe von Betreiberinformationen (ein Impressum) bei kommerziellem Betrieb zwingend." +privacyPolicy: "Datenschutzerklärung" +privacyPolicyUrl: "Datenschutzerklärungs-URL" +tosAndPrivacyPolicy: "Nutzungsbedingungen und Datenschutzerklärung" _announcement: forExistingUsers: "Nur für existierende Nutzer" forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt." @@ -1490,6 +1499,7 @@ _role: descriptionOfRateLimitFactor: "Je niedriger desto weniger restriktiv, je höher destro restriktiver." canHideAds: "Kann Werbung ausblenden" canSearchNotes: "Nutzung der Notizsuchfunktion" + canUseTranslator: "Verwendung des Übersetzers" _condition: isLocal: "Lokaler Benutzer" isRemote: "Benutzer fremder Instanz" @@ -1538,6 +1548,10 @@ _ad: reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen" hide: "Ausblenden" timezoneinfo: "Der Wochentag wird durch die Serverzeitzone bestimmt." + adsSettings: "Werbeeinstellungen" + notesPerOneAd: "Werbeintervall während Echtzeitaktualisierung (Notizen pro Werbung)" + setZeroToDisable: "Setze dies auf 0, um Werbung während Echtzeitaktualisierung zu deaktivieren" + adsTooClose: "Durch den momentan sehr niedrigen Werbeintervall kann es zu einer starken Verschlechterung der Benutzererfahrung kommen." _forgotPassword: enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst." ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator." @@ -1689,11 +1703,6 @@ _wordMute: muteWords: "Stummgeschaltete Wörter" muteWordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch trennen." muteWordsDescription2: "Umgib Schlüsselworter mit Schrägstrichen, um Reguläre Ausdrücke zu verwenden." - softDescription: "Notizen, die die angegebenen Konditionen erfüllen, in der Chronik ausblenden." - hardDescription: "Verhindern, dass Notizen, die die angegebenen Konditionen erfüllen, der Chronik hinzugefügt werden. Zudem werden diese Notizen auch nicht der Chronik hinzugefügt, falls die Konditionen geändert werden." - soft: "Leicht" - hard: "Schwer" - mutedNotes: "Stummgeschaltete Notizen" _instanceMute: instanceMuteDescription: "Schaltet alle Notizen/Renotes stumm, die von den gelisteten Instanzen stammen, inklusive Antworten von Benutzern an einen Benutzer einer stummgeschalteten Instanz." instanceMuteDescription2: "Instanzen getrennt durch Zeilenumbrüchen angeben" @@ -1757,9 +1766,6 @@ _theme: infoFg: "Text von Informationen" infoWarnBg: "Hintergrund von Warnungen" infoWarnFg: "Text von Warnungen" - cwBg: "Hintergrund des Inhaltswarnungsknopfs" - cwFg: "Text des Inhaltswarnungsknopfs" - cwHoverBg: "Hintergrund des Inhaltswarnungsknopfs (Mouseover)" toastBg: "Hintergrund von Benachrichtigungen" toastFg: "Text von Benachrichtigungen" buttonBg: "Hintergrund von Schaltflächen" @@ -2224,3 +2230,11 @@ _moderationLogTypes: createAd: "Werbung erstellt" deleteAd: "Werbung gelöscht" updateAd: "Werbung aktualisiert" +_fileViewer: + title: "Dateiinformationen" + type: "Dateityp" + size: "Dateigröße" + url: "URL" + uploadedAt: "Hochgeladen am" + attachedNotes: "Zugehörige Notizen" + thisPageCanBeSeenFromTheAuthor: "Nur der Benutzer, der diese Datei hochgeladen hat, kann diese Seite sehen." diff --git a/locales/en-US.yml b/locales/en-US.yml index 5134c26a4d..50cb1b9951 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1209,6 +1209,15 @@ edited: "Edited" notificationRecieveConfig: "Notification Settings" mutualFollow: "Mutual follow" fileAttachedOnly: "Only notes with files" +showRepliesToOthersInTimeline: "Show replies to others in TL" +hideRepliesToOthersInTimeline: "Hide replies to others from TL" +externalServices: "External Services" +impressum: "Impressum" +impressumUrl: "Impressum URL" +impressumDescription: "In some countries, like germany, the inclusion of operator contact information (an Impressum) is legally required for commercial websites." +privacyPolicy: "Privacy Policy" +privacyPolicyUrl: "Privacy Policy URL" +tosAndPrivacyPolicy: "Terms of Service and Privacy Policy" showCatOnly: "Show only cats" additionalPermissionsForFlash: "Allow to add permission to Play" thisFlashRequiresTheFollowingPermissions: "This Play requires the following permissions" @@ -1646,6 +1655,7 @@ _role: descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. " canHideAds: "Can hide ads" canSearchNotes: "Usage of note search" + canUseTranslator: "Translator usage" _condition: isLocal: "Local user" isRemote: "Remote user" @@ -1698,6 +1708,10 @@ _ad: reduceFrequencyOfThisAd: "Show this ad less" hide: "Hide" timezoneinfo: "The day of the week is determined from the server's timezone." + adsSettings: "Ad settings" + notesPerOneAd: "Real-time update ad placement interval (Notes per ad)" + setZeroToDisable: "Set this value to 0 to disable real-time update ads" + adsTooClose: "The current ad interval may significantly worsen the user experience due to being too low." _forgotPassword: enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it." ifNoEmail: "If you did not use an email during registration, please contact the instance administrator instead." @@ -1871,11 +1885,6 @@ _wordMute: muteWords: "Muted words" muteWordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition." muteWordsDescription2: "Surround keywords with slashes to use regular expressions." - softDescription: "Hide notes that fulfil the set conditions from the timeline." - hardDescription: "Prevents notes fulfilling the set conditions from being added to the timeline. In addition, these notes will not be added to the timeline even if the conditions are changed." - soft: "Soft" - hard: "Hard" - mutedNotes: "Muted notes" _instanceMute: instanceMuteDescription: "This will mute any notes/renotes from the listed instances, including those of users replying to a user from a muted instance." instanceMuteDescription2: "Separate with newlines" @@ -1939,9 +1948,6 @@ _theme: infoFg: "Information text" infoWarnBg: "Warning background" infoWarnFg: "Warning text" - cwBg: "CW button background" - cwFg: "CW button text" - cwHoverBg: "CW button background (Hover)" toastBg: "Notification background" toastFg: "Notification text" buttonBg: "Button background" @@ -2406,6 +2412,14 @@ _moderationLogTypes: createAd: "Ad created" deleteAd: "Ad deleted" updateAd: "Ad updated" +_fileViewer: + title: "File details" + type: "File type" + size: "Filesize" + url: "URL" + uploadedAt: "Uploaded at" + attachedNotes: "Attached notes" + thisPageCanBeSeenFromTheAuthor: "This page can only be seen by the user who uploaded this file." _abuse: _resolver: 1hour: "one hour" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 04f2cb0e51..383f1652a6 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1683,11 +1683,6 @@ _wordMute: muteWords: "Palabras que silenciar" muteWordsDescription: "Separar con espacios indica una declaracion And, separar con lineas nuevas indica una declaracion Or。" muteWordsDescription2: "Encerrar las palabras clave entre numerales para usar expresiones regulares" - softDescription: "Ocultar en la linea de tiempo las notas que cumplen las condiciones" - hardDescription: "Evitar que se agreguen a la linea de tiempo las notas que cumplen las condiciones. Las notas no agregadas seguirán quitadas aunque cambien las condiciones." - soft: "Suave" - hard: "Duro" - mutedNotes: "Notas silenciadas" _instanceMute: instanceMuteDescription: "Silencia todas las notas y reposts de la instancias seleccionadas, incluyendo respuestas a los usuarios de las mismas" instanceMuteDescription2: "Separar por líneas" @@ -1751,9 +1746,6 @@ _theme: infoFg: "Texto de información" infoWarnBg: "Fondo de advertencias" infoWarnFg: "Texto de advertencias" - cwBg: "Fondo del botón CW" - cwFg: "Texto del botón CW" - cwHoverBg: "Fondo del botón CW (hover)" toastBg: "Fondo de notificaciones" toastFg: "Texto de notificaciones" buttonBg: "Fondo de botón" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 7569961092..968f8b36ee 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -45,6 +45,7 @@ pin: "Épingler sur le profil" unpin: "Désépingler" copyContent: "Copier le contenu" copyLink: "Copier le lien" +copyLinkRenote: "Copier le lien de la renote" delete: "Supprimer" deleteAndEdit: "Supprimer et réécrire" deleteAndEditConfirm: "Êtes-vous sûr de vouloir effacer cette note et la modifier ? Vous perdrez toutes les réactions, renotes et réponses." @@ -129,6 +130,8 @@ unmarkAsSensitive: "Supprimer le marquage comme sensible" enterFileName: "Entrer le nom du fichier" mute: "Masquer" unmute: "Ne plus masquer" +renoteMute: "Masquer les renotes" +renoteUnmute: "Ne plus masquer les renotes" block: "Bloquer" unblock: "Débloquer" suspend: "Suspendre" @@ -414,6 +417,7 @@ moderator: "Modérateur·rice·s" moderation: "Modérations" moderationNote: "Note de modération" addModerationNote: "Ajouter une note de modération" +moderationLogs: "Journal de modération" nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s" securityKeyAndPasskey: "Sécurité et clés de sécurité" securityKey: "Clé de sécurité" @@ -484,6 +488,7 @@ native: "Natif" disableDrawer: "Les menus ne s'affichent pas dans le tiroir" youHaveNoGroups: "Vous n’avez aucun groupe" joinOrCreateGroup: "Vous pouvez être invité·e à rejoindre des groupes existants ou créer votre propre nouveau groupe." +showNoteActionsOnlyHover: "Afficher les actions de note uniquement au survol" noHistory: "Pas d'historique" signinHistory: "Historique de connexion" enableAdvancedMfm: "Activer la MFM avancée" @@ -659,6 +664,7 @@ behavior: "Comportement" sample: "Exemple" abuseReports: "Signalements" reportAbuse: "Signaler" +reportAbuseRenote: "Signaler la renote" reportAbuseOf: "Signaler {name}" fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien." abuseReported: "Le rapport est envoyé. Merci." @@ -683,6 +689,8 @@ clip: "Clip" createNew: "Créer nouveau" optional: "Facultatif" createNewClip: "Créer un nouveau clip" +unclip: "Supprimer le clip" +confirmToUnclipAlreadyClippedNote: "Cette note fait déjà partie du clip « {name} ». Souhaitez-vous la supprimer de ce clip ?" public: "Public" private: "Privé" i18nInfo: "CherryPick est traduit dans différentes langues par des bénévoles. Vous pouvez contribuer à {link}." @@ -947,12 +955,15 @@ unsubscribePushNotification: "Désactiver les notifications push" pushNotificationAlreadySubscribed: "Les notifications push sont déjà activées" pushNotificationNotSupported: "Votre navigateur ou votre instance ne prend pas en charge les notifications push" sendPushNotificationReadMessage: "Supprimer les notifications push une fois que les notifications ou messages pertinents ont été lus." +windowMaximize: "Maximiser" +windowMinimize: "Minimaliser" windowRestore: "Restaurer" caption: "Libellé" loggedInAsBot: "Connecté actuellement en tant que bot" tools: "Outils" cannotLoad: "Chargement impossible" like: "J'aime" +unlike: "Ne plus aimer" numberOfLikes: "Favoris" show: "Affichage" neverShow: "Ne plus afficher" @@ -963,6 +974,7 @@ noRole: "Aucun rôle" normalUser: "Simple utilisateur·rice" undefined: "Non défini" assign: "Attribuer" +unassign: "Retirer" color: "Couleur" manageCustomEmojis: "Gestion des émojis personnalisés" preset: "Préréglage" @@ -972,12 +984,16 @@ thisPostMayBeAnnoying: "Cette note peut gêner d'autres personnes." thisPostMayBeAnnoyingHome: "Publier vers le fil principal" thisPostMayBeAnnoyingCancel: "Annuler" thisPostMayBeAnnoyingIgnore: "Publier quand-même" +collapseRenotes: "Réduire les renotes déjà vues" internalServerError: "Erreur interne du serveur" copyErrorInfo: "Copier les détails de l’erreur" exploreOtherServers: "Trouver une autre instance" disableFederationOk: "Désactiver" likeOnly: "Les favoris uniquement" +sensitiveWords: "Mots sensibles" +notesSearchNotAvailable: "La recherche de notes n'est pas disponible." license: "Licence" +myClips: "Mes clips" video: "Vidéo" videos: "Vidéos" dataSaver: "Économiseur de données" @@ -987,6 +1003,7 @@ accountMovedShort: "Ce compte a migré" operationForbidden: "Opération non autorisée" addMemo: "Ajouter un mémo" reactionsList: "Réactions" +renotesList: "Liste de renotes" notificationDisplay: "Style des notifications" leftTop: "En haut à gauche" rightTop: "En haut à droite" @@ -996,6 +1013,7 @@ vertical: "Vertical" horizontal: "Latéral" serverRules: "Règles du serveur" archive: "Archive" +displayOfNote: "Affichage de la note" youFollowing: "Abonné·e" options: "Options" later: "Plus tard" @@ -1015,6 +1033,7 @@ pinnedList: "Liste épinglée" notifyNotes: "Notifier à propos des nouvelles notes" authentication: "Authentification" authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer" +showRenotes: "Afficher les renotes" _announcement: readConfirmTitle: "Marquer comme lu ?" _initialAccountSetting: @@ -1096,12 +1115,20 @@ _achievements: title: "Beaucoup d'amis" _followers10: title: "Abonnez-moi !" + description: "Obtenir plus de 10 abonné·e·s" + _followers50: + description: "Obtenir plus de 50 abonné·e·s" _followers100: title: "Populaire" + description: "Obtenir plus de 100 abonné·e·s" + _followers300: + description: "Obtenir plus de 300 abonné·e·s" _followers500: title: "Tour radio" + description: "Obtenir plus de 500 abonné·e·s" _followers1000: title: "Influenceur·euse" + description: "Obtenir plus de 1000 abonné·e·s" _iLoveMisskey: title: "J’adore CherryPick" description: "Publication « J’❤ #CherryPick »" @@ -1165,6 +1192,7 @@ _role: high: "Haute" _options: canManageCustomEmojis: "Gestion des émojis personnalisés" + wordMuteMax: "Nombre maximal de caractères dans le filtre de mots" _sensitiveMediaDetection: description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement." sensitivity: "Sensibilité de la détection" @@ -1347,11 +1375,6 @@ _wordMute: muteWords: "Mots à filtrer" muteWordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR." muteWordsDescription2: "Pour utiliser des expressions régulières (regex), mettez les mots-clés entre barres obliques." - softDescription: "Masquez les notes de votre fil selon les paramètres que vous définissez." - hardDescription: "Empêchez votre fil de charger les notes selon les paramètres que vous définissez. Cette action est irréversible : si vous modifiez ces paramètres plus tard, les notes précédemment filtrées ne seront pas récupérées." - soft: "Doux" - hard: "Strict" - mutedNotes: "Notes filtrées" _instanceMute: instanceMuteDescription: "Met en sourdine toutes les notes et renotes de l'instance configurée, y compris les réponses aux utilisateurs de l'instance muette." instanceMuteDescription2: "Séparer avec de nouvelles lignes" @@ -1415,9 +1438,6 @@ _theme: infoFg: "Texte d'information" infoWarnBg: "Arrière-plan des avertissements" infoWarnFg: "Texte d’avertissement" - cwBg: "Arrière-plan du CW" - cwFg: "Texte du bouton CW" - cwHoverBg: "Arrière-plan du bouton CW (survolé)" toastBg: "Arrière-plan de la bulle de notification" toastFg: "Texte de la bulle de notification" buttonBg: "Arrière-plan du bouton" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index de915a89ca..177a3e168b 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1642,11 +1642,6 @@ _wordMute: muteWords: "Kata yang dibisukan" muteWordsDescription: "Pisahkan dengan spasi untuk kondisi AND. Pisahkan dengan baris baru untuk kondisi OR." muteWordsDescription2: "Kurung kata kunci dengan garis miring untuk menggunakan ekspresi reguler." - softDescription: "Sembunyikan catatan yang memenuhi aturan kondisi dari lini masa." - hardDescription: "Cegah catatan memenuhi aturan kondisi dari ditambahkan ke lini masa. Dengan tambahan, catatan berikut tidak akan ditambahkan ke lini masa meskipun jika kondisi tersebut diubah." - soft: "Lembut" - hard: "Keras" - mutedNotes: "Catatan yang dibisukan" _instanceMute: instanceMuteDescription: "Pengaturan ini akan membisukan note/renote apa saja dari instansi yang terdaftar, termasuk pengguna yang membalas pengguna lain dalam instansi yang dibisukan." instanceMuteDescription2: "Pisah dengan baris baru" @@ -1710,9 +1705,6 @@ _theme: infoFg: "Teks informasi" infoWarnBg: "Latar belakang peringatan" infoWarnFg: "Teks peringatan" - cwBg: "Latar belakang tombol Sembunyikan Konten" - cwFg: "Teks tombol Sembunyikan Konten" - cwHoverBg: "Latar belakang tombol Sembunyikan Konten (Mengambang)" toastBg: "Latar belakang notifikasi" toastFg: "Teks notifikasi" buttonBg: "Latar belakang tombol" diff --git a/locales/index.d.ts b/locales/index.d.ts index 65ff12e1c5..70254272ab 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1215,6 +1215,12 @@ export interface Locale { "showRepliesToOthersInTimeline": string; "hideRepliesToOthersInTimeline": string; "externalServices": string; + "impressum": string; + "impressumUrl": string; + "impressumDescription": string; + "privacyPolicy": string; + "privacyPolicyUrl": string; + "tosAndPrivacyPolicy": string; "showCatOnly": string; "additionalPermissionsForFlash": string; "thisFlashRequiresTheFollowingPermissions": string; @@ -1806,6 +1812,10 @@ export interface Locale { "reduceFrequencyOfThisAd": string; "hide": string; "timezoneinfo": string; + "adsSettings": string; + "notesPerOneAd": string; + "setZeroToDisable": string; + "adsTooClose": string; }; "_forgotPassword": { "enterEmail": string; @@ -2062,9 +2072,6 @@ export interface Locale { "infoFg": string; "infoWarnBg": string; "infoWarnFg": string; - "cwBg": string; - "cwFg": string; - "cwHoverBg": string; "toastBg": string; "toastFg": string; "buttonBg": string; @@ -2568,6 +2575,15 @@ export interface Locale { "deleteAd": string; "updateAd": string; }; + "_fileViewer": { + "title": string; + "type": string; + "size": string; + "url": string; + "uploadedAt": string; + "attachedNotes": string; + "thisPageCanBeSeenFromTheAuthor": string; + }; "_abuse": { "_resolver": { "1hour": string; diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 2db23e8564..da63286320 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -64,7 +64,7 @@ reply: "Rispondi" loadMore: "Mostra di più" showMore: "Espandi" showLess: "Comprimi" -youGotNewFollower: "Ti sta seguendo" +youGotNewFollower: "Adesso ti segue" receiveFollowRequest: "Hai ricevuto una richiesta di follow" followRequestAccepted: "Ha accettato la tua richiesta di follow" mention: "Menzioni" @@ -78,7 +78,7 @@ download: "Scarica" driveFileDeleteConfirm: "Vuoi davvero eliminare il file \"{name}\", e le Note a cui è stato allegato?" unfollowConfirm: "Vuoi davvero smettere di seguire {name}?" exportRequested: "Hai richiesto un'esportazione, e potrebbe volerci tempo. Quando sarà compiuta, il file verrà aggiunto direttamente al Drive." -importRequested: "Hai richiesto un'importazione. Può volerci tempo. " +importRequested: "Hai richiesto un'importazione. Potrebbe richiedere un po' di tempo." lists: "Liste" noLists: "Nessuna lista" note: "Nota" @@ -113,7 +113,7 @@ cantReRenote: "È impossibile rinotare una Rinota." quote: "Cita" inChannelRenote: "Rinota nel canale" inChannelQuote: "Cita nel canale" -pinnedNote: "Nota fissata" +pinnedNote: "Nota in primo piano" pinned: "Fissa sul profilo" you: "Tu" clickToShow: "Clicca per visualizzare" @@ -186,7 +186,7 @@ recipient: "Destinatario" annotation: "Annotazione preventiva" federation: "Federazione" instances: "Istanza" -registeredAt: "Registrato presso" +registeredAt: "Prima federazione" latestRequestReceivedAt: "Ultima richiesta ricevuta" latestStatus: "Ultimo stato" storageUsage: "Capienza dei dischi" @@ -336,7 +336,7 @@ instanceName: "Nome dell'istanza" instanceDescription: "Descrizione dell'istanza" maintainerName: "Nome dell'amministratore" maintainerEmail: "Indirizzo e-mail dell'amministratore" -tosUrl: "URL dei termini del servizio e della privacy" +tosUrl: "URL delle condizioni d'uso" thisYear: "Anno" thisMonth: "Mese" today: "Oggi" @@ -364,7 +364,7 @@ pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagi pinnedPages: "Pagine in evidenza" pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga." pinnedClipId: "ID della Clip in evidenza" -pinnedNotes: "Nota fissata" +pinnedNotes: "Note in primo piano" hcaptcha: "hCaptcha" enableHcaptcha: "Abilita hCaptcha" hcaptchaSiteKey: "Chiave del sito" @@ -384,7 +384,7 @@ name: "Nome" antennaSource: "Fonte dell'antenna" antennaKeywords: "Parole chiavi da ricevere" antennaExcludeKeywords: "Parole chiavi da escludere" -antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." +antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)." notifyAntenna: "Invia notifiche delle nuove note" withFileAntenna: "Solo note con file in allegato" enableServiceworker: "Abilita ServiceWorker" @@ -393,7 +393,7 @@ caseSensitive: "Sensibile alla distinzione tra maiuscole e minuscole" withReplies: "Includere le risposte" connectedTo: "Connessione ai seguenti profili:" notesAndReplies: "Note e risposte" -withFiles: "Con file in allegato" +withFiles: "Con allegati" silence: "Silenzia" silenceConfirm: "Vuoi davvero silenziare questo profilo?" unsilence: "Riattiva" @@ -470,7 +470,7 @@ invitationCode: "Codice di invito" checking: "Confermando" available: "Disponibile" unavailable: "Il nome utente è già in uso" -usernameInvalidFormat: "Il nome utente può contenere solo lettere, numeri e '_'" +usernameInvalidFormat: "Il nome utente deve avere solo caratteri alfanumerici e trattino basso '_'" tooShort: "Troppo breve" tooLong: "Troppo lungo" weakPassword: "Password debole" @@ -1135,7 +1135,20 @@ unnotifyNotes: "Interrompi le notifiche di nuove Note" authentication: "Autenticazione" authenticationRequiredToContinue: "Per procedere, è richiesta l'autenticazione" dateAndTime: "Data e Ora" -showRenotes: "Leggi le Rinota" +showRenotes: "Includi le Rinota" +edited: "Modificato" +notificationRecieveConfig: "Preferenze di notifica" +mutualFollow: "Follow reciproco" +fileAttachedOnly: "Solo con allegati" +showRepliesToOthersInTimeline: "Risposte altrui nella TL" +hideRepliesToOthersInTimeline: "Nascondi Riposte altrui nella TL" +externalServices: "Servizi esterni" +impressum: "Dichiarazione di proprietà" +impressumUrl: "URL della dichiarazione di proprietà" +impressumDescription: "La dichiarazione di proprietà, è obbligatoria in alcuni paesi come la Germania (Impressum)." +privacyPolicy: "Informativa sulla privacy" +privacyPolicyUrl: "URL della informativa privacy" +tosAndPrivacyPolicy: "Condizioni d'uso e informativa sulla privacy" _announcement: forExistingUsers: "Solo ai profili attuali" forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio." @@ -1465,14 +1478,14 @@ _role: _options: gtlAvailable: "Disponibilità della Timeline Federata" ltlAvailable: "Disponibilità della Timeline Locale" - canPublicNote: "Può scrivere Note con Visibilità Pubblica" - canInvite: "Genera codici di invito all'istanza" + canPublicNote: "Scrivere Note con Visibilità Pubblica" + canInvite: "Generare codici di invito all'istanza" inviteLimit: "Limite di codici invito" inviteLimitCycle: "Intervallo di emissione del codice di invito" inviteExpirationTime: "Scadenza del codice di invito" canManageCustomEmojis: "Gestire le emoji personalizzate" driveCapacity: "Capienza del Drive" - alwaysMarkNsfw: "Imposta sempre come NSFW" + alwaysMarkNsfw: "Impostare sempre come esplicito (NSFW)" pinMax: "Quantità massima di Note in primo piano" antennaMax: "Quantità massima di Antenne" wordMuteMax: "Lunghezza massima del filtro parole" @@ -1483,8 +1496,9 @@ _role: userEachUserListsMax: "Quantità massima di profili per lista" rateLimitFactor: "Limite del rapporto" descriptionOfRateLimitFactor: "I rapporti più bassi sono meno restrittivi, quelli più alti lo sono di più." - canHideAds: "Può nascondere i banner" + canHideAds: "Nascondere i banner" canSearchNotes: "Ricercare nelle Note" + canUseTranslator: "Tradurre le Note" _condition: isLocal: "Profilo locale" isRemote: "Profilo remoto" @@ -1533,6 +1547,10 @@ _ad: reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso" hide: "Nascondi" timezoneinfo: "Il giorno della settimana è determinato in base al fuso orario del server." + adsSettings: "Impostazioni banner" + notesPerOneAd: "Quantità di Note tra i banner" + setZeroToDisable: "Imposta 0 (zero) per disattivare la distribuzione dei banner durante gli aggiornamenti in tempo reale" + adsTooClose: "Attenzione, l'intervallo di pubblicazione dei banner è molto breve, potrebbe infastidire significativamente la fruizione" _forgotPassword: enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo." ifNoEmail: "Se il tuo indirizzo email non risulta registrato, contatta l'amministrazione dell'istanza." @@ -1544,7 +1562,7 @@ _gallery: unlike: "Non mi piace più" _email: _follow: - title: "Ha iniziato a seguirti" + title: "Adesso ti segue" _receiveFollowRequest: title: "Hai ricevuto una richiesta di follow" _plugin: @@ -1682,13 +1700,8 @@ _menuDisplay: hide: "Nascondere" _wordMute: muteWords: "Parole da filtrare" - muteWordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con una interruzione di riga, indica la condizione \"O\"" + muteWordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)." muteWordsDescription2: "Se vuoi indicare delle Espressioni Regolari (regexp), metti la condizione all'interno di due slash (/)" - softDescription: "Verranno nascoste da tutte le Timeline quelle Note che soddisfano le seguenti condizioni" - hardDescription: "Impedisci alla istanza di caricare Note che soddisfano le seguenti condizioni. Le Note già filtrate sono già scomparse in modo irreversibile, fino al cambiamento delle condizioni. Dopo di che scompariranno quelle che soddisfano le nuove condizioni." - soft: "Leggero" - hard: "Pesante" - mutedNotes: "Note filtrate" _instanceMute: instanceMuteDescription: "Disattiva tutte le note, le note di rinvio (condivisione) dell'istanza configurata, comprese le risposte agli utenti dell'istanza." instanceMuteDescription2: "Impostazione separata da una nuova riga" @@ -1697,7 +1710,7 @@ _instanceMute: _theme: explore: "Esplora temi" install: "Installa un tema" - manage: "Gerisci temi" + manage: "Gestione temi" code: "Codice tema" description: "Descrizione" installed: "{name} è installato" @@ -1752,9 +1765,6 @@ _theme: infoFg: "Testo di informazioni" infoWarnBg: "Sfondo degli avvisi" infoWarnFg: "Testo di avviso" - cwBg: "Sfondo del CW" - cwFg: "Testo del pulsante CW" - cwHoverBg: "Sfondo del pulsante CW (sorvolato)" toastBg: "Sfondo di notifica a comparsa" toastFg: "Testo di notifica a comparsa" buttonBg: "Sfondo del pulsante" @@ -1959,7 +1969,7 @@ _visibility: followersDescription: "Visibile solo ai tuoi follower" specified: "Nota diretta" specifiedDescription: "Visibile solo ai profili menzionati" - disableFederation: "Non federare" + disableFederation: "Senza federazione" disableFederationDescription: "Non spedire attività alle altre istanze remote" _postForm: replyPlaceholder: "Rispondi a questa nota..." @@ -2102,7 +2112,7 @@ _notification: youRenoted: "{name} ha rinotato" youGotMessagingMessageFromUser: "{name} ti ha mandato un messaggio" youGotMessagingMessageFromGroup: "{name} ti ha mandato un messaggio nella chat" - youWereFollowed: "Ha iniziato a seguirti" + youWereFollowed: "Adesso ti segue" youReceivedFollowRequest: "Hai ricevuto una richiesta di follow" yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata" youWereInvitedToGroup: "Invitat@ al gruppo" @@ -2216,3 +2226,6 @@ _moderationLogTypes: unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito" resolveAbuseReport: "Segnalazione risolta" createInvitation: "Genera codice di invito" + createAd: "Banner creato" + deleteAd: "Banner eliminato" + updateAd: "Banner aggiornato" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 344650f0e7..e6deadec2f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1212,6 +1212,12 @@ fileAttachedOnly: "ファイル付きのみ" showRepliesToOthersInTimeline: "TLに他の人への返信を含める" hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない" externalServices: "外部サービス" +impressum: "運営者情報" +impressumUrl: "運営者情報URL" +impressumDescription: "ドイツなどの一部の国と地域では表示が義務付けられています(Impressum)。" +privacyPolicy: "プライバシーポリシー" +privacyPolicyUrl: "プライバシーポリシーURL" +tosAndPrivacyPolicy: "利用規約・プライバシーポリシー" showCatOnly: "キャット付きのみ" additionalPermissionsForFlash: "Playへの追加許可" thisFlashRequiresTheFollowingPermissions: "このPlayは以下の権限を要求しています" @@ -1724,6 +1730,10 @@ _ad: reduceFrequencyOfThisAd: "この広告の表示頻度を下げる" hide: "表示しない" timezoneinfo: "曜日はサーバーのタイムゾーンを元に指定されます。" + adsSettings: "広告配信設定" + notesPerOneAd: "リアルタイム更新中に広告を配信する間隔(ノートの個数)" + setZeroToDisable: "0でリアルタイム更新時の広告配信を無効" + adsTooClose: "広告の配信間隔が極めて短いため、ユーザー体験が著しく損われる可能性があります。" _forgotPassword: enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。" @@ -1977,9 +1987,6 @@ _theme: infoFg: "情報の文字" infoWarnBg: "警告の背景" infoWarnFg: "警告の文字" - cwBg: "CW ボタンの背景" - cwFg: "CW ボタンの文字" - cwHoverBg: "CW ボタンの背景 (ホバー)" toastBg: "通知トーストの背景" toastFg: "通知トーストの文字" buttonBg: "ボタンの背景" @@ -2478,6 +2485,15 @@ _moderationLogTypes: deleteAd: "広告を削除" updateAd: "広告を更新" +_fileViewer: + title: "ファイルの詳細" + type: "ファイルタイプ" + size: "ファイルサイズ" + url: "URL" + uploadedAt: "追加日" + attachedNotes: "添付されているノート" + thisPageCanBeSeenFromTheAuthor: "このページは、このファイルをアップロードしたユーザーしか閲覧できません。" + _abuse: _resolver: 1hour: "一時間" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 98e73e5a88..82e0c8e709 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -1666,11 +1666,6 @@ _wordMute: muteWords: "ミュートするワード" muteWordsDescription: "スペースで区切るとAND指定になって、改行で区切るとOR指定になるで。" muteWordsDescription2: "キーワードをスラッシュで囲むと正規表現になるで。" - softDescription: "指定した条件のノートをタイムラインから隠すで。" - hardDescription: "指定した条件のノートをタイムラインに追加しないようにするで。追加せーへんかったかったノートは、条件を変えても除外されたままになるで。" - soft: "ソフト" - hard: "ハード" - mutedNotes: "ミュートされたノート" _instanceMute: instanceMuteDescription: "ミュートしたサーバーのユーザーへの返信を含めて、設定したインスタンスの全てのノートとRenoteをミュートにするで。" instanceMuteDescription2: "改行で区切って設定するんやで" @@ -1734,9 +1729,6 @@ _theme: infoFg: "情報の文字" infoWarnBg: "警告の背景" infoWarnFg: "警告の文字" - cwBg: "CW ボタンの背景" - cwFg: "CW ボタンの文字" - cwHoverBg: "CW ボタンの背景 (ホバー)" toastBg: "通知トーストの背景" toastFg: "通知トーストの文字" buttonBg: "ボタンの背景" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 5532c2c80e..af1d7374c6 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1621,6 +1621,7 @@ _role: gtlAvailable: "글로벌 타임라인 보이기" ltlAvailable: "로컬 타임라인 보이기" canPublicNote: "공개 노트 허용" + canEditNote: "노트 편집 허용" canInvite: "서버 초대 코드 발행" inviteLimit: "초대 한도" inviteLimitCycle: "초대 발급 간격" @@ -1865,11 +1866,6 @@ _wordMute: muteWords: "뮤트할 단어" muteWordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정할 수 있어요" muteWordsDescription2: "정규 표현식을 사용하려면 키워드를 빗금표(/)로 감싸 주세요." - softDescription: "지정한 조건의 노트는 타임라인에서 숨겨질 거예요." - hardDescription: "지정한 조건의 노트는 타임라인에 추가되지 않아요. 나중에 조건을 변경해도 이미 올린 노트는 표시되지 않아요." - soft: "보통" - hard: "보다 높은 수준" - mutedNotes: "뮤트된 노트" _instanceMute: instanceMuteDescription: "뮤트한 서버에서 오는 답글을 포함한 모든 노트와 리노트를 뮤트해요." instanceMuteDescription2: "한 줄에 하나씩 입력해 주세요" @@ -1933,9 +1929,6 @@ _theme: infoFg: "정보창 텍스트" infoWarnBg: "경고창 배경" infoWarnFg: "경고창 텍스트" - cwBg: "CW 버튼 배경" - cwFg: "CW 버튼 텍스트" - cwHoverBg: "CW 버튼 배경 (호버)" toastBg: "알림창 배경" toastFg: "알림창 텍스트" buttonBg: "버튼 배경" diff --git a/locales/no-NO.yml b/locales/no-NO.yml index 97d5f53ea0..344dbefcae 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -575,9 +575,6 @@ _channel: nameAndDescription: "Navn og beskrivelse" _menuDisplay: hide: "Skjul" -_wordMute: - soft: "Myk" - hard: "Hard" _theme: description: "Beskrivelse" color: "Farge" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 087496f0a5..a4fcb60067 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -1058,9 +1058,6 @@ _menuDisplay: _wordMute: muteWords: "Słowo do wyciszenia" muteWordsDescription2: "Otocz słowa kluczowe ukośnikami, aby używać wyrażeń regularnych." - soft: "Łagodny" - hard: "Twardy" - mutedNotes: "Wyciszone wpisy" _instanceMute: title: "Ukrywa wpisy z wymienionych instancji." heading: "Lista instancji do wyciszenia" @@ -1122,9 +1119,6 @@ _theme: infoFg: "Tekst informacji" infoWarnBg: "Tło ostrzeżenia" infoWarnFg: "Tekst ostrzeżenia" - cwBg: "Tło CW" - cwFg: "Tekst CW" - cwHoverBg: "Tło CW (po najechaniu)" toastBg: "Tło powiadomień" toastFg: "Tekst powiadomień" buttonBg: "Tło przycisku" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 724cacfa7c..2cb0ad091d 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -1568,11 +1568,6 @@ _wordMute: muteWords: "Скрыть слово" muteWordsDescription: "Пишите слова через пробел в одной строке, чтобы фильтровать их появление вместе; а если хотите фильтровать любое из них, пишите в отдельных строках." muteWordsDescription2: "Здесь можно использовать регулярные выражения — просто заключите их между двумя дробными чертами (/)." - softDescription: "Соответствующие условиям заметки будут спрятаны из вашей ленты." - hardDescription: "Соответстующие условиям заметки вообще не будут попадать в вашу ленту. Даже если вы поменяете условия, отсеенные таким образом заметки уже не появятся." - soft: "Мягко" - hard: "Жёстко" - mutedNotes: "Скрытые заметки" _instanceMute: instanceMuteDescription: "Заметки и репосты с указанных здесь инстансов, а также ответы пользователям оттуда же не будут отображаться." instanceMuteDescription2: "Пишите каждый инстанс на отдельной строке" @@ -1636,9 +1631,6 @@ _theme: infoFg: "Текст сообщения" infoWarnBg: "Фон предупреждения" infoWarnFg: "Текст предупреждения" - cwBg: "Фон предупреждения о содержимом" - cwFg: "Текст предупреждения о содержимом" - cwHoverBg: "Фон предупреждения о содержимом (под указателем)" toastBg: "Фон оповещения" toastFg: "Текст оповещения" buttonBg: "Фон кнопки" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 0c9a7f68a3..1b5c04cab8 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -1119,11 +1119,6 @@ _wordMute: muteWords: "Umlčané slová" muteWordsDescription: "Medzerami oddeľte pre podmienku AND a novými riadkami pre podmienku OR." muteWordsDescription2: "Regulárne výrazy sa použijú keď použijete okolo lomítka." - softDescription: "Skryje poznámky z časovej osi, ktoré spĺňajú podmienky." - hardDescription: "Zabráni poznámky spĺňajúce množinu podmienok, aby boli pridané do časovej osi. Navyše tieto poznámky nepribudnú v časovej osi ani keď sa podmienky zmenia." - soft: "Mäkké" - hard: "Tvrdé" - mutedNotes: "Umlčané poznámky" _instanceMute: instanceMuteDescription: "Toto umlčí všetky poznámky/preposlania zo zoznamu serverov, vrátane tých, na ktoré používatelia odpovedajú z umlčaného servera." instanceMuteDescription2: "Oddeľte novými riadkami" @@ -1187,9 +1182,6 @@ _theme: infoFg: "Informačný text" infoWarnBg: "Pozadie varovania" infoWarnFg: "Text varovania" - cwBg: "CW pozadie tlačidla" - cwFg: "CW text tlačidla" - cwHoverBg: "CW pozadie tlačidla (pod kurzorom)" toastBg: "Pozadie upozornenia" toastFg: "Text upozornenia" buttonBg: "Pozadie tlačidla" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index b602e33c67..abbe604aef 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1164,6 +1164,7 @@ _serverRules: description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ" _serverSettings: iconUrl: "ไอคอน URL" + manifestJsonOverride: "manifest.json โอเวอร์ลาย" shortName: "ชื่อย่อ" _accountMigration: moveFrom: "ย้ายข้อมูลบัญชีอื่นไปยังอีกบัญชีนี้หนึ่ง" @@ -1421,6 +1422,7 @@ _achievements: flavor: "CherryPick-CherryPick La-Tu-Ma" _smashTestNotificationButton: title: "ทดสอบโอเวอร์โฟลว์" + description: "ทดสอบการแจ้งเตือนทริกเกอร์ซ้ำๆ ภายในระยะเวลาอันสั้นๆ" _role: new: "บทบาทใหม่" edit: "แก้ไขบทบาท" @@ -1479,6 +1481,7 @@ _role: descriptionOfRateLimitFactor: "ขีดจํากัดอัตราที่ต่ำกว่ามีข้อจํากัดน้อยกว่าข้อจํากัดที่สูงกว่า" canHideAds: "ซ่อนโฆษณา" canSearchNotes: "การใช้การค้นหาโน้ต" + canUseTranslator: "การใช้งานแปล" _condition: isLocal: "ผู้ใช้ภายใน" isRemote: "ผู้ใช้ระยะไกล" @@ -1678,11 +1681,6 @@ _wordMute: muteWords: "ปิดเสียงคำ" muteWordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR นะ" muteWordsDescription2: "ล้อมรอบคีย์เวิร์ดด้วยเครื่องหมายทับเพื่อใช้นิพจน์ทั่วไป" - softDescription: "ซ่อนโน้ตให้ตรงตามเงื่อนไขที่ตั้งไว้จากไทม์ไลน์" - hardDescription: "ป้องกันไม่ให้โน้ตย่อที่ตรงตามเงื่อนไขที่ตั้งไว้ไม่ให้ถูกเพิ่มลงในไทม์ไลน์ นอกจากนี้ โน้ตเหล่านี้จะไม่ถูกเพิ่มลงในไทม์ไลน์แม้ว่าจะมีการเปลี่ยนแปลงเงื่อนไขยังไงก็ตาม" - soft: "ซอฟ" - hard: "ยาก" - mutedNotes: "ปิดเสียงโน้ต" _instanceMute: instanceMuteDescription: "การดำเนินการนี้จะปิดเสียง\"โน้ต/รีโน้ต\"จากอินสแตนซ์ที่อยู่ในรายการ รวมถึงบันทึกของผู้ใช้ที่ตอบกลับผู้ใช้จากอินสแตนซ์ที่ปิดเสียง" instanceMuteDescription2: "คั่นด้วยการขึ้นบรรทัดใหม่" @@ -1746,9 +1744,6 @@ _theme: infoFg: "ข้อความข้อมูล" infoWarnBg: "คำเตือนพื้นหลัง" infoWarnFg: "คำเตือนข้อความ" - cwBg: "ปุ่ม CW พื้นหลัง" - cwFg: "ปุ่ม CW ข้อความ" - cwHoverBg: "ปุ่ม CW พื้นหลัง (โฮเวอร์)" toastBg: "ประวัติการแจ้งเตือน" toastFg: "ข้อความแจ้งเตือน" buttonBg: "ปุ่มพื้นหลัง" @@ -1873,6 +1868,7 @@ _antennaSources: users: "โน้ตจากผู้ใช้ที่เฉพาะเจาะจง" userList: "โน้ตจากรายชื่อผู้ใช้ที่ระบุ" userGroup: "โน้ตจากผู้ใช้ในกลุ่มที่ระบุ" + userBlacklist: "โน้ตทั้งหมดยกเว้นโน้ตของผู้ใช้ที่ต้องระบุเจาะจงตั้งแต่หนึ่งรายขึ้นไป" _weekday: sunday: "วันอาทิตย์" monday: "วันจันทร์" @@ -1972,6 +1968,7 @@ _profile: metadataContent: "เนื้อหา" changeAvatar: "เปลี่ยนอวาตาร์" changeBanner: "เปลี่ยนแบนเนอร์" + verifiedLinkDescription: "โดยการป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณตรงนี้ ส่วนไอคอนการยืนยันความเป็นเจ้าของนั้นก็สามารถแสดงถัดจากฟิลด์ได้นะ" _exportOrImport: allNotes: "โน้ตทั้งหมด" favoritedNotes: "บันทึกที่ชื่นชอบ" @@ -2190,7 +2187,17 @@ _moderationLogTypes: updateUserNote: "อัปเดตโน้ตการกลั่นกรองแล้ว" deleteDriveFile: "ลบไฟล์ออกแล้ว" deleteNote: "ลบโน้ตออกแล้ว" + createGlobalAnnouncement: "สร้างประกาศทั่วโลกแล้ว" + createUserAnnouncement: "สร้างประกาศผู้ใช้แล้ว" + updateGlobalAnnouncement: "อัปเดตประกาศทั่วโลกแล้ว" + updateUserAnnouncement: "อัปเดตประกาศผู้ใช้แล้ว" + deleteGlobalAnnouncement: "ลบประกาศทั่วโลกออกแล้ว" + deleteUserAnnouncement: "ลบประกาศผู้ใช้ออกแล้ว" resetPassword: "รีเซ็ตรหัสผ่าน" + suspendRemoteInstance: "อินสแตนซ์ระยะไกลถูกระงับ" + unsuspendRemoteInstance: "อินสแตนซ์ระยะไกลเลิกการระงับ" + markSensitiveDriveFile: "ทำเครื่องหมายไฟล์บอกว่าละเอียดอ่อน" + unmarkSensitiveDriveFile: "ยกเลิกทำเครื่องหมายไฟล์ว่าละเอียดอ่อน" resolveAbuseReport: "รายงานได้รับการแก้ไขแล้ว" createInvitation: "สร้างคำเชิญ" createAd: "สร้างโฆษณาแล้ว" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index ce4e261aa1..47c45f4213 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -1306,11 +1306,6 @@ _wordMute: muteWords: "Заглушені слова" muteWordsDescription: "Розділення ключових слів пробілами для \"І\" або з нової лінійки для \"АБО\"" muteWordsDescription2: "Для використання RegEx, ключові слова потрібно вписати поміж слешів \"/\"." - softDescription: "Приховати записи які відповідають критеріям зі стрічки подій." - hardDescription: "Приховати записи які відповідають критеріям зі стрічки подій. Також приховані записи не будуть додані до стрічки подій навіть якщо критерії буде змінено." - soft: "М'яко" - hard: "Жорстко" - mutedNotes: "Заблоковані нотатки" _instanceMute: instanceMuteDescription2: "Розділяйте новими рядками" title: "Приховує нотатки з перелічених інстансів." @@ -1368,9 +1363,6 @@ _theme: infoFg: "Текст інформації" infoWarnBg: "Фон попередження" infoWarnFg: "Текст попередження" - cwBg: "Фон чутливого змісту" - cwFg: "Текст чутливого змісту" - cwHoverBg: "Фон чутливого змісту (при наведенні)" toastBg: "Фон повідомлення" toastFg: "Текст повідомлення" buttonBg: "Фон кнопки" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 2201e9a4da..6c7690a56a 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -1484,11 +1484,6 @@ _wordMute: muteWords: "Ẩn từ ngữ" muteWordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition." muteWordsDescription2: "Bao quanh các từ khóa bằng dấu gạch chéo để sử dụng cụm từ thông dụng." - softDescription: "Ẩn các tút phù hợp điều kiện đã đặt khỏi bảng tin." - hardDescription: "Ngăn các tút đáp ứng các điều kiện đã đặt xuất hiện trên bảng tin. Lưu ý, những tút này sẽ không được thêm vào bảng tin ngay cả khi các điều kiện được thay đổi." - soft: "Yếu" - hard: "Mạnh" - mutedNotes: "Những tút đã ẩn" _instanceMute: instanceMuteDescription: "Thao tác này sẽ ẩn mọi tút/lượt đăng lại từ các máy chủ được liệt kê, bao gồm cả những tút dạng trả lời từ máy chủ bị ẩn." instanceMuteDescription2: "Tách bằng cách xuống dòng" @@ -1552,9 +1547,6 @@ _theme: infoFg: "Chữ thông tin" infoWarnBg: "Nền cảnh báo" infoWarnFg: "Chữ cảnh báo" - cwBg: "Nền nút nội dung ẩn" - cwFg: "Chữ nút nội dung ẩn" - cwHoverBg: "Nền nút nội dung ẩn (Chạm)" toastBg: "Nền thông báo" toastFg: "Chữ thông báo" buttonBg: "Nền nút" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 49e4c729a4..73ad738cf2 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1140,6 +1140,8 @@ edited: "已编辑" notificationRecieveConfig: "通知接收设置" mutualFollow: "互相关注" fileAttachedOnly: "仅限媒体" +showRepliesToOthersInTimeline: "在时间线上显示给其他人的回复" +hideRepliesToOthersInTimeline: "在时间线上隐藏给其他人的回复" _announcement: forExistingUsers: "仅限现有用户" forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。" @@ -1689,11 +1691,6 @@ _wordMute: muteWords: "禁用词" muteWordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。" muteWordsDescription2: "正则表达式用斜线包裹" - softDescription: "隐藏时间线中指定条件的帖子。" - hardDescription: "防止将具有指定条件的帖子添加到时间线。 即使您更改条件,未添加的帖文也会被排除在外。" - soft: "软屏蔽" - hard: "硬屏蔽" - mutedNotes: "被屏蔽的帖子" _instanceMute: instanceMuteDescription: "屏蔽服务器中的所有帖子和转帖,包括这些服务器上的用户回复。" instanceMuteDescription2: "一行一个" @@ -1757,9 +1754,6 @@ _theme: infoFg: "信息文本" infoWarnBg: "警告背景" infoWarnFg: "警告文本" - cwBg: "隐藏内容按钮背景" - cwFg: "隐藏内容按钮文本" - cwHoverBg: "隐藏内容按钮背景(悬停)" toastBg: "Toast 通知背景" toastFg: "Toast 通知文本" buttonBg: "按钮背景" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index d9ef28186e..2fd45c08c0 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -156,7 +156,7 @@ emojiUrl: "表情符號 URL" addEmoji: "新增表情符號" settingGuide: "推薦設定" cacheRemoteFiles: "快取遠端檔案" -cacheRemoteFilesDescription: "啟用此設定後,遠端檔案會被快取在本伺服器的儲存空間中。雖然顯示圖片會變快,但會消耗較多伺服器的儲存空間。至於要快取遠端使用者到什麼程度,是依照角色的雲端硬碟容量而定。當超過這個限制時,從較舊的檔案開始自快取中刪除並改為連結。關閉這個設定時,遠端檔案從一開始就維持連結的方式,但建議將 default.yml 的 proxyRemoteFiles 設為 true,以便產生圖片的縮圖並保護使用者的隱私,。" +cacheRemoteFilesDescription: "啟用此設定後,遠端檔案會被快取在本伺服器的儲存空間中。雖然顯示圖片會變快,但會消耗較多伺服器的儲存空間。至於要快取遠端使用者到什麼程度,是依照角色的雲端硬碟容量而定。當超過這個限制時,從較舊的檔案開始自快取中刪除並改為連結。關閉這個設定時,遠端檔案從一開始就維持連結的方式,但建議將 default.yml 的 proxyRemoteFiles 設為 true,以便產生圖片的縮圖並保護使用者的隱私。" youCanCleanRemoteFilesCache: "按檔案管理的🗑️按鈕,可將快取全部刪除。" cacheRemoteSensitiveFiles: "快取遠端的敏感檔案" cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取遠端的敏感檔案,而是直接連結。" @@ -1137,7 +1137,18 @@ authenticationRequiredToContinue: "請於繼續前完成驗證" dateAndTime: "日期與時間" showRenotes: "顯示轉發貼文" edited: "已編輯" +notificationRecieveConfig: "接受通知的設定" mutualFollow: "互相追隨" +fileAttachedOnly: "包含附件" +showRepliesToOthersInTimeline: "在時間軸上顯示給其他人的回覆" +hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆" +externalServices: "外部服務" +impressum: "營運者資訊" +impressumUrl: "營運者資訊網址" +impressumDescription: "在德國與部份地區必須要明確顯示營運者資訊。" +privacyPolicy: "隱私政策" +privacyPolicyUrl: "隱私政策網址" +tosAndPrivacyPolicy: "服務條款和隱私政策" _announcement: forExistingUsers: "僅限既有的使用者" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" @@ -1488,6 +1499,7 @@ _role: descriptionOfRateLimitFactor: "值越小限制越少,值越大限制越多。" canHideAds: "不顯示廣告" canSearchNotes: "可否搜尋貼文" + canUseTranslator: "使用翻譯功能" _condition: isLocal: "本地使用者" isRemote: "遠端使用者" @@ -1536,6 +1548,10 @@ _ad: reduceFrequencyOfThisAd: "降低此廣告的頻率 " hide: "隱藏" timezoneinfo: "星期幾是由伺服器的時區指定的。" + adsSettings: "廣告投放設定" + notesPerOneAd: "即時更新中投放廣告的間隔(貼文數)" + setZeroToDisable: "設為 0 則在即時更新時不投放廣告" + adsTooClose: "由於廣告投放的間隔極短,可能會嚴重影響使用者體驗。" _forgotPassword: enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。" ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 " @@ -1687,11 +1703,6 @@ _wordMute: muteWords: "加入靜音文字" muteWordsDescription: "空格代表「以及」(AND),換行代表「或者」(OR)。" muteWordsDescription2: "用斜線包圍關鍵字代表正規表達式。" - softDescription: "隱藏時間軸中符合特定條件的貼文。" - hardDescription: "符合特定條件的貼文將不會新增至時間軸。 即使您更改條件,未被新增的貼文也會被排除在外。" - soft: "軟性靜音" - hard: "硬性靜音" - mutedNotes: "已靜音的貼文" _instanceMute: instanceMuteDescription: "包括對被靜音實例上的使用者的回覆,被設定的實例上所有貼文及轉發都會被靜音。" instanceMuteDescription2: "設定時以換行進行分隔" @@ -1755,9 +1766,6 @@ _theme: infoFg: "資訊內容" infoWarnBg: "警告背景" infoWarnFg: "警告文字" - cwBg: "隱藏內容按鈕背景" - cwFg: "隱藏內容按鈕文字" - cwHoverBg: "隱藏內容按鈕背景(懸浮)" toastBg: "通知背景" toastFg: "通知文本" buttonBg: "按鈕背景" diff --git a/package.json b/package.json index 118befda5b..0106d066d3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cherrypick", "version": "4.3.3", - "basedMisskeyVersion": "2023.10.0-beta.3", + "basedMisskeyVersion": "2023.10.0", "codename": "nasubi", "repository": { "type": "git", @@ -50,15 +50,15 @@ "cssnano": "6.0.1", "js-yaml": "4.1.0", "postcss": "8.4.31", - "terser": "5.20.0", + "terser": "5.21.0", "typescript": "5.2.2" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "6.7.3", - "@typescript-eslint/parser": "6.7.3", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", "cross-env": "7.0.3", "cypress": "13.3.0", - "eslint": "8.50.0", + "eslint": "8.51.0", "start-server-and-test": "2.0.1" }, "optionalDependencies": { diff --git a/packages/backend/migration/1696003580220-AddSomeUrls.js b/packages/backend/migration/1696003580220-AddSomeUrls.js new file mode 100644 index 0000000000..660cc2b099 --- /dev/null +++ b/packages/backend/migration/1696003580220-AddSomeUrls.js @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class AddSomeUrls1696003580220 { + name = 'AddSomeUrls1696003580220' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "impressumUrl" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "privacyPolicyUrl" character varying(1024)`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "impressumUrl"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "privacyPolicyUrl"`); + } +} diff --git a/packages/backend/migration/1696569742153-clean-up.js b/packages/backend/migration/1696569742153-clean-up.js new file mode 100644 index 0000000000..661d6aeaca --- /dev/null +++ b/packages/backend/migration/1696569742153-clean-up.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class CleanUp1696569742153 { + name = 'CleanUp1696569742153' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_01f4581f114e0ebd2bbb876f0b"`); + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "score"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "score" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`CREATE INDEX "IDX_01f4581f114e0ebd2bbb876f0b" ON "note_reaction" ("createdAt") `); + } +} diff --git a/packages/backend/migration/1696581429196-clean-up.js b/packages/backend/migration/1696581429196-clean-up.js new file mode 100644 index 0000000000..d75d37f726 --- /dev/null +++ b/packages/backend/migration/1696581429196-clean-up.js @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class CleanUp1696581429196 { + name = 'CleanUp1696581429196' + + async up(queryRunner) { + await queryRunner.query(`DROP TABLE IF EXISTS "muted_note"`); + } + + async down(queryRunner) { + } +} diff --git a/packages/backend/migration/1696743032098-AdsOnStream.js b/packages/backend/migration/1696743032098-AdsOnStream.js new file mode 100644 index 0000000000..d0bf699bc4 --- /dev/null +++ b/packages/backend/migration/1696743032098-AdsOnStream.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class AdsOnStream1696743032098 { + name = 'AdsOnStream1696743032098' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "notesPerOneAd" integer NOT NULL DEFAULT '0'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "notesPerOneAd"`); + } +} diff --git a/packages/backend/migration/1696807733453-userListUserId.js b/packages/backend/migration/1696807733453-userListUserId.js new file mode 100644 index 0000000000..62ce987a34 --- /dev/null +++ b/packages/backend/migration/1696807733453-userListUserId.js @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class UserListUserId1696807733453 { + name = 'UserListUserId1696807733453' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_list_membership" ADD "userListUserId" character varying(32) NOT NULL DEFAULT ''`); + const memberships = await queryRunner.query(`SELECT "id", "userListId" FROM "user_list_membership"`); + for(let i = 0; i < memberships.length; i++) { + const userList = await queryRunner.query(`SELECT "userId" FROM "user_list" WHERE "id" = $1`, [memberships[i].userListId]); + await queryRunner.query(`UPDATE "user_list_membership" SET "userListUserId" = $1 WHERE "id" = $2`, [userList[0].userId, memberships[i].id]); + } + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_list_membership" DROP COLUMN "userListUserId"`); + } +} diff --git a/packages/backend/migration/1696808725134-userListUserId-2.js b/packages/backend/migration/1696808725134-userListUserId-2.js new file mode 100644 index 0000000000..ded6b22b12 --- /dev/null +++ b/packages/backend/migration/1696808725134-userListUserId-2.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class UserListUserId21696808725134 { + name = 'UserListUserId21696808725134' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_list_membership" ALTER COLUMN "userListUserId" DROP DEFAULT`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_list_membership" ALTER COLUMN "userListUserId" SET DEFAULT ''`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 6baaf9a1fc..e8d8a38687 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -73,14 +73,14 @@ "@fastify/view": "8.2.0", "@google-cloud/logging": "^10.5.0", "@google-cloud/translate": "^7.2.1", - "@nestjs/common": "10.2.6", - "@nestjs/core": "10.2.6", - "@nestjs/testing": "10.2.6", + "@nestjs/common": "10.2.7", + "@nestjs/core": "10.2.7", + "@nestjs/testing": "10.2.7", "@peertube/http-signature": "1.7.0", "@simplewebauthn/server": "8.2.0", "@sinonjs/fake-timers": "11.1.0", "@swc/cli": "0.1.62", - "@swc/core": "1.3.90", + "@swc/core": "1.3.92", "@vitalets/google-translate-api": "9.2.0", "accepts": "1.3.8", "ajv": "8.12.0", @@ -89,7 +89,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.2", - "bullmq": "4.11.4", + "bullmq": "4.12.3", "cacheable-lookup": "7.0.0", "cbor": "9.0.1", "chalk": "5.3.0", @@ -127,13 +127,13 @@ "nanoid": "5.0.1", "nested-property": "4.0.0", "node-fetch": "3.3.2", - "nodemailer": "6.9.5", + "nodemailer": "6.9.6", "nsfwjs": "2.4.2", "oauth": "0.10.0", "oauth2orize": "1.11.1", "oauth2orize-pkce": "0.1.2", "os-utils": "0.0.14", - "otpauth": "9.1.4", + "otpauth": "9.1.5", "parse5": "7.1.2", "pg": "8.11.3", "pkce-challenge": "4.0.1", @@ -159,7 +159,7 @@ "stringz": "2.1.0", "strip-ansi": "^7.1.0", "summaly": "github:misskey-dev/summaly", - "systeminformation": "5.21.9", + "systeminformation": "5.21.11", "tinycolor2": "1.6.0", "tmp": "0.2.1", "tsc-alias": "1.8.8", @@ -193,13 +193,13 @@ "@types/jsrsasign": "10.5.9", "@types/mime-types": "2.1.2", "@types/ms": "0.7.32", - "@types/node": "20.7.1", + "@types/node": "20.8.4", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.11", "@types/oauth": "0.9.2", "@types/oauth2orize": "1.11.1", "@types/oauth2orize-pkce": "0.1.0", - "@types/pg": "8.10.3", + "@types/pg": "8.10.4", "@types/pug": "2.0.7", "@types/punycode": "2.1.0", "@types/qrcode": "1.5.2", @@ -216,11 +216,11 @@ "@types/vary": "1.1.1", "@types/web-push": "3.6.1", "@types/ws": "8.5.6", - "@typescript-eslint/eslint-plugin": "6.7.3", - "@typescript-eslint/parser": "6.7.3", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", "aws-sdk-client-mock": "3.0.0", "cross-env": "7.0.3", - "eslint": "8.50.0", + "eslint": "8.51.0", "eslint-plugin-import": "2.28.1", "execa": "8.0.1", "jest": "29.7.0", diff --git a/packages/backend/src/boot/common.ts b/packages/backend/src/boot/common.ts index 1e00375bbd..50375fc73d 100644 --- a/packages/backend/src/boot/common.ts +++ b/packages/backend/src/boot/common.ts @@ -17,7 +17,6 @@ export async function server() { const app = await NestFactory.createApplicationContext(MainModule, { logger: new NestLogger(), }); - app.enableShutdownHooks(); const serverService = app.get(ServerService); await serverService.launch(); @@ -35,7 +34,6 @@ export async function jobQueue() { const jobQueue = await NestFactory.createApplicationContext(QueueProcessorModule, { logger: new NestLogger(), }); - jobQueue.enableShutdownHooks(); jobQueue.get(QueueProcessorService).start(); jobQueue.get(ChartManagementService).start(); diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index 878decbac0..0fc1f60e0b 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -228,7 +228,7 @@ export class AccountMoveService { }, }).then(memberships => memberships.map(membership => membership.userListId)); - const newMemberships: Map = new Map(); + const newMemberships: Map = new Map(); // 重複しないようにIDを生成 const genId = (): string => { @@ -244,6 +244,7 @@ export class AccountMoveService { createdAt: new Date(), userId: dst.id, userListId: membership.userListId, + userListUserId: membership.userListUserId, }); } diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index db26ed893f..72d3a4e23e 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -16,6 +16,7 @@ import type { AntennasRepository, UserGroupJoiningsRepository, UserListMembershi import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -41,6 +42,7 @@ export class AntennaService implements OnApplicationShutdown { private utilityService: UtilityService, private globalEventService: GlobalEventService, + private redisTimelineService: RedisTimelineService, ) { this.antennasFetched = false; this.antennas = []; @@ -87,12 +89,7 @@ export class AntennaService implements OnApplicationShutdown { const redisPipeline = this.redisForTimelines.pipeline(); for (const antenna of matchedAntennas) { - redisPipeline.xadd( - `antennaTimeline:${antenna.id}`, - 'MAXLEN', '~', '200', - '*', - 'note', note.id); - + this.redisTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline); this.globalEventService.publishAntennaStream(antenna.id, 'note', note); } diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 33b1726afe..0234355ae8 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -61,6 +61,8 @@ import { UtilityService } from './UtilityService.js'; import { FileInfoService } from './FileInfoService.js'; import { SearchService } from './SearchService.js'; import { ClipService } from './ClipService.js'; +import { FeaturedService } from './FeaturedService.js'; +import { RedisTimelineService } from './RedisTimelineService.js'; import { ChartLoggerService } from './chart/ChartLoggerService.js'; import FederationChart from './chart/charts/federation.js'; import NotesChart from './chart/charts/notes.js'; @@ -193,6 +195,8 @@ const $UtilityService: Provider = { provide: 'UtilityService', useExisting: Util const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService }; const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService }; const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService }; +const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService }; +const $RedisTimelineService: Provider = { provide: 'RedisTimelineService', useExisting: RedisTimelineService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -329,6 +333,8 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv FileInfoService, SearchService, ClipService, + FeaturedService, + RedisTimelineService, ChartLoggerService, FederationChart, NotesChart, @@ -458,6 +464,8 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv $FileInfoService, $SearchService, $ClipService, + $FeaturedService, + $RedisTimelineService, $ChartLoggerService, $FederationChart, $NotesChart, @@ -588,6 +596,8 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv FileInfoService, SearchService, ClipService, + FeaturedService, + RedisTimelineService, FederationChart, NotesChart, UsersChart, @@ -716,6 +726,8 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv $FileInfoService, $SearchService, $ClipService, + $FeaturedService, + $RedisTimelineService, $FederationChart, $NotesChart, $UsersChart, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index c9d9de4335..f347434474 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -48,7 +48,6 @@ export class CustomEmojiService implements OnApplicationShutdown { fetcher: () => this.emojisRepository.find({ where: { host: IsNull() } }).then(emojis => new Map(emojis.map(emoji => [emoji.name, emoji]))), toRedisConverter: (value) => JSON.stringify(Array.from(value.values())), fromRedisConverter: (value) => { - if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す) return new Map(JSON.parse(value).map((x: Serialized) => [x.name, { ...x, updatedAt: x.updatedAt ? new Date(x.updatedAt) : null, @@ -383,6 +382,20 @@ export class CustomEmojiService implements OnApplicationShutdown { } } + /** + * ローカル内の絵文字に重複がないかチェックします + * @param name 絵文字名 + */ + @bindThis + public checkDuplicate(name: string): Promise { + return this.emojisRepository.exist({ where: { name, host: IsNull() } }); + } + + @bindThis + public getEmojiById(id: string): Promise { + return this.emojisRepository.findOneBy({ id }); + } + @bindThis public dispose(): void { this.cache.dispose(); diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts new file mode 100644 index 0000000000..5b9a412ddd --- /dev/null +++ b/packages/backend/src/core/FeaturedService.ts @@ -0,0 +1,116 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import type { MiNote, MiUser } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; + +const GLOBAL_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと +const PER_USER_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 7; // 1週間ごと +const HASHTAG_RANKING_WINDOW = 1000 * 60 * 60; // 1時間ごと + +@Injectable() +export class FeaturedService { + constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, // TODO: 専用のRedisサーバーを設定できるようにする + ) { + } + + @bindThis + private getCurrentWindow(windowRange: number): number { + const passed = new Date().getTime() - new Date(new Date().getFullYear(), 0, 1).getTime(); + return Math.floor(passed / windowRange); + } + + @bindThis + private async updateRankingOf(name: string, windowRange: number, element: string, score = 1): Promise { + const currentWindow = this.getCurrentWindow(windowRange); + const redisTransaction = this.redisClient.multi(); + redisTransaction.zincrby( + `${name}:${currentWindow}`, + score, + element); + redisTransaction.expire( + `${name}:${currentWindow}`, + (windowRange * 3) / 1000, + 'NX'); // "NX -- Set expiry only when the key has no expiry" = 有効期限がないときだけ設定 + await redisTransaction.exec(); + } + + @bindThis + private async getRankingOf(name: string, windowRange: number, threshold: number): Promise { + const currentWindow = this.getCurrentWindow(windowRange); + const previousWindow = currentWindow - 1; + + const redisPipeline = this.redisClient.pipeline(); + redisPipeline.zrange( + `${name}:${currentWindow}`, 0, threshold, 'REV', 'WITHSCORES'); + redisPipeline.zrange( + `${name}:${previousWindow}`, 0, threshold, 'REV', 'WITHSCORES'); + const [currentRankingResult, previousRankingResult] = await redisPipeline.exec().then(result => result ? result.map(r => r[1] as string[]) : [[], []]); + + const ranking = new Map(); + for (let i = 0; i < currentRankingResult.length; i += 2) { + const noteId = currentRankingResult[i]; + const score = parseInt(currentRankingResult[i + 1], 10); + ranking.set(noteId, score); + } + for (let i = 0; i < previousRankingResult.length; i += 2) { + const noteId = previousRankingResult[i]; + const score = parseInt(previousRankingResult[i + 1], 10); + const exist = ranking.get(noteId); + if (exist != null) { + ranking.set(noteId, (exist + score) / 2); + } else { + ranking.set(noteId, score); + } + } + + return Array.from(ranking.keys()); + } + + @bindThis + public updateGlobalNotesRanking(noteId: MiNote['id'], score = 1): Promise { + return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score); + } + + @bindThis + public updateInChannelNotesRanking(channelId: MiNote['channelId'], noteId: MiNote['id'], score = 1): Promise { + return this.updateRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, noteId, score); + } + + @bindThis + public updatePerUserNotesRanking(userId: MiUser['id'], noteId: MiNote['id'], score = 1): Promise { + return this.updateRankingOf(`featuredPerUserNotesRanking:${userId}`, PER_USER_NOTES_RANKING_WINDOW, noteId, score); + } + + @bindThis + public updateHashtagsRanking(hashtag: string, score = 1): Promise { + return this.updateRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, hashtag, score); + } + + @bindThis + public getGlobalNotesRanking(threshold: number): Promise { + return this.getRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, threshold); + } + + @bindThis + public getInChannelNotesRanking(channelId: MiNote['channelId'], threshold: number): Promise { + return this.getRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, threshold); + } + + @bindThis + public getPerUserNotesRanking(userId: MiUser['id'], threshold: number): Promise { + return this.getRankingOf(`featuredPerUserNotesRanking:${userId}`, PER_USER_NOTES_RANKING_WINDOW, threshold); + } + + @bindThis + public getHashtagsRanking(threshold: number): Promise { + return this.getRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, threshold); + } +} diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 97a54f5ccb..653fa245ca 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import type { MiUser } from '@/models/User.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; @@ -12,15 +13,22 @@ import type { MiHashtag } from '@/models/Hashtag.js'; import type { HashtagsRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; +import { FeaturedService } from '@/core/FeaturedService.js'; +import { MetaService } from '@/core/MetaService.js'; @Injectable() export class HashtagService { constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, // TODO: 専用のRedisサーバーを設定できるようにする + @Inject(DI.hashtagsRepository) private hashtagsRepository: HashtagsRepository, private userEntityService: UserEntityService, + private featuredService: FeaturedService, private idService: IdService, + private metaService: MetaService, ) { } @@ -46,6 +54,9 @@ export class HashtagService { public async updateHashtag(user: { id: MiUser['id']; host: MiUser['host']; }, tag: string, isUserAttached = false, inc = true) { tag = normalizeForSearch(tag); + // TODO: サンプリング + this.updateHashtagsRanking(tag, user.id); + const index = await this.hashtagsRepository.findOneBy({ name: tag }); if (index == null && !inc) return; @@ -85,7 +96,7 @@ export class HashtagService { } } } else { - // 自分が初めてこのタグを使ったなら + // 自分が初めてこのタグを使ったなら if (!index.mentionedUserIds.some(id => id === user.id)) { set.mentionedUserIds = () => `array_append("mentionedUserIds", '${user.id}')`; set.mentionedUsersCount = () => '"mentionedUsersCount" + 1'; @@ -144,4 +155,94 @@ export class HashtagService { } } } + + @bindThis + public async updateHashtagsRanking(hashtag: string, userId: MiUser['id']): Promise { + const instance = await this.metaService.fetch(); + const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); + if (hiddenTags.includes(hashtag)) return; + + // YYYYMMDDHHmm (10分間隔) + const now = new Date(); + now.setMinutes(Math.floor(now.getMinutes() / 10) * 10, 0, 0); + const window = `${now.getUTCFullYear()}${(now.getUTCMonth() + 1).toString().padStart(2, '0')}${now.getUTCDate().toString().padStart(2, '0')}${now.getUTCHours().toString().padStart(2, '0')}${now.getUTCMinutes().toString().padStart(2, '0')}`; + + const exist = await this.redisClient.sismember(`hashtagUsers:${hashtag}`, userId); + if (exist === 1) return; + + this.featuredService.updateHashtagsRanking(hashtag, 1); + + const redisPipeline = this.redisClient.pipeline(); + + // チャート用 + redisPipeline.pfadd(`hashtagUsers:${hashtag}:${window}`, userId); + redisPipeline.expire(`hashtagUsers:${hashtag}:${window}`, + 60 * 60 * 24 * 3, // 3日間 + 'NX', // "NX -- Set expiry only when the key has no expiry" = 有効期限がないときだけ設定 + ); + + // ユニークカウント用 + // TODO: Bloom Filter を使うようにしても良さそう + redisPipeline.sadd(`hashtagUsers:${hashtag}`, userId); + redisPipeline.expire(`hashtagUsers:${hashtag}`, + 60 * 60, // 1時間 + 'NX', // "NX -- Set expiry only when the key has no expiry" = 有効期限がないときだけ設定 + ); + + redisPipeline.exec(); + } + + @bindThis + public async getChart(hashtag: string, range: number): Promise { + const now = new Date(); + now.setMinutes(Math.floor(now.getMinutes() / 10) * 10, 0, 0); + + const redisPipeline = this.redisClient.pipeline(); + + for (let i = 0; i < range; i++) { + const window = `${now.getUTCFullYear()}${(now.getUTCMonth() + 1).toString().padStart(2, '0')}${now.getUTCDate().toString().padStart(2, '0')}${now.getUTCHours().toString().padStart(2, '0')}${now.getUTCMinutes().toString().padStart(2, '0')}`; + redisPipeline.pfcount(`hashtagUsers:${hashtag}:${window}`); + now.setMinutes(now.getMinutes() - (i * 10), 0, 0); + } + + const result = await redisPipeline.exec(); + + if (result == null) return []; + + return result.map(x => x[1]) as number[]; + } + + @bindThis + public async getCharts(hashtags: string[], range: number): Promise> { + const now = new Date(); + now.setMinutes(Math.floor(now.getMinutes() / 10) * 10, 0, 0); + + const redisPipeline = this.redisClient.pipeline(); + + for (let i = 0; i < range; i++) { + const window = `${now.getUTCFullYear()}${(now.getUTCMonth() + 1).toString().padStart(2, '0')}${now.getUTCDate().toString().padStart(2, '0')}${now.getUTCHours().toString().padStart(2, '0')}${now.getUTCMinutes().toString().padStart(2, '0')}`; + for (const hashtag of hashtags) { + redisPipeline.pfcount(`hashtagUsers:${hashtag}:${window}`); + } + now.setMinutes(now.getMinutes() - (i * 10), 0, 0); + } + + const result = await redisPipeline.exec(); + + if (result == null) return {}; + + // key is hashtag + const charts = {} as Record; + for (const hashtag of hashtags) { + charts[hashtag] = []; + } + + for (let i = 0; i < range; i++) { + for (let j = 0; j < hashtags.length; j++) { + charts[hashtags[j]].push(result[(i * hashtags.length) + j][1] as number); + } + } + + return charts; + } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 9a4f38a702..f0e3d4b040 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -54,6 +54,8 @@ import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { RoleService } from '@/core/RoleService.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; +import { FeaturedService } from '@/core/FeaturedService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -196,6 +198,7 @@ export class NoteCreateService implements OnApplicationShutdown { private idService: IdService, private globalEventService: GlobalEventService, private queueService: QueueService, + private redisTimelineService: RedisTimelineService, private noteReadService: NoteReadService, private notificationService: NotificationService, private relayService: RelayService, @@ -203,6 +206,7 @@ export class NoteCreateService implements OnApplicationShutdown { private hashtagService: HashtagService, private antennaService: AntennaService, private webhookService: WebhookService, + private featuredService: FeaturedService, private remoteUserResolveService: RemoteUserResolveService, private apDeliverManagerService: ApDeliverManagerService, private apRendererService: ApRendererService, @@ -349,14 +353,6 @@ export class NoteCreateService implements OnApplicationShutdown { const note = await this.insertNote(user, data, tags, emojis, mentionedUsers); - if (data.channel) { - this.redisForTimelines.xadd( - `channelTimeline:${data.channel.id}`, - 'MAXLEN', '~', this.config.perChannelMaxNoteCacheCount.toString(), - '*', - 'note', note.id); - } - setImmediate('post created', { signal: this.#shutdownController.signal }).then( () => this.postNoteCreated(note, user, data, silent, tags!, mentionedUsers!), () => { /* aborted, ignore this */ }, @@ -515,11 +511,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Increment notes count (user) this.incNotesCountOfUser(user); - if (data.visibility === 'specified') { - // TODO? - } else { - this.pushToTl(note, user); - } + this.pushToTl(note, user); this.antennaService.addNoteToAntennas(note, user); @@ -542,9 +534,8 @@ export class NoteCreateService implements OnApplicationShutdown { }); } - // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき - if (data.renote && (await this.noteEntityService.countSameRenotes(user.id, data.renote.id, note.id) === 0)) { - if (!user.isBot) this.incRenoteCount(data.renote); + if (data.renote && data.renote.userId !== user.id && !user.isBot) { + this.incRenoteCount(data.renote); } if (data.poll && data.poll.expiresAt) { @@ -744,10 +735,23 @@ export class NoteCreateService implements OnApplicationShutdown { this.notesRepository.createQueryBuilder().update() .set({ renoteCount: () => '"renoteCount" + 1', - score: () => '"score" + 1', }) .where('id = :id', { id: renote.id }) .execute(); + + // 30%の確率、3日以内に投稿されたノートの場合ハイライト用ランキング更新 + if (Math.random() < 0.3 && (Date.now() - this.idService.parse(renote.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3) { + if (renote.channelId != null) { + if (renote.replyId == null) { + this.featuredService.updateInChannelNotesRanking(renote.channelId, renote.id, 5); + } + } else { + if (renote.visibility === 'public' && renote.userHost == null && renote.replyId == null) { + this.featuredService.updateGlobalNotesRanking(renote.id, 5); + this.featuredService.updatePerUserNotesRanking(renote.userId, renote.id, 5); + } + } + } } @bindThis @@ -837,14 +841,12 @@ export class NoteCreateService implements OnApplicationShutdown { private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) { const meta = await this.metaService.fetch(); - const redisPipeline = this.redisForTimelines.pipeline(); + const r = this.redisForTimelines.pipeline(); if (note.channelId) { - redisPipeline.xadd( - `userTimelineWithChannel:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r); + + this.redisTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); const channelFollowings = await this.channelFollowingsRepository.find({ where: { @@ -854,138 +856,94 @@ export class NoteCreateService implements OnApplicationShutdown { }); for (const channelFollowing of channelFollowings) { - redisPipeline.xadd( - `homeTimeline:${channelFollowing.followerId}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${channelFollowing.followerId}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } } else { // TODO: キャッシュ? - const followings = await this.followingsRepository.find({ - where: { - followeeId: user.id, - followerHost: IsNull(), - isFollowerHibernated: false, - }, - select: ['followerId', 'withReplies'], - }); + // eslint-disable-next-line prefer-const + let [followings, userListMemberships] = await Promise.all([ + this.followingsRepository.find({ + where: { + followeeId: user.id, + followerHost: IsNull(), + isFollowerHibernated: false, + }, + select: ['followerId', 'withReplies'], + }), + this.userListMembershipsRepository.find({ + where: { + userId: user.id, + }, + select: ['userListId', 'userListUserId', 'withReplies'], + }), + ]); - const userListMemberships = await this.userListMembershipsRepository.find({ - where: { - userId: user.id, - }, - select: ['userListId', 'withReplies'], - }); + if (note.visibility === 'followers') { + // TODO: 重そうだから何とかしたい Set 使う? + userListMemberships = userListMemberships.filter(x => followings.some(f => f.followerId === x.userListUserId)); + } // TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする for (const following of followings) { - // 自分自身以外への返信 - if (note.replyId && note.replyUserId !== note.userId) { + // 基本的にvisibleUserIdsには自身のidが含まれている前提であること + if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue; + + // 「自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合 + if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) { if (!following.withReplies) continue; } - redisPipeline.xadd( - `homeTimeline:${following.followerId}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${following.followerId}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } - // TODO - //if (note.visibility === 'followers') { - // // TODO: 重そうだから何とかしたい Set 使う? - // userLists = userLists.filter(x => followings.some(f => f.followerId === x.userListUserId)); - //} - for (const userListMembership of userListMemberships) { - // 自分自身以外への返信 - if (note.replyId && note.replyUserId !== note.userId) { + // ダイレクトのとき、そのリストが対象外のユーザーの場合 + if ( + note.visibility === 'specified' && + !note.visibleUserIds.some(v => v === userListMembership.userListUserId) + ) continue; + + // 「自分自身への返信 or そのリストの作成者への返信」のどちらでもない場合 + if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) { if (!userListMembership.withReplies) continue; } - redisPipeline.xadd( - `userListTimeline:${userListMembership.userListId}`, - 'MAXLEN', '~', meta.perUserListTimelineCacheMax.toString(), - '*', - 'note', note.id); - + this.redisTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `userListTimelineWithFiles:${userListMembership.userListId}`, - 'MAXLEN', '~', (meta.perUserListTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r); } } - { // 自分自身のHTL - redisPipeline.xadd( - `homeTimeline:${user.id}`, - 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(), - '*', - 'note', note.id); - + if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL + this.redisTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `homeTimelineWithFiles:${user.id}`, - 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } // 自分自身以外への返信 if (note.replyId && note.replyUserId !== note.userId) { - redisPipeline.xadd( - `userTimelineWithReplies:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(), - '*', - 'note', note.id); - } else { - redisPipeline.xadd( - `userTimeline:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); + if (note.visibility === 'public' && note.userHost == null) { + this.redisTimelineService.push('localTimelineWithReplies', note.id, 300, r); + } + } else { + this.redisTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - `userTimelineWithFiles:${user.id}`, - 'MAXLEN', '~', note.userHost == null ? (meta.perLocalUserUserTimelineCacheMax / 2).toString() : (meta.perRemoteUserUserTimelineCacheMax / 2).toString(), - '*', - 'note', note.id); + this.redisTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r); } if (note.visibility === 'public' && note.userHost == null) { - redisPipeline.xadd( - 'localTimeline', - 'MAXLEN', '~', '1000', - '*', - 'note', note.id); - + this.redisTimelineService.push('localTimeline', note.id, 1000, r); if (note.fileIds.length > 0) { - redisPipeline.xadd( - 'localTimelineWithFiles', - 'MAXLEN', '~', '500', - '*', - 'note', note.id); + this.redisTimelineService.push('localTimelineWithFiles', note.id, 500, r); } } } @@ -997,7 +955,7 @@ export class NoteCreateService implements OnApplicationShutdown { } } - redisPipeline.exec(); + r.exec(); } @bindThis diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 9e6bba5db9..abe8e9ad00 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -64,12 +64,6 @@ export class NoteDeleteService { const deletedAt = new Date(); const cascadingNotes = await this.findCascadingNotes(note); - // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき - if (note.renoteId && (await this.noteEntityService.countSameRenotes(user.id, note.renoteId, note.id)) === 0) { - this.notesRepository.decrement({ id: note.renoteId }, 'renoteCount', 1); - if (!user.isBot) this.notesRepository.decrement({ id: note.renoteId }, 'score', 1); - } - if (note.replyId) { await this.notesRepository.decrement({ id: note.replyId }, 'repliesCount', 1); } diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 7c65d8bb72..3f5f566a8b 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -96,6 +96,8 @@ export class PollService { const note = await this.notesRepository.findOneBy({ id: noteId }); if (note == null) throw new Error('note not found'); + if (note.localOnly) return; + const user = await this.usersRepository.findOneBy({ id: note.userId }); if (user == null) throw new Error('note not found'); diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index c49f2b0278..2b18098651 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -9,6 +9,7 @@ import { DI } from '@/di-symbols.js'; import type { MiUser } from '@/models/User.js'; import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; import type { SelectQueryBuilder } from 'typeorm'; @Injectable() @@ -34,6 +35,8 @@ export class QueryService { @Inject(DI.renoteMutingsRepository) private renoteMutingsRepository: RenoteMutingsRepository, + + private idService: IdService, ) { } @@ -49,15 +52,15 @@ export class QueryService { q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); q.orderBy(`${q.alias}.id`, 'DESC'); } else if (sinceDate && untilDate) { - q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); - q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); - q.orderBy(`${q.alias}.createdAt`, 'DESC'); + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); + q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); + q.orderBy(`${q.alias}.id`, 'DESC'); } else if (sinceDate) { - q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); - q.orderBy(`${q.alias}.createdAt`, 'ASC'); + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: this.idService.genId(new Date(sinceDate)) }); + q.orderBy(`${q.alias}.id`, 'ASC'); } else if (untilDate) { - q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); - q.orderBy(`${q.alias}.createdAt`, 'DESC'); + q.andWhere(`${q.alias}.id < :untilId`, { untilId: this.idService.genId(new Date(untilDate)) }); + q.orderBy(`${q.alias}.id`, 'DESC'); } else { q.orderBy(`${q.alias}.id`, 'DESC'); } @@ -76,13 +79,15 @@ export class QueryService { // 投稿の引用元の作者にブロックされていない q .andWhere(`note.userId NOT IN (${ blockingQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserId IS NULL') - .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.replyUserId IS NULL') + .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`); })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserId IS NULL') - .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.renoteUserId IS NULL') + .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`); })); q.setParameters(blockingQuery.getParameters()); @@ -112,16 +117,17 @@ export class QueryService { .where('threadMuted.userId = :userId', { userId: me.id }); q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); - q.andWhere(new Brackets(qb => { qb - .where('note.threadId IS NULL') - .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); + q.andWhere(new Brackets(qb => { + qb + .where('note.threadId IS NULL') + .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); })); q.setParameters(mutedQuery.getParameters()); } @bindThis - public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: MiUser): void { + public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); @@ -139,26 +145,31 @@ export class QueryService { // 投稿の引用元の作者をミュートしていない q .andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserId IS NULL') - .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.replyUserId IS NULL') + .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserId IS NULL') - .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); + .andWhere(new Brackets(qb => { + qb + .where('note.renoteUserId IS NULL') + .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); })) // mute instances - .andWhere(new Brackets(qb => { qb - .andWhere('note.userHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); + .andWhere(new Brackets(qb => { + qb + .andWhere('note.userHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); })) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); + .andWhere(new Brackets(qb => { + qb + .where('note.replyUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); + .andWhere(new Brackets(qb => { + qb + .where('note.renoteUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); })); q.setParameters(mutingQuery.getParameters()); @@ -180,36 +191,41 @@ export class QueryService { public generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: MiUser['id'] } | null): void { // This code must always be synchronized with the checks in Notes.isVisibleForMe. if (me == null) { - q.andWhere(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); + q.andWhere(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); })); } else { const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :meId'); - q.andWhere(new Brackets(qb => { qb + q.andWhere(new Brackets(qb => { + qb // 公開投稿である - .where(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })) + .where(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })) // または 自分自身 - .orWhere('note.userId = :meId') + .orWhere('note.userId = :meId') // または 自分宛て - .orWhere(':meId = ANY(note.visibleUserIds)') - .orWhere(':meId = ANY(note.mentions)') - .orWhere(new Brackets(qb => { qb - // または フォロワー宛ての投稿であり、 - .where('note.visibility = \'followers\'') - .andWhere(new Brackets(qb => { qb - // 自分がフォロワーである - .where(`note.userId IN (${ followingQuery.getQuery() })`) - // または 自分の投稿へのリプライ - .orWhere('note.replyUserId = :meId'); + .orWhere(':meId = ANY(note.visibleUserIds)') + .orWhere(':meId = ANY(note.mentions)') + .orWhere(new Brackets(qb => { + qb + // または フォロワー宛ての投稿であり、 + .where('note.visibility = \'followers\'') + .andWhere(new Brackets(qb => { + qb + // 自分がフォロワーである + .where(`note.userId IN (${ followingQuery.getQuery() })`) + // または 自分の投稿へのリプライ + .orWhere('note.replyUserId = :meId'); + })); })); - })); })); q.setParameters({ meId: me.id }); diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 5a42ee9eda..5d8c2a2959 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/_.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; @@ -26,6 +27,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { RoleService } from '@/core/RoleService.js'; +import { FeaturedService } from '@/core/FeaturedService.js'; const FALLBACK = '❤'; @@ -66,6 +68,9 @@ const decodeCustomEmojiRegexp = /^:([\w+-]+)(?:@([\w.-]+))?:$/; @Injectable() export class ReactionService { constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -86,6 +91,7 @@ export class ReactionService { private noteEntityService: NoteEntityService, private userBlockingService: UserBlockingService, private idService: IdService, + private featuredService: FeaturedService, private globalEventService: GlobalEventService, private apRendererService: ApRendererService, private apDeliverManagerService: ApDeliverManagerService, @@ -182,11 +188,28 @@ export class ReactionService { await this.notesRepository.createQueryBuilder().update() .set({ reactions: () => sql, - ... (!user.isBot ? { score: () => '"score" + 1' } : {}), }) .where('id = :id', { id: note.id }) .execute(); + // 30%の確率、セルフではない、3日以内に投稿されたノートの場合ハイライト用ランキング更新 + if ( + Math.random() < 0.3 && + note.userId !== user.id && + (Date.now() - this.idService.parse(note.id).date.getTime()) < 1000 * 60 * 60 * 24 * 3 + ) { + if (note.channelId != null) { + if (note.replyId == null) { + this.featuredService.updateInChannelNotesRanking(note.channelId, note.id, 1); + } + } else { + if (note.visibility === 'public' && note.userHost == null && note.replyId == null) { + this.featuredService.updateGlobalNotesRanking(note.id, 1); + this.featuredService.updatePerUserNotesRanking(note.userId, note.id, 1); + } + } + } + const meta = await this.metaService.fetch(); if (meta.enableChartsForRemoteUser || (user.host == null)) { @@ -275,8 +298,6 @@ export class ReactionService { .where('id = :id', { id: note.id }) .execute(); - if (!user.isBot) this.notesRepository.decrement({ id: note.id }, 'score', 1); - this.globalEventService.publishNoteStream(note.id, 'unreacted', { reaction: this.decodeReaction(exist.reaction).reaction, userId: user.id, diff --git a/packages/backend/src/core/RedisTimelineService.ts b/packages/backend/src/core/RedisTimelineService.ts new file mode 100644 index 0000000000..ef14be2c86 --- /dev/null +++ b/packages/backend/src/core/RedisTimelineService.ts @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; + +@Injectable() +export class RedisTimelineService { + constructor( + @Inject(DI.redisForTimelines) + private redisForTimelines: Redis.Redis, + + private idService: IdService, + ) { + } + + @bindThis + public push(tl: string, id: string, maxlen: number, pipeline: Redis.ChainableCommander) { + // リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、 + // 3分以内に投稿されたものでない場合、Redisにある最古のIDより新しい場合のみ追加する + if (this.idService.parse(id).date.getTime() > Date.now() - 1000 * 60 * 3) { + pipeline.lpush('list:' + tl, id); + if (Math.random() < 0.1) { // 10%の確率でトリム + pipeline.ltrim('list:' + tl, 0, maxlen - 1); + } + } else { + // 末尾のIDを取得 + this.redisForTimelines.lindex('list:' + tl, -1).then(lastId => { + if (lastId == null || (this.idService.parse(id).date.getTime() > this.idService.parse(lastId).date.getTime())) { + this.redisForTimelines.lpush('list:' + tl, id); + } else { + Promise.resolve(); + } + }); + } + } + + @bindThis + public get(name: string, untilId?: string | null, sinceId?: string | null) { + if (untilId && sinceId) { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1)); + } else if (untilId) { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.filter(id => id < untilId).sort((a, b) => a > b ? -1 : 1)); + } else if (sinceId) { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1)); + } else { + return this.redisForTimelines.lrange('list:' + name, 0, -1) + .then(ids => ids.sort((a, b) => a > b ? -1 : 1)); + } + } + + @bindThis + public getMulti(name: string[], untilId?: string | null, sinceId?: string | null): Promise { + const pipeline = this.redisForTimelines.pipeline(); + for (const n of name) { + pipeline.lrange('list:' + n, 0, -1); + } + return pipeline.exec().then(res => { + if (res == null) return []; + const tls = res.map(r => r[1] as string[]); + return tls.map(ids => + (untilId && sinceId) + ? ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1) + : untilId + ? ids.filter(id => id < untilId).sort((a, b) => a > b ? -1 : 1) + : sinceId + ? ids.filter(id => id > sinceId).sort((a, b) => a < b ? -1 : 1) + : ids.sort((a, b) => a > b ? -1 : 1), + ); + }); + } +} diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 5b601d4bf9..59b916b095 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -20,6 +20,7 @@ import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import type { Packed } from '@/misc/json-schema.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; export type RolePolicies = { @@ -106,6 +107,7 @@ export class RoleService implements OnApplicationShutdown { private globalEventService: GlobalEventService, private idService: IdService, private moderationLogService: ModerationLogService, + private redisTimelineService: RedisTimelineService, ) { //this.onMessage = this.onMessage.bind(this); @@ -478,12 +480,7 @@ export class RoleService implements OnApplicationShutdown { const redisPipeline = this.redisClient.pipeline(); for (const role of roles) { - redisPipeline.xadd( - `roleTimeline:${role.id}`, - 'MAXLEN', '~', '1000', - '*', - 'note', note.id); - + this.redisTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline); this.globalEventService.publishRoleTimelineStream(role.id, 'note', note); } diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 1b9b18d188..465e365c50 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -97,6 +97,7 @@ export class UserListService implements OnApplicationShutdown { createdAt: new Date(), userId: target.id, userListId: list.id, + userListUserId: list.userId, } as MiUserListMembership); this.globalEventService.publishInternalEvent('userListMemberAdded', { userListId: list.id, memberId: target.id }); diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index cc346d3d1d..697fbd6893 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -473,25 +473,10 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - public async countSameRenotes(userId: string, renoteId: string, excludeNoteId: string | undefined): Promise { - // 指定したユーザーの指定したノートのリノートがいくつあるか数える - const query = this.notesRepository.createQueryBuilder('note') - .where('note.userId = :userId', { userId }) - .andWhere('note.renoteId = :renoteId', { renoteId }); - - // 指定した投稿を除く - if (excludeNoteId) { - query.andWhere('note.id != :excludeNoteId', { excludeNoteId }); - } - - return await query.getCount(); - } - - @bindThis - private async findNoteOrFail(id: string): Promise { - return await this.notesRepository.findOneOrFail({ + private findNoteOrFail(id: string): Promise { + return this.notesRepository.findOneOrFail({ where: { id }, - relations: ["user"], + relations: ['user'], }); } } diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 35f500e220..cf0db25b12 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -33,9 +33,10 @@ export class RoleEntityService { const assignedCount = await this.roleAssignmentsRepository.createQueryBuilder('assign') .where('assign.roleId = :roleId', { roleId: role.id }) - .andWhere(new Brackets(qb => { qb - .where('assign.expiresAt IS NULL') - .orWhere('assign.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('assign.expiresAt IS NULL') + .orWhere('assign.expiresAt > :now', { now: new Date() }); })) .getCount(); diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 23c6ea5180..705cd78603 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -153,64 +153,76 @@ export class UserEntityService implements OnModuleInit { @bindThis public async getRelation(me: MiUser['id'], target: MiUser['id']) { - const following = await this.followingsRepository.findOneBy({ - followerId: me, - followeeId: target, - }); - return awaitAll({ - id: target, + const [ following, - isFollowing: following != null, - isFollowed: this.followingsRepository.count({ + isFollowed, + hasPendingFollowRequestFromYou, + hasPendingFollowRequestToYou, + isBlocking, + isBlocked, + isMuted, + isRenoteMuted, + ] = await Promise.all([ + this.followingsRepository.findOneBy({ + followerId: me, + followeeId: target, + }), + this.followingsRepository.exist({ where: { followerId: target, followeeId: me, }, - take: 1, - }).then(n => n > 0), - hasPendingFollowRequestFromYou: this.followRequestsRepository.count({ + }), + this.followRequestsRepository.exist({ where: { followerId: me, followeeId: target, }, - take: 1, - }).then(n => n > 0), - hasPendingFollowRequestToYou: this.followRequestsRepository.count({ + }), + this.followRequestsRepository.exist({ where: { followerId: target, followeeId: me, }, - take: 1, - }).then(n => n > 0), - isBlocking: this.blockingsRepository.count({ + }), + this.blockingsRepository.exist({ where: { blockerId: me, blockeeId: target, }, - take: 1, - }).then(n => n > 0), - isBlocked: this.blockingsRepository.count({ + }), + this.blockingsRepository.exist({ where: { blockerId: target, blockeeId: me, }, - take: 1, - }).then(n => n > 0), - isMuted: this.mutingsRepository.count({ + }), + this.mutingsRepository.exist({ where: { muterId: me, muteeId: target, }, - take: 1, - }).then(n => n > 0), - isRenoteMuted: this.renoteMutingsRepository.count({ + }), + this.renoteMutingsRepository.exist({ where: { muterId: me, muteeId: target, }, - take: 1, - }).then(n => n > 0), - }); + }), + ]); + + return { + id: target, + following, + isFollowing: following != null, + isFollowed, + hasPendingFollowRequestFromYou, + hasPendingFollowRequestToYou, + isBlocking, + isBlocked, + isMuted, + isRenoteMuted, + }; } @bindThis diff --git a/packages/backend/src/misc/loader.ts b/packages/backend/src/misc/loader.ts index 49aac5d883..25f7b54d31 100644 --- a/packages/backend/src/misc/loader.ts +++ b/packages/backend/src/misc/loader.ts @@ -1,9 +1,12 @@ export type FetchFunction = (key: K) => Promise; + type ResolveReject = Parameters>[0]>; + type ResolverPair = { resolve: ResolveReject[0]; reject: ResolveReject[1]; }; + export class DebounceLoader { private resolverMap = new Map>(); private promiseMap = new Map>(); diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 53273fdb30..3df8906c32 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -376,6 +376,18 @@ export class MiMeta { }) public feedbackUrl: string | null; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public impressumUrl: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public privacyPolicyUrl: string | null; + @Column('varchar', { length: 8192, nullable: true, @@ -610,6 +622,11 @@ export class MiMeta { }) public perUserListTimelineCacheMax: number; + @Column('integer', { + default: 0, + }) + public notesPerOneAd: number; + @Column('boolean', { default: false, }) diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 9e643db2d9..17d93de122 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -160,11 +160,6 @@ export class MiNote { }) public url: string | null; - @Column('integer', { - default: 0, select: false, - }) - public score: number; - @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/NoteReaction.ts b/packages/backend/src/models/NoteReaction.ts index 378e6c1712..a17e80b890 100644 --- a/packages/backend/src/models/NoteReaction.ts +++ b/packages/backend/src/models/NoteReaction.ts @@ -14,7 +14,6 @@ export class MiNoteReaction { @PrimaryColumn(id()) public id: string; - @Index() @Column('timestamp with time zone', { comment: 'The created date of the NoteReaction.', }) diff --git a/packages/backend/src/models/UserListMembership.ts b/packages/backend/src/models/UserListMembership.ts index 77935e311e..4255f3fa2b 100644 --- a/packages/backend/src/models/UserListMembership.ts +++ b/packages/backend/src/models/UserListMembership.ts @@ -50,4 +50,11 @@ export class MiUserListMembership { default: false, }) public withReplies: boolean; + + //#region Denormalized fields + @Column({ + ...id(), + }) + public userListUserId: MiUser['id']; + //#endregion } diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index ef3035b038..5c91d3ab85 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -379,9 +379,10 @@ export class ActivityPubServerService { if (page) { const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) .andWhere('note.userId = :userId', { userId: user.id }) - .andWhere(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); + .andWhere(new Brackets(qb => { + qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); })) .andWhere('note.localOnly = FALSE'); diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 5d864a4f73..f672c06711 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -107,6 +107,8 @@ export class NodeinfoServerService { }, langs: meta.langs, tosUrl: meta.termsOfServiceUrl, + privacyPolicyUrl: meta.privacyPolicyUrl, + impressumUrl: meta.impressumUrl, repositoryUrl: meta.repositoryUrl, feedbackUrl: meta.feedbackUrl, disableRegistration: meta.disableRegistration, @@ -138,7 +140,11 @@ export class NodeinfoServerService { .type( 'application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.1#"', ) - .header('Cache-Control', 'public, max-age=600'); + .header('Cache-Control', 'public, max-age=600') + .header('Access-Control-Allow-Headers', 'Accept') + .header('Access-Control-Allow-Methods', 'GET, OPTIONS') + .header('Access-Control-Allow-Origin', '*') + .header('Access-Control-Expose-Headers', 'Vary'); return { version: '2.1', ...base }; }); @@ -151,7 +157,11 @@ export class NodeinfoServerService { .type( 'application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"', ) - .header('Cache-Control', 'public, max-age=600'); + .header('Cache-Control', 'public, max-age=600') + .header('Access-Control-Allow-Headers', 'Accept') + .header('Access-Control-Allow-Methods', 'GET, OPTIONS') + .header('Access-Control-Allow-Origin', '*') + .header('Access-Control-Expose-Headers', 'Vary'); return { version: '2.0', ...base }; }); diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index ee5d4d802f..30466ec83f 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -199,10 +199,10 @@ export class ServerService implements OnApplicationShutdown { includeSecrets: true, })); - reply.code(200); - return 'Verify succeeded!'; + reply.code(200).send('Verification succeeded! メールアドレスの認証に成功しました。'); + return; } else { - reply.code(404); + reply.code(404).send('Verification failed. Please try again. メールアドレスの認証に失敗しました。もう一度お試しください'); return; } }); diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 7067998401..618b05afb9 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -342,6 +342,7 @@ import * as ep___users_followers from './endpoints/users/followers.js'; import * as ep___users_following from './endpoints/users/following.js'; import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; +import * as ep___users_featuredNotes from './endpoints/users/featured-notes.js'; import * as ep___users_groups_create from './endpoints/users/groups/create.js'; import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; @@ -720,6 +721,7 @@ const $users_followers: Provider = { provide: 'ep:users/followers', useClass: ep const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default }; const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }; const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }; +const $users_featuredNotes: Provider = { provide: 'ep:users/featured-notes', useClass: ep___users_featuredNotes.default }; const $users_groups_create: Provider = { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }; const $users_groups_delete: Provider = { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }; const $users_groups_invitations_accept: Provider = { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }; @@ -1103,6 +1105,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_following, $users_gallery_posts, $users_getFrequentlyRepliedUsers, + $users_featuredNotes, $users_groups_create, $users_groups_delete, $users_groups_invitations_accept, @@ -1475,6 +1478,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_following, $users_gallery_posts, $users_getFrequentlyRepliedUsers, + $users_featuredNotes, $users_groups_create, $users_groups_delete, $users_groups_invitations_accept, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 34385f4f28..f18d79d52d 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -341,6 +341,7 @@ import * as ep___users_followers from './endpoints/users/followers.js'; import * as ep___users_following from './endpoints/users/following.js'; import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; +import * as ep___users_featuredNotes from './endpoints/users/featured-notes.js'; import * as ep___users_groups_create from './endpoints/users/groups/create.js'; import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; @@ -717,6 +718,7 @@ const eps = [ ['users/following', ep___users_following], ['users/gallery/posts', ep___users_gallery_posts], ['users/get-frequently-replied-users', ep___users_getFrequentlyRepliedUsers], + ['users/featured-notes', ep___users_featuredNotes], ['users/groups/create', ep___users_groups_create], ['users/groups/delete', ep___users_groups_delete], ['users/groups/invitations/accept', ep___users_groups_invitations_accept], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 9a71afc93f..a86c84e90b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -23,6 +23,11 @@ export const meta = { code: 'NO_SUCH_FILE', id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', }, + duplicateName: { + message: 'Duplicate name.', + code: 'DUPLICATE_NAME', + id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975', + }, }, } as const; @@ -64,6 +69,8 @@ export default class extends Endpoint { // eslint- super(meta, paramDef, async (ps, me) => { const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); + if (isDuplicate) throw new ApiError(meta.errors.duplicateName); const emoji = await this.customEmojiService.add({ driveFile, 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 8738c428ab..cc4faefa5b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -74,6 +74,15 @@ export default class extends Endpoint { // eslint- driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } + const emoji = await this.customEmojiService.getEmojiById(ps.id); + if (emoji != null) { + if (ps.name !== emoji.name) { + const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); + if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); + } + } else { + throw new ApiError(meta.errors.noSuchEmoji); + } await this.customEmojiService.update(ps.id, { driveFile, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index d67c90559c..c544212742 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -357,6 +357,10 @@ export const meta = { type: 'number', optional: false, nullable: false, }, + notesPerOneAd: { + type: 'number', + optional: false, nullable: false, + }, enableReceivePrerelease: { type: 'boolean', optional: false, nullable: false, @@ -404,6 +408,8 @@ export default class extends Endpoint { // eslint- tosUrl: instance.termsOfServiceUrl, repositoryUrl: instance.repositoryUrl, feedbackUrl: instance.feedbackUrl, + impressumUrl: instance.impressumUrl, + privacyPolicyUrl: instance.privacyPolicyUrl, disableRegistration: instance.disableRegistration, emailRequiredForSignup: instance.emailRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, @@ -503,6 +509,7 @@ export default class extends Endpoint { // eslint- perRemoteUserUserTimelineCacheMax: instance.perRemoteUserUserTimelineCacheMax, perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax, perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax, + notesPerOneAd: instance.notesPerOneAd, enableReceivePrerelease: instance.enableReceivePrerelease, skipVersion: instance.skipVersion, skipCherryPickVersion: instance.skipCherryPickVersion, diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index 1147962141..df34984f63 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -61,9 +61,10 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) .andWhere('assign.roleId = :roleId', { roleId: role.id }) - .andWhere(new Brackets(qb => { qb - .where('assign.expiresAt IS NULL') - .orWhere('assign.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('assign.expiresAt IS NULL') + .orWhere('assign.expiresAt > :now', { now: new Date() }); })) .innerJoinAndSelect('assign.user', 'user'); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 6d5a9919a2..4091ec5b4d 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -85,6 +85,7 @@ export default class extends Endpoint { // eslint- isModerator: isModerator, isSilenced: isSilenced, isSuspended: user.isSuspended, + isHibernated: user.isHibernated, lastActiveDate: user.lastActiveDate, moderationNote: profile.moderationNote ?? '', signins, 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 9f5145343f..7d40ee9f7c 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -94,6 +94,8 @@ export const paramDef = { tosUrl: { type: 'string', nullable: true }, repositoryUrl: { type: 'string' }, feedbackUrl: { type: 'string' }, + impressumUrl: { type: 'string' }, + privacyPolicyUrl: { type: 'string' }, useObjectStorage: { type: 'boolean' }, objectStorageBaseUrl: { type: 'string', nullable: true }, objectStorageBucket: { type: 'string', nullable: true }, @@ -135,6 +137,7 @@ export const paramDef = { perRemoteUserUserTimelineCacheMax: { type: 'integer' }, perUserHomeTimelineCacheMax: { type: 'integer' }, perUserListTimelineCacheMax: { type: 'integer' }, + notesPerOneAd: { type: 'integer' }, enableReceivePrerelease: { type: 'boolean' }, skipVersion: { type: 'boolean' }, skipCherryPickVersion: { type: 'string', nullable: true }, @@ -372,6 +375,14 @@ export default class extends Endpoint { // eslint- set.feedbackUrl = ps.feedbackUrl; } + if (ps.impressumUrl !== undefined) { + set.impressumUrl = ps.impressumUrl; + } + + if (ps.privacyPolicyUrl !== undefined) { + set.privacyPolicyUrl = ps.privacyPolicyUrl; + } + if (ps.useObjectStorage !== undefined) { set.useObjectStorage = ps.useObjectStorage; } @@ -576,6 +587,10 @@ export default class extends Endpoint { // eslint- set.perUserListTimelineCacheMax = ps.perUserListTimelineCacheMax; } + if (ps.notesPerOneAd !== undefined) { + set.notesPerOneAd = ps.notesPerOneAd; + } + if (ps.enableReceivePrerelease !== undefined) { set.enableReceivePrerelease = ps.enableReceivePrerelease; } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index bd9753dcdf..e1fda37d36 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -12,6 +12,7 @@ import { NoteReadService } from '@/core/NoteReadService.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -69,8 +70,12 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private queryService: QueryService, private noteReadService: NoteReadService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, userId: me.id, @@ -85,19 +90,8 @@ export default class extends Endpoint { // eslint- lastUsedAt: new Date(), }); - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - const noteIdsRes = await this.redisForTimelines.xrevrange( - `antennaTimeline:${antenna.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - - if (noteIdsRes.length === 0) { - return []; - } - - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); - + let noteIds = await this.redisTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; } @@ -115,7 +109,11 @@ export default class extends Endpoint { // eslint- this.queryService.generateBlockedUserQuery(query, me); const notes = await query.getMany(); - notes.sort((a, b) => a.id > b.id ? -1 : 1); + if (sinceId != null && untilId == null) { + notes.sort((a, b) => a.id < b.id ? -1 : 1); + } else { + notes.sort((a, b) => a.id > b.id ? -1 : 1); + } if (notes.length > 0) { this.noteReadService.read(me.id, notes); diff --git a/packages/backend/src/server/api/endpoints/channels/search.ts b/packages/backend/src/server/api/endpoints/channels/search.ts index 76371aa0b9..fba8e7ea68 100644 --- a/packages/backend/src/server/api/endpoints/channels/search.ts +++ b/packages/backend/src/server/api/endpoints/channels/search.ts @@ -55,9 +55,10 @@ export default class extends Endpoint { // eslint- if (ps.query !== '') { if (ps.type === 'nameAndDescription') { - query.andWhere(new Brackets(qb => { qb - .where('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }) - .orWhere('channel.description ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); + query.andWhere(new Brackets(qb => { + qb + .where('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }) + .orWhere('channel.description ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); })); } else { query.andWhere('channel.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index a04bcc9c8f..454984d8f9 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -12,6 +12,9 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; +import { isUserRelated } from '@/misc/is-user-related.js'; +import { CacheService } from '@/core/CacheService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -66,9 +69,15 @@ export default class extends Endpoint { // eslint- private idService: IdService, private noteEntityService: NoteEntityService, private queryService: QueryService, + private redisTimelineService: RedisTimelineService, + private cacheService: CacheService, private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const isRangeSpecified = untilId != null && sinceId != null; + const channel = await this.channelsRepository.findOneBy({ id: ps.channelId, }); @@ -77,68 +86,66 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchChannel); } - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - noteIdsRes = await this.redisForTimelines.xrevrange( - `channelTimeline:${channel.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - } - - // redis から取得していないとき・取得数が足りないとき - if (noteIdsRes.length < limit) { - //#region Construct query - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.channelId = :channelId', { channelId: channel.id }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('note.channel', 'channel'); - - if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - } - //#endregion - - timeline = await query.limit(ps.limit).getMany(); - } else { - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); - - if (noteIds.length === 0) { - return []; - } - - //#region Construct query - const query = this.notesRepository.createQueryBuilder('note') - .where('note.id IN (:...noteIds)', { noteIds: noteIds }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('note.channel', 'channel'); - - if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - } - //#endregion - - timeline = await query.getMany(); - timeline.sort((a, b) => a.id > b.id ? -1 : 1); - } - if (me) this.activeUsersChart.read(me); + if (isRangeSpecified || sinceId == null) { + const [ + userIdsWhoMeMuting, + ] = me ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(me.id), + ]) : [new Set()]; + + let noteIds = await this.redisTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); + + if (noteIds.length > 0) { + const query = this.notesRepository.createQueryBuilder('note') + .where('note.id IN (:...noteIds)', { noteIds: noteIds }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); + + let timeline = await query.getMany(); + + timeline = timeline.filter(note => { + if (me && isUserRelated(note, userIdsWhoMeMuting)) return false; + + return true; + }); + + // TODO: フィルタで件数が減った場合の埋め合わせ処理 + + timeline.sort((a, b) => a.id > b.id ? -1 : 1); + + if (timeline.length > 0) { + return await this.noteEntityService.packMany(timeline, me); + } + } + } + + //#region fallback to database + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.channelId = :channelId', { channelId: channel.id }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); + + if (me) { + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + } + //#endregion + + const timeline = await query.limit(ps.limit).getMany(); + return await this.noteEntityService.packMany(timeline, me); + //#endregion }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index c1e5d8ba70..1f843a0ec1 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -6,6 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { NotesRepository, DriveFilesRepository } from '@/models/_.js'; +import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -41,6 +42,9 @@ export const meta = { export const paramDef = { type: 'object', properties: { + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, fileId: { type: 'string', format: 'misskey:id' }, }, required: ['fileId'], @@ -56,6 +60,7 @@ export default class extends Endpoint { // eslint- private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { // Fetch file @@ -68,9 +73,10 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchFile); } - const notes = await this.notesRepository.createQueryBuilder('note') - .where(':file = ANY(note.fileIds)', { file: file.id }) - .getMany(); + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); + query.andWhere(':file = ANY(note.fileIds)', { file: file.id }); + + const notes = await query.limit(ps.limit).getMany(); return await this.noteEntityService.packMany(notes, me, { detail: true, diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 6ed15820db..2a20986bf0 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -3,29 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { NotesRepository } from '@/models/_.js'; -import type { MiNote } from '@/models/Note.js'; -import { safeForSql } from '@/misc/safe-for-sql.js'; -import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; - -/* -トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前~今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要 -ユニーク投稿数とはそのハッシュタグと投稿ユーザーのペアのカウントで、例えば同じユーザーが複数回同じハッシュタグを投稿してもそのハッシュタグのユニーク投稿数は1とカウントされる - -..が理想だけどPostgreSQLでどうするのか分からないので単に「直近Aの内に投稿されたユニーク投稿数が多いハッシュタグ」で妥協する -*/ - -const rangeA = 1000 * 60 * 60; // 60分 -//const rangeB = 1000 * 60 * 120; // 2時間 -//const coefficient = 1.25; // 「n倍」の部分 -//const requiredUsers = 3; // 最低何人がそのタグを投稿している必要があるか - -const max = 5; +import { FeaturedService } from '@/core/FeaturedService.js'; +import { HashtagService } from '@/core/HashtagService.js'; export const meta = { tags: ['hashtags'], @@ -71,98 +53,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - private metaService: MetaService, + private featuredService: FeaturedService, + private hashtagService: HashtagService, ) { super(meta, paramDef, async () => { - const instance = await this.metaService.fetch(true); - const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); + const ranking = await this.featuredService.getHashtagsRanking(10); - const now = new Date(); // 5分単位で丸めた現在日時 - now.setMinutes(Math.round(now.getMinutes() / 5) * 5, 0, 0); + const charts = ranking.length === 0 ? {} : await this.hashtagService.getCharts(ranking, 20); - const tagNotes = await this.notesRepository.createQueryBuilder('note') - .where('note.createdAt > :date', { date: new Date(now.getTime() - rangeA) }) - .andWhere(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })) - .andWhere('note.tags != \'{}\'') - .select(['note.tags', 'note.userId']) - .cache(60000) // 1 min - .getMany(); - - if (tagNotes.length === 0) { - return []; - } - - const tags: { - name: string; - users: MiNote['userId'][]; - }[] = []; - - for (const note of tagNotes) { - for (const tag of note.tags) { - if (hiddenTags.includes(tag)) continue; - - const x = tags.find(x => x.name === tag); - if (x) { - if (!x.users.includes(note.userId)) { - x.users.push(note.userId); - } - } else { - tags.push({ - name: tag, - users: [note.userId], - }); - } - } - } - - // タグを人気順に並べ替え - const hots = tags - .sort((a, b) => b.users.length - a.users.length) - .map(tag => tag.name) - .slice(0, max); - - //#region 2(または3)で話題と判定されたタグそれぞれについて過去の投稿数グラフを取得する - const countPromises: Promise[] = []; - - const range = 20; - - // 10分 - const interval = 1000 * 60 * 10; - - for (let i = 0; i < range; i++) { - countPromises.push(Promise.all(hots.map(tag => this.notesRepository.createQueryBuilder('note') - .select('count(distinct note.userId)') - .where(`'{"${safeForSql(tag) ? tag : 'aichan_kawaii'}"}' <@ note.tags`) - .andWhere('note.createdAt < :lt', { lt: new Date(now.getTime() - (interval * i)) }) - .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - (interval * (i + 1))) }) - .cache(60000) // 1 min - .getRawOne() - .then(x => parseInt(x.count, 10)), - ))); - } - - const countsLog = await Promise.all(countPromises); - //#endregion - - const totalCounts = await Promise.all(hots.map(tag => this.notesRepository.createQueryBuilder('note') - .select('count(distinct note.userId)') - .where(`'{"${safeForSql(tag) ? tag : 'aichan_kawaii'}"}' <@ note.tags`) - .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - rangeA) }) - .cache(60000 * 60) // 60 min - .getRawOne() - .then(x => parseInt(x.count, 10)), - )); - - const stats = hots.map((tag, i) => ({ + const stats = ranking.map((tag, i) => ({ tag, - chart: countsLog.map(counts => counts[i]), - usersCount: totalCounts[i], + chart: charts[tag], + usersCount: Math.max(...charts[tag]), })); return stats; diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index e37a0345d2..e8c1b2091f 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -185,6 +185,11 @@ export const meta = { }, }, }, + notesPerOneAd: { + type: 'number', + optional: false, nullable: false, + default: 0, + }, requireSetup: { type: 'boolean', optional: false, nullable: false, @@ -304,6 +309,8 @@ export default class extends Endpoint { // eslint- tosUrl: instance.termsOfServiceUrl, repositoryUrl: instance.repositoryUrl, feedbackUrl: instance.feedbackUrl, + impressumUrl: instance.impressumUrl, + privacyPolicyUrl: instance.privacyPolicyUrl, disableRegistration: instance.disableRegistration, emailRequiredForSignup: instance.emailRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, @@ -334,6 +341,7 @@ export default class extends Endpoint { // eslint- imageUrl: ad.imageUrl, dayOfWeek: ad.dayOfWeek, })), + notesPerOneAd: instance.notesPerOneAd, enableEmail: instance.enableEmail, enableServiceWorker: instance.enableServiceWorker, diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 6430b4b96a..f189dedb54 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -49,16 +49,19 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where('note.replyId = :noteId', { noteId: ps.noteId }) - .orWhere(new Brackets(qb => { qb - .where('note.renoteId = :noteId', { noteId: ps.noteId }) - .andWhere(new Brackets(qb => { qb - .where('note.text IS NOT NULL') - .orWhere('note.fileIds != \'{}\'') - .orWhere('note.hasPoll = TRUE'); + .andWhere(new Brackets(qb => { + qb + .where('note.replyId = :noteId', { noteId: ps.noteId }) + .orWhere(new Brackets(qb => { + qb + .where('note.renoteId = :noteId', { noteId: ps.noteId }) + .andWhere(new Brackets(qb => { + qb + .where('note.text IS NOT NULL') + .orWhere('note.fileIds != \'{}\'') + .orWhere('note.hasPoll = TRUE'); + })); })); - })); })) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 6b46015f7a..e2b786c49e 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -6,9 +6,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; +import { FeaturedService } from '@/core/FeaturedService.js'; export const meta = { tags: ['notes'], @@ -32,7 +32,7 @@ export const paramDef = { type: 'object', properties: { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - offset: { type: 'integer', default: 0 }, + untilId: { type: 'string', format: 'misskey:id' }, channelId: { type: 'string', nullable: true, format: 'misskey:id' }, }, required: [], @@ -40,41 +40,53 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export + private globalNotesRankingCache: string[] = []; + private globalNotesRankingCacheLastFetchedAt = 0; + constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, - private queryService: QueryService, + private featuredService: FeaturedService, ) { super(meta, paramDef, async (ps, me) => { - const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで + let noteIds: string[]; + if (ps.channelId) { + noteIds = await this.featuredService.getInChannelNotesRanking(ps.channelId, 50); + } else { + if (this.globalNotesRankingCacheLastFetchedAt !== 0 && (Date.now() - this.globalNotesRankingCacheLastFetchedAt < 1000 * 60 * 30)) { + noteIds = this.globalNotesRankingCache; + } else { + noteIds = await this.featuredService.getGlobalNotesRanking(100); + this.globalNotesRankingCache = noteIds; + this.globalNotesRankingCacheLastFetchedAt = Date.now(); + } + } + + if (noteIds.length === 0) { + return []; + } + + noteIds.sort((a, b) => a > b ? -1 : 1); + if (ps.untilId) { + noteIds = noteIds.filter(id => id < ps.untilId!); + } + noteIds = noteIds.slice(0, ps.limit); const query = this.notesRepository.createQueryBuilder('note') - .addSelect('note.score') - .where('note.userHost IS NULL') - .andWhere('note.score > 0') - .andWhere('note.createdAt > :date', { date: new Date(Date.now() - day) }) - .andWhere('note.visibility = \'public\'') + .where('note.id IN (:...noteIds)', { noteIds: noteIds }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); - if (ps.channelId) query.andWhere('note.channelId = :channelId', { channelId: ps.channelId }); + const notes = await query.getMany(); + notes.sort((a, b) => a.id > b.id ? -1 : 1); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); - - let notes = await query - .orderBy('note.score', 'DESC') - .limit(100) - .getMany(); - - notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); - - notes = notes.slice(ps.offset, ps.offset + ps.limit); + // TODO: ミュート等考慮 return await this.noteEntityService.packMany(notes, me); }); diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 73f0faaf2e..97bd410f90 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -68,8 +68,41 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.gtlDisabled); } - // TODO? - return []; + //#region Construct query + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.visibility = \'public\'') + .andWhere('note.channelId IS NULL') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); + + if (me) { + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + } + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } + //#endregion + + const timeline = await query.limit(ps.limit).getMany(); + + process.nextTick(() => { + if (me) { + this.activeUsersChart.read(me); + } + }); + + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index bf1481705b..6a68c8a310 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -15,6 +15,7 @@ import { RoleService } from '@/core/RoleService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { CacheService } from '@/core/CacheService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -54,6 +55,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, withCats: { type: 'boolean', default: false }, }, required: [], @@ -73,8 +75,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const policies = await this.roleService.getUserPolicies(me.id); if (!policies.ltlAvailable) { throw new ApiError(meta.errors.stlDisabled); @@ -90,30 +96,29 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let timeline: MiNote[] = []; + let noteIds: string[]; - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let htlNoteIdsRes: [string, string[]][] = []; - let ltlNoteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - [htlNoteIdsRes, ltlNoteIdsRes] = await Promise.all([ - this.redisForTimelines.xrevrange( - ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit), - this.redisForTimelines.xrevrange( - ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit), - ]); + if (ps.withFiles) { + const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([ + `homeTimelineWithFiles:${me.id}`, + 'localTimelineWithFiles', + ], untilId, sinceId); + noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); + } else if (ps.withReplies) { + const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.redisTimelineService.getMulti([ + `homeTimeline:${me.id}`, + 'localTimeline', + 'localTimelineWithReplies', + ], untilId, sinceId); + noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds, ...ltlReplyNoteIds])); + } else { + const [htlNoteIds, ltlNoteIds] = await this.redisTimelineService.getMulti([ + `homeTimeline:${me.id}`, + 'localTimeline', + ], untilId, sinceId); + noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); } - const htlNoteIds = htlNoteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); - const ltlNoteIds = ltlNoteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); - let noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); noteIds.sort((a, b) => a > b ? -1 : 1); noteIds = noteIds.slice(0, ps.limit); @@ -134,7 +139,7 @@ export default class extends Endpoint { // eslint- query.andWhere('(select "isCat" from "user" where id = note."userId")'); } - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (note.userId === me.id) { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 3448615da2..f5fdffb6a8 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -15,6 +15,7 @@ import { RoleService } from '@/core/RoleService.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -44,6 +45,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, withCats: { type: 'boolean', default: false }, excludeNsfw: { type: 'boolean', default: false }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, @@ -69,8 +71,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const policies = await this.roleService.getUserPolicies(me ? me.id : null); if (!policies.ltlAvailable) { throw new ApiError(meta.errors.ltlDisabled); @@ -86,20 +92,22 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]) : [new Set(), new Set(), new Set()]; - let timeline: MiNote[] = []; + let noteIds: string[]; - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - noteIdsRes = await this.redisForTimelines.xrevrange( - ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); + if (ps.withFiles) { + noteIds = await this.redisTimelineService.get('localTimelineWithFiles', untilId, sinceId); + } else if (ps.withReplies) { + const [nonReplyNoteIds, replyNoteIds] = await this.redisTimelineService.getMulti([ + 'localTimeline', + 'localTimelineWithReplies', + ], untilId, sinceId); + noteIds = Array.from(new Set([...nonReplyNoteIds, ...replyNoteIds])); + noteIds.sort((a, b) => a > b ? -1 : 1); + } else { + noteIds = await this.redisTimelineService.get('localTimeline', untilId, sinceId); } - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -118,7 +126,7 @@ export default class extends Endpoint { // eslint- query.andWhere('(select "isCat" from "user" where id = note."userId")'); } - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (me && (note.userId === me.id)) { @@ -136,6 +144,8 @@ export default class extends Endpoint { // eslint- return true; }); + // TODO: フィルタした結果件数が足りなかった場合の対応 + timeline.sort((a, b) => a.id > b.id ? -1 : 1); process.nextTick(() => { diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 2fdcdc35c6..f932952860 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -59,9 +59,10 @@ export default class extends Endpoint { // eslint- .where('following.followerId = :followerId', { followerId: me.id }); const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where(`'{"${me.id}"}' <@ note.mentions`) - .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); + .andWhere(new Brackets(qb => { + qb + .where(`'{"${me.id}"}' <@ note.mentions`) + .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); })) // Avoid scanning primary key index .orderBy('CONCAT(note.id)', 'DESC') diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 7f1fc8545a..66ab9ecacc 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -57,9 +57,10 @@ export default class extends Endpoint { // eslint- .where('poll.userHost IS NULL') .andWhere('poll.userId != :meId', { meId: me.id }) .andWhere('poll.noteVisibility = \'public\'') - .andWhere(new Brackets(qb => { qb - .where('poll.expiresAt IS NULL') - .orWhere('poll.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('poll.expiresAt IS NULL') + .orWhere('poll.expiresAt > :now', { now: new Date() }); })); //#region exclude arleady voted polls diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 6bcd16f21a..d9f11b60c0 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; export const meta = { tags: ['notes'], @@ -63,8 +64,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const [ followings, userIdsWhoMeMuting, @@ -77,20 +82,8 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - noteIdsRes = await this.redisForTimelines.xrevrange( - ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - } - - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + let noteIds = await this.redisTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -109,7 +102,7 @@ export default class extends Endpoint { // eslint- query.andWhere('(select "isCat" from "user" where id = note."userId")'); } - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (note.userId === me.id) { diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 98f726831a..e72dad9779 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js'; import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -80,8 +81,12 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private cacheService: CacheService, private idService: IdService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const list = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, @@ -101,20 +106,8 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - noteIdsRes = await this.redisForTimelines.xrevrange( - ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - } - - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + let noteIds = await this.redisTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -133,7 +126,7 @@ export default class extends Endpoint { // eslint- query.andWhere('(select "isCat" from "user" where id = note."userId")'); } - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (note.userId === me.id) { diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 522cd58334..b8cd93e660 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -11,6 +11,7 @@ import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -65,8 +66,12 @@ export default class extends Endpoint { // eslint- private idService: IdService, private noteEntityService: NoteEntityService, private queryService: QueryService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const role = await this.rolesRepository.findOneBy({ id: ps.roleId, isPublic: true, @@ -78,18 +83,9 @@ export default class extends Endpoint { // eslint- if (!role.isExplorable) { return []; } - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - const noteIdsRes = await this.redisForTimelines.xrevrange( - `roleTimeline:${role.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - if (noteIdsRes.length === 0) { - return []; - } - - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + let noteIds = await this.redisTimelineService.get(`roleTimeline:${role.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts index 5763b25465..04d24c552d 100644 --- a/packages/backend/src/server/api/endpoints/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/roles/users.ts @@ -62,9 +62,10 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.roleAssignmentsRepository.createQueryBuilder('assign'), ps.sinceId, ps.untilId) .andWhere('assign.roleId = :roleId', { roleId: role.id }) - .andWhere(new Brackets(qb => { qb - .where('assign.expiresAt IS NULL') - .orWhere('assign.expiresAt > :now', { now: new Date() }); + .andWhere(new Brackets(qb => { + qb + .where('assign.expiresAt IS NULL') + .orWhere('assign.expiresAt > :now', { now: new Date() }); })) .innerJoinAndSelect('assign.user', 'user'); diff --git a/packages/backend/src/server/api/endpoints/users/featured-notes.ts b/packages/backend/src/server/api/endpoints/users/featured-notes.ts new file mode 100644 index 0000000000..4779b065fa --- /dev/null +++ b/packages/backend/src/server/api/endpoints/users/featured-notes.ts @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import type { NotesRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { FeaturedService } from '@/core/FeaturedService.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: false, + allowGet: true, + cacheSec: 3600, + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + untilId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private noteEntityService: NoteEntityService, + private featuredService: FeaturedService, + ) { + super(meta, paramDef, async (ps, me) => { + let noteIds = await this.featuredService.getPerUserNotesRanking(ps.userId, 50); + + noteIds.sort((a, b) => a > b ? -1 : 1); + if (ps.untilId) { + noteIds = noteIds.filter(id => id < ps.untilId!); + } + noteIds = noteIds.slice(0, ps.limit); + + if (noteIds.length === 0) { + return []; + } + + const query = this.notesRepository.createQueryBuilder('note') + .where('note.id IN (:...noteIds)', { noteIds: noteIds }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); + + const notes = await query.getMany(); + notes.sort((a, b) => a.id > b.id ? -1 : 1); + + // TODO: ミュート等考慮 + + return await this.noteEntityService.packMany(notes, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 2f5fa31052..1c12931046 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -10,10 +10,11 @@ import type { MiNote, NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; -import { GetterService } from '@/server/api/GetterService.js'; import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { QueryService } from '@/core/QueryService.js'; +import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -68,94 +69,122 @@ export default class extends Endpoint { // eslint- private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, - private getterService: GetterService, + private queryService: QueryService, private cacheService: CacheService, private idService: IdService, + private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const [ - userIdsWhoMeMuting, - ] = me ? await Promise.all([ - this.cacheService.userMutingsCache.fetch(me.id), - ]) : [new Set()]; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const isRangeSpecified = untilId != null && sinceId != null; + const isSelf = me && (me.id === ps.userId); - let timeline: MiNote[] = []; + if (isRangeSpecified || sinceId == null) { + const [ + userIdsWhoMeMuting, + ] = me ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(me.id), + ]) : [new Set()]; - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - let repliesNoteIdsRes: [string, string[]][] = []; - let channelNoteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([ - this.redisForTimelines.xrevrange( - ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit), - ps.withReplies - ? this.redisForTimelines.xrevrange( - `userTimelineWithReplies:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit) - : Promise.resolve([]), - ps.withChannelNotes - ? this.redisForTimelines.xrevrange( - `userTimelineWithChannel:${ps.userId}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit) - : Promise.resolve([]), + const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([ + this.redisTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId), + ps.withReplies ? this.redisTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), + ps.withChannelNotes ? this.redisTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), ]); + + let noteIds = Array.from(new Set([ + ...noteIdsRes, + ...repliesNoteIdsRes, + ...channelNoteIdsRes, + ])); + noteIds.sort((a, b) => a > b ? -1 : 1); + noteIds = noteIds.slice(0, ps.limit); + + if (noteIds.length > 0) { + const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId); + + const query = this.notesRepository.createQueryBuilder('note') + .where('note.id IN (:...noteIds)', { noteIds: noteIds }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('note.channel', 'channel'); + + if (ps.withCats) { + query.andWhere('(select "isCat" from "user" where id = note."userId")'); + } + + let timeline = await query.getMany(); + + timeline = timeline.filter(note => { + if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false; + + if (note.renoteId) { + if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) { + if (ps.withRenotes === false) return false; + } + } + + if (note.channel?.isSensitive && !isSelf) return false; + if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false; + if (note.visibility === 'followers' && !isFollowing && !isSelf) return false; + + return true; + }); + + // TODO: フィルタで件数が減った場合の埋め合わせ処理 + + timeline.sort((a, b) => a.id > b.id ? -1 : 1); + + if (timeline.length > 0) { + return await this.noteEntityService.packMany(timeline, me); + } + } } - let noteIds = Array.from(new Set([ - ...noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId), - ...repliesNoteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId), - ...channelNoteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId), - ])); - noteIds.sort((a, b) => a > b ? -1 : 1); - noteIds = noteIds.slice(0, ps.limit); - - if (noteIds.length === 0) { - return []; - } - - const isFollowing = me ? Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId) : false; - - const query = this.notesRepository.createQueryBuilder('note') - .where('note.id IN (:...noteIds)', { noteIds: noteIds }) + //#region fallback to database + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.userId = :userId', { userId: ps.userId }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('note.channel', 'channel') .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('note.channel', 'channel'); + .leftJoinAndSelect('renote.user', 'renoteUser'); - if (ps.withCats) { - query.andWhere('(select "isCat" from "user" where id = note."userId")'); + if (ps.withChannelNotes) { + if (!isSelf) query.andWhere('channel.isSensitive = false'); + } else { + query.andWhere('note.channelId IS NULL'); } - timeline = await query.getMany(); + this.queryService.generateVisibilityQuery(query, me); + if (me) { + this.queryService.generateMutedUserQuery(query, me, { id: ps.userId }); + this.queryService.generateBlockedUserQuery(query, me); + } - timeline = timeline.filter(note => { - if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false; + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } - if (note.renoteId) { - if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) { - if (ps.withRenotes === false) return false; - } - } + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :userId', { userId: ps.userId }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } - if (note.visibility === 'followers' && !isFollowing) return false; - - return true; - }); - - timeline.sort((a, b) => a.id > b.id ? -1 : 1); + const timeline = await query.limit(ps.limit).getMany(); return await this.noteEntityService.packMany(timeline, me); + //#endregion }); } } diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index b937bf8a41..1874b6d78d 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -92,9 +92,10 @@ export default class extends Endpoint { // eslint- .andWhere(`user.id IN (${ followingQuery.getQuery() })`) .andWhere('user.id != :meId', { meId: me.id }) .andWhere('user.isSuspended = FALSE') - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })); query.setParameters(followingQuery.getParameters()); diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 426e6c706f..d597270e3a 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -64,9 +64,10 @@ export default class extends Endpoint { // eslint- if (isUsername) { const usernameQuery = this.usersRepository.createQueryBuilder('user') .where('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' }) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) .andWhere('user.isSuspended = FALSE'); @@ -91,9 +92,10 @@ export default class extends Endpoint { // eslint- qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' }); } })) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) .andWhere('user.isSuspended = FALSE'); @@ -122,9 +124,10 @@ export default class extends Endpoint { // eslint- const query = this.usersRepository.createQueryBuilder('user') .where(`user.id IN (${ profQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); })) .andWhere('user.isSuspended = FALSE') .setParameters(profQuery.getParameters()); 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 83d4d7164c..fb3da323f5 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -16,9 +16,10 @@ import Channel from '../channel.js'; class GlobalTimelineChannel extends Channel { public readonly chName = 'globalTimeline'; - public static shouldShare = true; + public static shouldShare = false; public static requireCredential = false; private withRenotes: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +39,7 @@ class GlobalTimelineChannel extends Channel { if (!policies.gtlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -45,6 +47,8 @@ class GlobalTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.visibility !== 'public') return; if (note.channelId != null) return; 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 89de8f713c..d7c66608f4 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -14,9 +14,10 @@ import Channel from '../channel.js'; class HomeTimelineChannel extends Channel { public readonly chName = 'homeTimeline'; - public static shouldShare = true; + public static shouldShare = false; public static requireCredential = true; private withRenotes: boolean; + private withFiles: boolean; constructor( private noteEntityService: NoteEntityService, @@ -31,12 +32,15 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { this.withRenotes = params.withRenotes ?? true; + this.withFiles = params.withFiles ?? false; this.subscriber.on('notesStream', this.onNote); } @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.channelId) { if (!this.followingChannels.has(note.channelId)) return; } else { 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 564de364bd..b733b591f2 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -16,9 +16,11 @@ import Channel from '../channel.js'; class HybridTimelineChannel extends Channel { public readonly chName = 'hybridTimeline'; - public static shouldShare = true; + public static shouldShare = false; public static requireCredential = true; private withRenotes: boolean; + private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +40,8 @@ class HybridTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withReplies = params.withReplies ?? false; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -45,6 +49,8 @@ class HybridTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + // チャンネルの投稿ではなく、自分自身の投稿 または // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または // チャンネルの投稿ではなく、全体公開のローカルの投稿 または @@ -83,7 +89,7 @@ class HybridTimelineChannel extends Channel { if (isInstanceMuted(note, new Set(this.userProfile!.mutedInstances ?? []))) return; // 関係ない返信は除外 - if (note.reply && !this.following[note.userId]?.withReplies) { + if (note.reply && !this.following[note.userId]?.withReplies && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; 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 01a9fdd231..3d2893006c 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -15,9 +15,11 @@ import Channel from '../channel.js'; class LocalTimelineChannel extends Channel { public readonly chName = 'localTimeline'; - public static shouldShare = true; + public static shouldShare = false; public static requireCredential = false; private withRenotes: boolean; + private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -37,6 +39,8 @@ class LocalTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withRenotes = params.withRenotes ?? true; + this.withReplies = params.withReplies ?? false; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -44,6 +48,8 @@ class LocalTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.user.host !== null) return; if (note.visibility !== 'public') return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; @@ -62,7 +68,7 @@ class LocalTimelineChannel extends Channel { } // 関係ない返信は除外 - if (note.reply && this.user && !this.following[note.userId]?.withReplies) { + if (note.reply && this.user && !this.following[note.userId]?.withReplies && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return; 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 40745d54d0..0eb25e637f 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -18,8 +18,9 @@ class UserListChannel extends Channel { public static shouldShare = false; public static requireCredential = false; private listId: string; - public membershipsMap: Record | undefined> = {}; + private membershipsMap: Record | undefined> = {}; private listUsersClock: NodeJS.Timeout; + private withFiles: boolean; constructor( private userListsRepository: UserListsRepository, @@ -37,6 +38,7 @@ class UserListChannel extends Channel { @bindThis public async init(params: any) { this.listId = params.listId as string; + this.withFiles = params.withFiles ?? false; // Check existence and owner const listExist = await this.userListsRepository.exist({ @@ -76,6 +78,8 @@ class UserListChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (!Object.hasOwn(this.membershipsMap, note.userId)) return; if (['followers', 'specified'].includes(note.visibility)) { diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts index 05eceb7683..685dcc5e21 100644 --- a/packages/backend/test/e2e/timelines.ts +++ b/packages/backend/test/e2e/timelines.ts @@ -3,6 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +// How to run: +// pnpm jest -- e2e/timelines.ts + process.env.NODE_ENV = 'test'; process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING = 'true'; @@ -197,6 +200,22 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); }); + test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('自分の他人への返信が含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -378,6 +397,104 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); + + test.concurrent('自分の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); + }); + + test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); + }); + + test.concurrent('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + + test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + + test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + const aliceNote = await post(alice, { text: 'ok', visibility: 'specified', visibleUserIds: [bob.id], replyId: bobNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); + }); + + /* TODO + test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok'); + }); + */ + + // ↑の挙動が理想だけど実装が面倒かも + test.concurrent('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] }); + const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); }); describe('Local TL', () => { @@ -395,6 +512,20 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); + test.concurrent('他人の他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/local-timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + }); + test.concurrent('チャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -488,6 +619,37 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); + /* 実装が面倒 + test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/local-timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + */ + + test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/local-timeline', { withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -543,6 +705,36 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + await api('/following/create', { userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/hybrid-timeline', {}, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + + test.concurrent('他人の他人への返信が含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/hybrid-timeline', { }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + }); + test.concurrent('リモートユーザーのノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup({ host: genHost() })]); @@ -583,6 +775,20 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const carolNote = await post(carol, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: carolNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/hybrid-timeline', { withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); + }); + test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -630,7 +836,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); - /* 未実装 test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -645,23 +850,6 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); }); - */ - - test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれるが隠される', async () => { - const [alice, bob] = await Promise.all([signup(), signup()]); - - const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); - await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); - await sleep(1000); - const bobNote = await post(bob, { text: 'hi', visibility: 'followers' }); - - await waitForPushToTl(); - - const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); - - assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); - assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, null); - }); test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); @@ -696,6 +884,22 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); }); + test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await sleep(1000); + const aliceNote = await post(alice, { text: 'hi' }); + const bobNote = await post(bob, { text: 'hi', replyId: aliceNote.id }); + + await waitForPushToTl(); + + const res = await api('/notes/user-list-timeline', { listId: list.id, withReplies: false }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); @@ -746,6 +950,22 @@ describe('Timelines', () => { assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); }); + test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body); + const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -762,6 +982,38 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); }, 1000 * 10); + + test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); + }); + + test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => { + const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); + + const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body); + await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice); + await api('/users/lists/push', { listId: list.id, userId: carol.id }, alice); + await sleep(1000); + const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [carol.id] }); + + await waitForPushToTl(); + + const res = await api('/notes/user-list-timeline', { listId: list.id }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); }); describe('User TL', () => { @@ -804,6 +1056,19 @@ describe('Timelines', () => { assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); }); + test.concurrent('自身の visibility: followers なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'followers' }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: alice.id }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); + }); + test.concurrent('チャンネル投稿が含まれない', async () => { const [alice, bob] = await Promise.all([signup(), signup()]); @@ -890,6 +1155,32 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); }); + test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); + + test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => { + const [bob] = await Promise.all([signup()]); + + const channel = await api('/channels/create', { name: 'channel', isSensitive: true }, bob).then(x => x.body); + const bobNote = await post(bob, { text: 'hi', channelId: channel.id }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: bob.id, withChannelNotes: true }, bob); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); + }); + test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => { const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]); @@ -922,6 +1213,30 @@ describe('Timelines', () => { assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some((note: any) => note.id === bobNote3.id), true); }); + + test.concurrent('自身の visibility: specified なノートが含まれる', async () => { + const [alice] = await Promise.all([signup()]); + + const aliceNote = await post(alice, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: alice.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); + }); + + test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => { + const [alice, bob] = await Promise.all([signup(), signup()]); + + const bobNote = await post(bob, { text: 'hi', visibility: 'specified' }); + + await waitForPushToTl(); + + const res = await api('/users/notes', { userId: bob.id, withReplies: true }, alice); + + assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); + }); }); // TODO: リノートミュート済みユーザーのテスト diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 4b7a2f201f..d03f7af9eb 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -301,12 +301,14 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO }; export const uploadUrl = async (user: UserToken, url: string) => { - let file: any; + let resolve: unknown; + const file = new Promise(ok => resolve = ok); const marker = Math.random().toString(); const ws = await connectStream(user, 'main', (msg) => { if (msg.type === 'urlUploadFinished' && msg.body.marker === marker) { - file = msg.body.file; + ws.close(); + resolve(msg.body.file); } }); @@ -316,9 +318,6 @@ export const uploadUrl = async (user: UserToken, url: string) => { force: true, }, user); - await sleep(7000); - ws.close(); - return file; }; @@ -458,6 +457,7 @@ export async function testPaginationConsistency id + ':' + createdAt), expected.map(({ id, createdAt }) => id + ':' + createdAt)); } + */ // 3. untilId指定+limitで取得してつなぎ合わせた結果が期待通りになっていること if (ordering === 'desc') { diff --git a/packages/cherrypick-js/etc/cherrypick-js.api.md b/packages/cherrypick-js/etc/cherrypick-js.api.md index cbc5aa42bb..396ce5e449 100644 --- a/packages/cherrypick-js/etc/cherrypick-js.api.md +++ b/packages/cherrypick-js/etc/cherrypick-js.api.md @@ -2465,6 +2465,8 @@ type LiteInstanceMetadata = { tosUrl: string | null; repositoryUrl: string; feedbackUrl: string; + impressumUrl: string | null; + privacyPolicyUrl: string | null; disableRegistration: boolean; disableLocalTimeline: boolean; disableGlobalTimeline: boolean; @@ -2503,6 +2505,7 @@ type LiteInstanceMetadata = { url: string; imageUrl: string; }[]; + notesPerOneAd: number; translatorAvailable: boolean; serverRules: string[]; }; @@ -2817,6 +2820,9 @@ type Notification_2 = { invitation: UserGroup; user: User; userId: User['id']; +} | { + type: 'achievementEarned'; + achievement: string; } | { type: 'app'; header?: string | null; @@ -2827,7 +2833,7 @@ type Notification_2 = { }); // @public (undocumented) -export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app"]; +export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "achievementEarned"]; // @public (undocumented) type OriginType = 'combined' | 'local' | 'remote'; @@ -3043,7 +3049,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:658:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:107:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:603:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:609:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/cherrypick-js/package.json b/packages/cherrypick-js/package.json index 7436a7c56a..c663be961f 100644 --- a/packages/cherrypick-js/package.json +++ b/packages/cherrypick-js/package.json @@ -20,13 +20,13 @@ "url": "git+https://github.com/misskey-dev/misskey.js.git" }, "devDependencies": { - "@microsoft/api-extractor": "7.37.2", + "@microsoft/api-extractor": "7.38.0", "@swc/jest": "0.2.29", "@types/jest": "29.5.5", - "@types/node": "20.7.1", - "@typescript-eslint/eslint-plugin": "6.7.3", - "@typescript-eslint/parser": "6.7.3", - "eslint": "8.50.0", + "@types/node": "20.8.4", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", + "eslint": "8.51.0", "jest": "29.7.0", "jest-fetch-mock": "3.0.3", "jest-websocket-mock": "2.5.0", @@ -39,7 +39,7 @@ ], "dependencies": { "@swc/cli": "0.1.62", - "@swc/core": "1.3.90", + "@swc/core": "1.3.92", "eventemitter3": "5.0.1", "reconnecting-websocket": "4.4.0" } diff --git a/packages/cherrypick-js/src/api.ts b/packages/cherrypick-js/src/api.ts index 974cb35ace..9415e692e3 100644 --- a/packages/cherrypick-js/src/api.ts +++ b/packages/cherrypick-js/src/api.ts @@ -67,8 +67,7 @@ export class APIClient { IsCaseMatched extends true ? GetCaseResult : IsCaseMatched extends true ? GetCaseResult : Endpoints[E]['res']['$switch']['$default'] - : Endpoints[E]['res']> - { + : Endpoints[E]['res']> { const promise = new Promise((resolve, reject) => { this.fetch(`${this.origin}/api/${endpoint}`, { method: 'POST', diff --git a/packages/cherrypick-js/src/consts.ts b/packages/cherrypick-js/src/consts.ts index ccc55537d2..c4ddead823 100644 --- a/packages/cherrypick-js/src/consts.ts +++ b/packages/cherrypick-js/src/consts.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'achievementEarned'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/cherrypick-js/src/entities.ts b/packages/cherrypick-js/src/entities.ts index 29fd12d4a9..da23b90029 100644 --- a/packages/cherrypick-js/src/entities.ts +++ b/packages/cherrypick-js/src/entities.ts @@ -285,6 +285,9 @@ export type Notification = { invitation: UserGroup; user: User; userId: User['id']; +} | { + type: 'achievementEarned'; + achievement: string; } | { type: 'app'; header?: string | null; @@ -331,6 +334,8 @@ export type LiteInstanceMetadata = { tosUrl: string | null; repositoryUrl: string; feedbackUrl: string; + impressumUrl: string | null; + privacyPolicyUrl: string | null; disableRegistration: boolean; disableLocalTimeline: boolean; disableGlobalTimeline: boolean; @@ -369,6 +374,7 @@ export type LiteInstanceMetadata = { url: string; imageUrl: string; }[]; + notesPerOneAd: number; translatorAvailable: boolean; serverRules: string[]; }; diff --git a/packages/frontend/package.json b/packages/frontend/package.json index be61a4f6ae..0989bbad76 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -19,13 +19,13 @@ "@discordapp/twemoji": "14.1.2", "@fontsource/jetbrains-mono": "^5.0.12", "@github/webauthn-json": "2.1.1", - "@rollup/plugin-alias": "5.0.0", - "@rollup/plugin-json": "6.0.0", - "@rollup/plugin-replace": "5.0.2", - "@rollup/pluginutils": "5.0.4", + "@rollup/plugin-alias": "5.0.1", + "@rollup/plugin-json": "6.0.1", + "@rollup/plugin-replace": "5.0.3", + "@rollup/pluginutils": "5.0.5", "@syuilo/aiscript": "0.16.0", "@tabler/icons-webfont": "2.37.0", - "@vitejs/plugin-vue": "4.3.4", + "@vitejs/plugin-vue": "4.4.0", "@vue-macros/reactivity-transform": "0.3.23", "@vue/compiler-sfc": "3.3.4", "astring": "1.8.6", @@ -41,7 +41,7 @@ "chartjs-plugin-zoom": "2.0.1", "cherrypick-js": "workspace:*", "cherrypick-mfm-js": "github:kokonect-link/mfm.js", - "chromatic": "7.2.0", + "chromatic": "7.2.3", "compare-versions": "6.1.0", "cropperjs": "2.0.0-beta.4", "date-fns": "2.30.0", @@ -54,15 +54,15 @@ "is-file-animated": "1.0.2", "json5": "2.2.3", "matter-js": "0.19.0", - "photoswipe": "5.4.1", + "photoswipe": "5.4.2", "pretendard": "^1.3.8", "pretendard-jp": "^1.3.8", "prismjs": "1.29.0", "punycode": "2.3.0", "querystring": "0.2.1", - "rollup": "3.29.4", + "rollup": "4.0.2", "sanitize-html": "2.11.0", - "sass": "1.68.0", + "sass": "1.69.1", "strict-event-emitter-types": "2.0.0", "temml": "0.10.13", "textarea-caret": "3.1.0", @@ -76,73 +76,73 @@ "uuid": "9.0.1", "v-code-diff": "1.7.1", "vanilla-tilt": "1.8.1", - "vite": "4.4.9", + "vite": "4.4.11", "vue": "3.3.4", "vue-prism-editor": "2.0.0-alpha.2", "vuedraggable": "next" }, "devDependencies": { - "@storybook/addon-actions": "7.4.5", - "@storybook/addon-essentials": "7.4.5", - "@storybook/addon-interactions": "7.4.5", - "@storybook/addon-links": "7.4.5", - "@storybook/addon-storysource": "7.4.5", - "@storybook/addons": "7.4.5", - "@storybook/blocks": "7.4.5", - "@storybook/core-events": "7.4.5", - "@storybook/jest": "0.2.2", - "@storybook/manager-api": "7.4.5", - "@storybook/preview-api": "7.4.5", - "@storybook/react": "7.4.5", - "@storybook/react-vite": "7.4.5", - "@storybook/testing-library": "0.2.1", - "@storybook/theming": "7.4.5", - "@storybook/types": "7.4.5", - "@storybook/vue3": "7.4.5", - "@storybook/vue3-vite": "7.4.5", + "@storybook/addon-actions": "7.4.6", + "@storybook/addon-essentials": "7.4.6", + "@storybook/addon-interactions": "7.4.6", + "@storybook/addon-links": "7.4.6", + "@storybook/addon-storysource": "7.4.6", + "@storybook/addons": "7.4.6", + "@storybook/blocks": "7.4.6", + "@storybook/core-events": "7.4.6", + "@storybook/jest": "0.2.3", + "@storybook/manager-api": "7.4.6", + "@storybook/preview-api": "7.4.6", + "@storybook/react": "7.4.6", + "@storybook/react-vite": "7.4.6", + "@storybook/testing-library": "0.2.2", + "@storybook/theming": "7.4.6", + "@storybook/types": "7.4.6", + "@storybook/vue3": "7.4.6", + "@storybook/vue3-vite": "7.4.6", "@testing-library/vue": "7.0.0", "@types/autosize": "^4.0.1", "@types/escape-regexp": "0.0.1", "@types/estree": "1.0.2", "@types/matter-js": "0.19.1", "@types/micromatch": "4.0.3", - "@types/node": "20.7.1", + "@types/node": "20.8.4", "@types/prismjs": "^1.26.0", "@types/punycode": "2.1.0", "@types/sanitize-html": "2.9.1", "@types/throttle-debounce": "5.0.0", "@types/tinycolor2": "1.4.4", - "@types/uuid": "9.0.4", + "@types/uuid": "9.0.5", "@types/websocket": "1.0.7", "@types/ws": "8.5.6", - "@typescript-eslint/eslint-plugin": "6.7.3", - "@typescript-eslint/parser": "6.7.3", - "@vitest/coverage-v8": "0.34.5", + "@typescript-eslint/eslint-plugin": "6.7.5", + "@typescript-eslint/parser": "6.7.5", + "@vitest/coverage-v8": "0.34.6", "@vue/runtime-core": "3.3.4", "acorn": "8.10.0", "cross-env": "7.0.3", "cypress": "13.3.0", - "eslint": "8.50.0", + "eslint": "8.51.0", "eslint-plugin-import": "2.28.1", "eslint-plugin-storybook": "^0.6.13", "eslint-plugin-vue": "9.17.0", "fast-glob": "3.3.1", "happy-dom": "10.0.3", "micromatch": "4.0.5", - "msw": "1.3.1", + "msw": "1.3.2", "msw-storybook-addon": "1.8.0", "nodemon": "3.0.1", "prettier": "3.0.3", "react": "18.2.0", "react-dom": "18.2.0", "start-server-and-test": "2.0.1", - "storybook": "7.4.5", + "storybook": "7.4.6", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "summaly": "github:misskey-dev/summaly", "vite-plugin-turbosnap": "1.0.3", - "vitest": "0.34.5", + "vitest": "0.34.6", "vitest-fetch-mock": "0.2.2", - "vue-eslint-parser": "9.3.1", - "vue-tsc": "1.8.15" + "vue-eslint-parser": "9.3.2", + "vue-tsc": "1.8.18" } } diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue index e168f33efb..c32a99e193 100644 --- a/packages/frontend/src/components/MkCwButton.vue +++ b/packages/frontend/src/components/MkCwButton.vue @@ -4,10 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/drive.file.notes.vue b/packages/frontend/src/pages/drive.file.notes.vue new file mode 100644 index 0000000000..b66008b479 --- /dev/null +++ b/packages/frontend/src/pages/drive.file.notes.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/packages/frontend/src/pages/drive.file.vue b/packages/frontend/src/pages/drive.file.vue new file mode 100644 index 0000000000..a95b802959 --- /dev/null +++ b/packages/frontend/src/pages/drive.file.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue index 26cb44aab6..40529083f1 100644 --- a/packages/frontend/src/pages/explore.featured.vue +++ b/packages/frontend/src/pages/explore.featured.vue @@ -22,7 +22,6 @@ import { i18n } from '@/i18n.js'; const paginationForNotes = { endpoint: 'notes/featured' as const, limit: 10, - offsetMode: true, }; const paginationForPolls = { diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index 5024fd3128..1afab64686 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -286,8 +286,7 @@ definePageMetadata(computed(() => { let title = i18n.ts._pages.newPage; if (props.initPageId) { title = i18n.ts._pages.editPage; - } - else if (props.initPageName && props.initUser) { + } else if (props.initPageName && props.initUser) { title = i18n.ts._pages.readPage; } return { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 5d9ff7da27..4a5de9eea5 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -33,10 +33,11 @@ SPDX-License-Identifier: AGPL-3.0-only
($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); const withRenotes = $ref(true); +const withReplies = $ref(false); const onlyFiles = $ref(false); const onlyCats = $ref(false); const friendlyEnableNotifications = computed(defaultStore.makeGetterSetter('friendlyEnableNotifications')); @@ -222,7 +224,11 @@ const headerActions = $computed(() => [{ text: i18n.ts.showRenotes, icon: 'ti ti-repeat', ref: $$(withRenotes), - }, { + }, src === 'local' || src === 'social' ? { + type: 'switch', + text: i18n.ts.showRepliesToOthersInTimeline, + ref: $$(withReplies), + } : undefined, { type: 'switch', text: i18n.ts.fileAttachedOnly, icon: 'ti ti-photo', diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 957869ac5c..77786a04d8 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -61,20 +61,7 @@ function settings() { router.push(`/my/lists/${props.listId}`); } -async function timetravel() { - const { canceled, result: date } = await os.inputDate({ - title: i18n.ts.date, - }); - if (canceled) return; - - tlEl.timetravel(date); -} - const headerActions = $computed(() => list ? [{ - icon: 'ti ti-calendar-time', - text: i18n.ts.jumpToSpecifiedDate, - handler: timetravel, -}, { icon: 'ti ti-settings', text: i18n.ts.settings, handler: settings, diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 1e8c32c57f..6de5838234 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -148,7 +148,10 @@ SPDX-License-Identifier: AGPL-3.0-only - +
+
{{ i18n.ts.featured }}
+ +
@@ -233,7 +236,7 @@ watch($$(moderationNote), async () => { }); const pagination = { - endpoint: 'users/notes' as const, + endpoint: 'users/featured-notes' as const, limit: 10, params: computed(() => ({ userId: props.user.id, diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue index 09acf4cb5c..010c34bc6b 100644 --- a/packages/frontend/src/pages/user/index.files.vue +++ b/packages/frontend/src/pages/user/index.files.vue @@ -10,23 +10,26 @@ SPDX-License-Identifier: AGPL-3.0-only
- - - - +

{{ i18n.ts.nothing }}

@@ -41,6 +44,8 @@ import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; import MkContainer from '@/components/MkContainer.vue'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; +import MkA from '@/components/global/MkA.vue'; +import MkLoading from '@/components/global/MkLoading.vue'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -53,6 +58,7 @@ let files = $ref<{ note: Misskey.entities.Note; file: Misskey.entities.DriveFile; }[]>([]); +let showingFiles = $ref([]); let playAnimation = $ref(true); if (defaultStore.state.showingAnimatedImages === 'interaction') playAnimation = false; @@ -125,4 +131,9 @@ onUnmounted(() => { padding: 16px; text-align: center; } + +.sensitive { + display: grid; + place-items: center; +} diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 382088b770..fa5ebc2508 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -500,6 +500,10 @@ export const routes = [{ path: '/my/drive', component: page(() => import('./pages/drive.vue')), loginRequired: true, +}, { + path: '/my/drive/file/:fileId', + component: page(() => import('./pages/drive.file.vue')), + loginRequired: true, }, { path: '/my/follow-requests', component: page(() => import('./pages/follow-requests.vue')), diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts index 731b88859d..587e170ae8 100644 --- a/packages/frontend/src/scripts/get-drive-file-menu.ts +++ b/packages/frontend/src/scripts/get-drive-file-menu.ts @@ -27,7 +27,7 @@ function rename(file: Misskey.entities.DriveFile) { function describe(file: Misskey.entities.DriveFile) { os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { - default: file.comment != null ? file.comment : '', + default: file.comment ?? '', file: file, }, { done: caption => { @@ -112,6 +112,11 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss text: i18n.ts.download, icon: 'ti ti-download', download: file.name, + }, null, { + type: 'link', + to: `/my/drive/file/${file.id}`, + text: i18n.ts._fileViewer.title, + icon: 'ti ti-file', }, null, { text: i18n.ts.delete, icon: 'ti ti-trash', diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index faa681c85b..ef3e206b78 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -7,10 +7,6 @@ import { markRaw } from 'vue'; import { Storage } from '@/pizzax.js'; export const soundConfigStore = markRaw(new Storage('sound', { - mediaVolume: { - where: 'device', - default: 0.5, - }, sound_masterVolume: { where: 'device', default: 0.3, diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts index 9986ab8f12..f07fcd1a30 100644 --- a/packages/frontend/src/scripts/theme.ts +++ b/packages/frontend/src/scripts/theme.ts @@ -5,7 +5,11 @@ import { ref } from 'vue'; import tinycolor from 'tinycolor2'; +import { deepClone } from './clone.js'; import { globalEvents } from '@/events.js'; +import lightTheme from '@/themes/_light.json5'; +import darkTheme from '@/themes/_dark.json5'; +import { miLocalStorage } from '@/local-storage.js'; export type Theme = { id: string; @@ -16,11 +20,6 @@ export type Theme = { props: Record; }; -import lightTheme from '@/themes/_light.json5'; -import darkTheme from '@/themes/_dark.json5'; -import { deepClone } from './clone'; -import { miLocalStorage } from '@/local-storage.js'; - export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); export const getBuiltinThemes = () => Promise.all( @@ -115,18 +114,11 @@ export function applyTheme(theme: Theme, persist = true) { function compile(theme: Theme): Record { function getColor(val: string): tinycolor.Instance { - // ref (prop) - if (val[0] === '@') { + if (val[0] === '@') { // ref (prop) return getColor(theme.props[val.substring(1)]); - } - - // ref (const) - else if (val[0] === '$') { + } else if (val[0] === '$') { // ref (const) return getColor(theme.props[val]); - } - - // func - else if (val[0] === ':') { + } else if (val[0] === ':') { // func const parts = val.split('<'); const func = parts.shift().substring(1); const arg = parseFloat(parts.shift()); diff --git a/packages/frontend/src/themes/_dark.json5 b/packages/frontend/src/themes/_dark.json5 index ae3b0f3870..6106e5492f 100644 --- a/packages/frontend/src/themes/_dark.json5 +++ b/packages/frontend/src/themes/_dark.json5 @@ -56,9 +56,6 @@ infoWarnBg: '#42321c', infoWarnFg: '#ffbd3e', switchBg: 'rgba(255, 255, 255, 0.15)', - cwBg: '#687390', - cwFg: '#393f4f', - cwHoverBg: '#707b97', buttonBg: 'rgba(255, 255, 255, 0.05)', buttonHoverBg: 'rgba(255, 255, 255, 0.1)', buttonGradateA: '@accent', diff --git a/packages/frontend/src/themes/_light.json5 b/packages/frontend/src/themes/_light.json5 index 217eb6fe09..1ca9bb51de 100644 --- a/packages/frontend/src/themes/_light.json5 +++ b/packages/frontend/src/themes/_light.json5 @@ -56,9 +56,6 @@ infoWarnBg: '#fff0db', infoWarnFg: '#8f6e31', switchBg: 'rgba(0, 0, 0, 0.15)', - cwBg: '#b1b9c1', - cwFg: '#fff', - cwHoverBg: '#bbc4ce', buttonBg: 'rgba(0, 0, 0, 0.05)', buttonHoverBg: 'rgba(0, 0, 0, 0.1)', buttonGradateA: '@accent', diff --git a/packages/frontend/src/themes/d-astro.json5 b/packages/frontend/src/themes/d-astro.json5 index e3c3e806cc..7bb1c708c4 100644 --- a/packages/frontend/src/themes/d-astro.json5 +++ b/packages/frontend/src/themes/d-astro.json5 @@ -6,8 +6,6 @@ props: { bg: '#232125', fg: '#efdab9', - cwBg: '#687390', - cwFg: '#393f4f', link: '#78b0a0', warn: '#ecb637', badge: '#afd2ff', @@ -29,7 +27,6 @@ success: '#86b300', buttonBg: 'rgba(255, 255, 255, 0.05)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#707b97', indicator: '@accent', mentionMe: '#fb5d38', messageBg: '@bg', diff --git a/packages/frontend/src/themes/d-byeolvit-noctiluca.json5 b/packages/frontend/src/themes/d-byeolvit-noctiluca.json5 index 7c1e3f0db7..e3d0e69d47 100644 --- a/packages/frontend/src/themes/d-byeolvit-noctiluca.json5 +++ b/packages/frontend/src/themes/d-byeolvit-noctiluca.json5 @@ -22,8 +22,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: ':alpha<0.06<@accent', - cwFg: ':alpha<0.7<@renote', link: ':lighten<15<@accent', warn: '@infoWarnFg', badge: '@infoFg', @@ -49,7 +47,6 @@ switchBg: 'rgba(255, 255, 255, 0.15)', MessageBg: '@bg', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: ':alpha<0.1<@accent', fgOnWhite: '@bg', indicator: '@fg', mentionMe: '@mention', diff --git a/packages/frontend/src/themes/d-qdon.json5 b/packages/frontend/src/themes/d-qdon.json5 index 224bfac763..589ff3ce25 100644 --- a/packages/frontend/src/themes/d-qdon.json5 +++ b/packages/frontend/src/themes/d-qdon.json5 @@ -32,9 +32,6 @@ hashtag: '#87ceeb', mention: '$mstdnHyperLink', renote: '$mstdnHyperLink', - cwBg: '#5e6781', - cwFg: '$mstdnUI', - cwHoverBg: ':lighten<5<@cwBg', buttonBg: '#393f4f', buttonHoverBg: '#242731', quote: '$mstdnHyperLink', diff --git a/packages/frontend/src/themes/d-rosepine.json5 b/packages/frontend/src/themes/d-rosepine.json5 index 2446f7a99d..55d9a2df47 100644 --- a/packages/frontend/src/themes/d-rosepine.json5 +++ b/packages/frontend/src/themes/d-rosepine.json5 @@ -22,8 +22,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: '#1f1d2e', - cwFg: '#f6c177', link: '#9ccfd8', warn: '#f6c177', badge: '#ebbcba', @@ -47,7 +45,6 @@ buttonBg: 'rgba(255, 255, 255, 0.05)', switchBg: 'rgba(255, 255, 255, 0.15)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#26233a', indicator: '@accent', mentionMe: '@mention', messageBg: '@bg', diff --git a/packages/frontend/src/themes/d-rosepinemoon.json5 b/packages/frontend/src/themes/d-rosepinemoon.json5 index 2e06050b56..5f24af2355 100644 --- a/packages/frontend/src/themes/d-rosepinemoon.json5 +++ b/packages/frontend/src/themes/d-rosepinemoon.json5 @@ -22,8 +22,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: '#393552', - cwFg: '#f6c177', link: '#3e8fb0', warn: '#f6c177', badge: '#ea9a97', @@ -47,7 +45,6 @@ buttonBg: 'rgba(255, 255, 255, 0.05)', switchBg: 'rgba(255, 255, 255, 0.15)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#393552', indicator: '@accent', mentionMe: '@mention', messageBg: '@bg', diff --git a/packages/frontend/src/themes/d-scone-color.json5 b/packages/frontend/src/themes/d-scone-color.json5 index 2584f83f66..81aba1f989 100644 --- a/packages/frontend/src/themes/d-scone-color.json5 +++ b/packages/frontend/src/themes/d-scone-color.json5 @@ -22,8 +22,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: '#fae7a1', - cwFg: '#3a312e', link: '#eedb97', warn: '#ecb637', badge: '#fadda1', @@ -47,7 +45,6 @@ buttonBg: 'rgba(255, 255, 255, 0.05)', switchBg: 'rgba(255, 255, 255, 0.15)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#f5af59', indicator: '@accent', mentionMe: '#8d86ff', messageBg: '@bg', diff --git a/packages/frontend/src/themes/d-u0.json5 b/packages/frontend/src/themes/d-u0.json5 index 6e5199161e..c66c536777 100644 --- a/packages/frontend/src/themes/d-u0.json5 +++ b/packages/frontend/src/themes/d-u0.json5 @@ -21,8 +21,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: '#687390', - cwFg: '#393f4f', link: '@accent', warn: '#ecb637', badge: '#afd2ff', @@ -46,7 +44,6 @@ buttonBg: 'rgba(255, 255, 255, 0.05)', switchBg: 'rgba(255, 255, 255, 0.15)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#707b97', indicator: '@accent', mentionMe: '@mention', messageBg: '@bg', diff --git a/packages/frontend/src/themes/l-byeolvit-polaris.json5 b/packages/frontend/src/themes/l-byeolvit-polaris.json5 index eaeb382ce5..6839649a80 100644 --- a/packages/frontend/src/themes/l-byeolvit-polaris.json5 +++ b/packages/frontend/src/themes/l-byeolvit-polaris.json5 @@ -22,8 +22,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: ':alpha<0.06<@accent', - cwFg: ':alpha<0.7<@renote', link: ':lighten<2.5<@accent', warn: '@infoWarnFg', badge: '@infoFg', @@ -49,7 +47,6 @@ switchBg: 'rgba(0, 0, 0, 0.15)', MessageBg: '@bg', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: ':alpha<0.1<@accent', fgOnWhite: '@bg', indicator: '@fg', mentionMe: '@mention', diff --git a/packages/frontend/src/themes/l-rosepinedawn.json5 b/packages/frontend/src/themes/l-rosepinedawn.json5 index d19eb5925c..7a9493cc20 100644 --- a/packages/frontend/src/themes/l-rosepinedawn.json5 +++ b/packages/frontend/src/themes/l-rosepinedawn.json5 @@ -47,9 +47,6 @@ infoWarnBg: '#f2e9e1', infoWarnFg: '#b4637a', switchBg: 'rgba(0, 0, 0, 0.15)', - cwBg: '#b4637a', - cwFg: '#faf4ed', - cwHoverBg: '#d7827e', buttonBg: 'rgba(0, 0, 0, 0.05)', buttonHoverBg: 'rgba(0, 0, 0, 0.1)', buttonGradateA: '#d7827e', diff --git a/packages/frontend/src/themes/l-scone-color.json5 b/packages/frontend/src/themes/l-scone-color.json5 index d8134b9dae..862515edb3 100644 --- a/packages/frontend/src/themes/l-scone-color.json5 +++ b/packages/frontend/src/themes/l-scone-color.json5 @@ -21,8 +21,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: '#b1b9c1', - cwFg: '#fff', link: '#44a4c1', warn: '#ecb637', badge: '#31b1ce', @@ -46,7 +44,6 @@ buttonBg: 'rgba(0, 0, 0, 0.05)', switchBg: 'rgba(0, 0, 0, 0.15)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#bbc4ce', indicator: '@accent', mentionMe: '@mention', messageBg: '@bg', diff --git a/packages/frontend/src/themes/l-u0.json5 b/packages/frontend/src/themes/l-u0.json5 index 116dbb4a01..667b7c081e 100644 --- a/packages/frontend/src/themes/l-u0.json5 +++ b/packages/frontend/src/themes/l-u0.json5 @@ -21,8 +21,6 @@ X15: ':alpha<0<@panel', X16: ':alpha<0.7<@panel', X17: ':alpha<0.8<@bg', - cwBg: '#687390', - cwFg: '#393f4f', link: '@accent', warn: '#ecb637', badge: '#afd2ff', @@ -46,7 +44,6 @@ buttonBg: '#0000000d', switchBg: 'rgba(255, 255, 255, 0.15)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#707b97', indicator: '@accent', mentionMe: '@mention', messageBg: '@bg', diff --git a/packages/frontend/src/themes/l-vivid.json5 b/packages/frontend/src/themes/l-vivid.json5 index 54dcaf05a5..d3cdaf778f 100644 --- a/packages/frontend/src/themes/l-vivid.json5 +++ b/packages/frontend/src/themes/l-vivid.json5 @@ -9,8 +9,6 @@ props: { bg: '#fafafa', fg: '#444', - cwBg: '#b1b9c1', - cwFg: '#fff', link: '#ff9400', warn: '#ecb637', badge: '#afd2ff', @@ -32,7 +30,6 @@ success: '#86b300', buttonBg: 'rgba(0, 0, 0, 0.05)', acrylicBg: ':alpha<0.5<@bg', - cwHoverBg: '#bbc4ce', indicator: '@accent', mentionMe: '@mention', messageBg: '@bg', diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index 65de7b9070..e3fe6823cc 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -71,14 +71,25 @@ export function openInstanceMenu(ev: MouseEvent) { text: i18n.ts.manageCustomEmojis, icon: 'ti ti-icons', } : undefined], - }, null, { - type: 'button', + }, null, (instance.impressumUrl) ? { + text: i18n.ts.impressum, + icon: 'ti ti-file-invoice', + action: () => { + window.open(instance.impressumUrl, '_blank'); + }, + } : undefined, (instance.tosUrl) ? { text: i18n.ts.termsOfService, - icon: 'ti ti-checklist', + icon: 'ti ti-notebook', action: () => { window.open(instance.tosUrl, '_blank'); }, - }, { + } : undefined, (instance.privacyPolicyUrl) ? { + text: i18n.ts.privacyPolicy, + icon: 'ti ti-shield-lock', + action: () => { + window.open(instance.privacyPolicyUrl, '_blank'); + }, + } : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : null, { type: 'parent', text: i18n.ts.help, icon: 'ti ti-help-circle', diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index ed8bcbf6f9..67649a90ab 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -31,6 +31,7 @@ export type Column = { excludeTypes?: typeof notificationTypes[number][]; tl?: 'home' | 'local' | 'social' | 'global'; withRenotes?: boolean; + withReplies?: boolean; onlyFiles?: boolean; onlyCats?: boolean; }; diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 4b684da1a4..b59f0d564e 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -23,9 +23,10 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -52,6 +53,7 @@ let disabled = $ref(false); const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable)); const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable)); const withRenotes = $ref(props.column.withRenotes ?? true); +const withReplies = $ref(props.column.withReplies ?? false); const onlyFiles = $ref(props.column.onlyFiles ?? false); const onlyCats = $ref(props.column.onlyCats ?? false); @@ -115,7 +117,11 @@ const menu = [{ type: 'switch', text: i18n.ts.showRenotes, ref: $$(withRenotes), -}, { +}, props.column.tl === 'local' || props.column.tl === 'social' ? { + type: 'switch', + text: i18n.ts.showRepliesToOthersInTimeline, + ref: $$(withReplies), +} : undefined, { type: 'switch', text: i18n.ts.fileAttachedOnly, ref: $$(onlyFiles), diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 1ecad7ab75..c578894f60 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -38,6 +38,9 @@ module.exports = { 'before': true, 'after': true, }], + 'brace-style': ['error', '1tbs', { + 'allowSingleLine': true, + }], 'padded-blocks': ['error', 'never'], /* TODO: path aliasを使わないとwarnする 'no-restricted-imports': ['warn', { diff --git a/packages/sw/package.json b/packages/sw/package.json index 76019c033e..c0fa8c5245 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -14,9 +14,9 @@ "cherrypick-js": "workspace:*" }, "devDependencies": { - "@typescript-eslint/parser": "6.7.3", + "@typescript-eslint/parser": "6.7.5", "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", - "eslint": "8.50.0", + "eslint": "8.51.0", "eslint-plugin-import": "2.28.1", "typescript": "5.2.2" }, diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts index 2d68b18fb9..53cecd05f5 100644 --- a/packages/sw/src/scripts/operations.ts +++ b/packages/sw/src/scripts/operations.ts @@ -15,7 +15,7 @@ import { getUrlWithLoginId } from '@/scripts/login-id.js'; export const cli = new Misskey.api.APIClient({ origin, fetch: (...args): Promise => fetch(...args) }); export async function api(endpoint: E, userId?: string, options?: O): Promise>> { - let account: { token: string; id: string } | void; + let account: { token: string; id: string } | void = undefined; if (userId) { account = await getAccountFromId(userId); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ad56477a1..f2746ddbfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,8 +25,8 @@ importers: specifier: 8.4.31 version: 8.4.31 terser: - specifier: 5.20.0 - version: 5.20.0 + specifier: 5.21.0 + version: 5.21.0 typescript: specifier: 5.2.2 version: 5.2.2 @@ -36,11 +36,11 @@ importers: version: 4.4.0 devDependencies: '@typescript-eslint/eslint-plugin': - specifier: 6.7.3 - version: 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.3 - version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) cross-env: specifier: 7.0.3 version: 7.0.3 @@ -48,8 +48,8 @@ importers: specifier: 13.3.0 version: 13.3.0 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 start-server-and-test: specifier: 2.0.1 version: 2.0.1 @@ -105,14 +105,14 @@ importers: specifier: ^7.2.1 version: 7.2.1 '@nestjs/common': - specifier: 10.2.6 - version: 10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1) + specifier: 10.2.7 + version: 10.2.7(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/core': - specifier: 10.2.6 - version: 10.2.6(@nestjs/common@10.2.6)(reflect-metadata@0.1.13)(rxjs@7.8.1) + specifier: 10.2.7 + version: 10.2.7(@nestjs/common@10.2.7)(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nestjs/testing': - specifier: 10.2.6 - version: 10.2.6(@nestjs/common@10.2.6)(@nestjs/core@10.2.6) + specifier: 10.2.7 + version: 10.2.7(@nestjs/common@10.2.7)(@nestjs/core@10.2.7) '@peertube/http-signature': specifier: 1.7.0 version: 1.7.0 @@ -127,10 +127,10 @@ importers: version: 2.1.5 '@swc/cli': specifier: 0.1.62 - version: 0.1.62(@swc/core@1.3.90)(chokidar@3.5.3) + version: 0.1.62(@swc/core@1.3.92)(chokidar@3.5.3) '@swc/core': - specifier: 1.3.90 - version: 1.3.90 + specifier: 1.3.92 + version: 1.3.92 '@vitalets/google-translate-api': specifier: 9.2.0 version: 9.2.0 @@ -156,8 +156,8 @@ importers: specifier: 1.20.2 version: 1.20.2 bullmq: - specifier: 4.11.4 - version: 4.11.4 + specifier: 4.12.3 + version: 4.12.3 cacheable-lookup: specifier: 7.0.0 version: 7.0.0 @@ -270,8 +270,8 @@ importers: specifier: 3.3.2 version: 3.3.2 nodemailer: - specifier: 6.9.5 - version: 6.9.5 + specifier: 6.9.6 + version: 6.9.6 nsfwjs: specifier: 2.4.2 version: 2.4.2(@tensorflow/tfjs@4.4.0) @@ -288,8 +288,8 @@ importers: specifier: 0.0.14 version: 0.0.14 otpauth: - specifier: 9.1.4 - version: 9.1.4 + specifier: 9.1.5 + version: 9.1.5 parse5: specifier: 7.1.2 version: 7.1.2 @@ -366,8 +366,8 @@ importers: specifier: github:misskey-dev/summaly version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7 systeminformation: - specifier: 5.21.9 - version: 5.21.9 + specifier: 5.21.11 + version: 5.21.11 tinycolor2: specifier: 1.6.0 version: 1.6.0 @@ -501,7 +501,7 @@ importers: version: 8.0.0 '@swc/jest': specifier: 0.2.29 - version: 0.2.29(@swc/core@1.3.90) + version: 0.2.29(@swc/core@1.3.92) '@types/accepts': specifier: 1.3.5 version: 1.3.5 @@ -551,8 +551,8 @@ importers: specifier: 0.7.32 version: 0.7.32 '@types/node': - specifier: 20.7.1 - version: 20.7.1 + specifier: 20.8.4 + version: 20.8.4 '@types/node-fetch': specifier: 3.0.3 version: 3.0.3 @@ -569,8 +569,8 @@ importers: specifier: 0.1.0 version: 0.1.0 '@types/pg': - specifier: 8.10.3 - version: 8.10.3 + specifier: 8.10.4 + version: 8.10.4 '@types/pug': specifier: 2.0.7 version: 2.0.7 @@ -620,11 +620,11 @@ importers: specifier: 8.5.6 version: 8.5.6 '@typescript-eslint/eslint-plugin': - specifier: 6.7.3 - version: 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.3 - version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) aws-sdk-client-mock: specifier: 3.0.0 version: 3.0.0 @@ -632,17 +632,17 @@ importers: specifier: 7.0.3 version: 7.0.3 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0) execa: specifier: 8.0.1 version: 8.0.1 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.7.1) + version: 29.7.0(@types/node@20.8.4) jest-mock: specifier: 29.7.0 version: 29.7.0 @@ -654,10 +654,10 @@ importers: dependencies: '@swc/cli': specifier: 0.1.62 - version: 0.1.62(@swc/core@1.3.90)(chokidar@3.5.3) + version: 0.1.62(@swc/core@1.3.92)(chokidar@3.5.3) '@swc/core': - specifier: 1.3.90 - version: 1.3.90 + specifier: 1.3.92 + version: 1.3.92 eventemitter3: specifier: 5.0.1 version: 5.0.1 @@ -666,29 +666,29 @@ importers: version: 4.4.0 devDependencies: '@microsoft/api-extractor': - specifier: 7.37.2 - version: 7.37.2(@types/node@20.7.1) + specifier: 7.38.0 + version: 7.38.0(@types/node@20.8.4) '@swc/jest': specifier: 0.2.29 - version: 0.2.29(@swc/core@1.3.90) + version: 0.2.29(@swc/core@1.3.92) '@types/jest': specifier: 29.5.5 version: 29.5.5 '@types/node': - specifier: 20.7.1 - version: 20.7.1 + specifier: 20.8.4 + version: 20.8.4 '@typescript-eslint/eslint-plugin': - specifier: 6.7.3 - version: 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.3 - version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.7.1) + version: 29.7.0(@types/node@20.8.4) jest-fetch-mock: specifier: 3.0.3 version: 3.0.3 @@ -717,17 +717,17 @@ importers: specifier: 2.1.1 version: 2.1.1 '@rollup/plugin-alias': - specifier: 5.0.0 - version: 5.0.0(rollup@3.29.4) + specifier: 5.0.1 + version: 5.0.1(rollup@4.0.2) '@rollup/plugin-json': - specifier: 6.0.0 - version: 6.0.0(rollup@3.29.4) + specifier: 6.0.1 + version: 6.0.1(rollup@4.0.2) '@rollup/plugin-replace': - specifier: 5.0.2 - version: 5.0.2(rollup@3.29.4) + specifier: 5.0.3 + version: 5.0.3(rollup@4.0.2) '@rollup/pluginutils': - specifier: 5.0.4 - version: 5.0.4(rollup@3.29.4) + specifier: 5.0.5 + version: 5.0.5(rollup@4.0.2) '@syuilo/aiscript': specifier: 0.16.0 version: 0.16.0 @@ -735,11 +735,11 @@ importers: specifier: 2.37.0 version: 2.37.0 '@vitejs/plugin-vue': - specifier: 4.3.4 - version: 4.3.4(vite@4.4.9)(vue@3.3.4) + specifier: 4.4.0 + version: 4.4.0(vite@4.4.11)(vue@3.3.4) '@vue-macros/reactivity-transform': specifier: 0.3.23 - version: 0.3.23(rollup@3.29.4)(vue@3.3.4) + version: 0.3.23(rollup@4.0.2)(vue@3.3.4) '@vue/compiler-sfc': specifier: 3.3.4 version: 3.3.4 @@ -783,8 +783,8 @@ importers: specifier: github:kokonect-link/mfm.js version: github.com/kokonect-link/mfm.js/6cfec923792dce92a5401372a42904ea48e5484c chromatic: - specifier: 7.2.0 - version: 7.2.0 + specifier: 7.2.3 + version: 7.2.3 compare-versions: specifier: 6.1.0 version: 6.1.0 @@ -822,8 +822,8 @@ importers: specifier: 0.19.0 version: 0.19.0 photoswipe: - specifier: 5.4.1 - version: 5.4.1 + specifier: 5.4.2 + version: 5.4.2 pretendard: specifier: ^1.3.8 version: 1.3.8 @@ -840,14 +840,14 @@ importers: specifier: 0.2.1 version: 0.2.1 rollup: - specifier: 3.29.4 - version: 3.29.4 + specifier: 4.0.2 + version: 4.0.2 sanitize-html: specifier: 2.11.0 version: 2.11.0 sass: - specifier: 1.68.0 - version: 1.68.0 + specifier: 1.69.1 + version: 1.69.1 strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 @@ -888,8 +888,8 @@ importers: specifier: 1.8.1 version: 1.8.1 vite: - specifier: 4.4.9 - version: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + specifier: 4.4.11 + version: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) vue: specifier: 3.3.4 version: 3.3.4 @@ -901,59 +901,59 @@ importers: version: 4.1.0(vue@3.3.4) devDependencies: '@storybook/addon-actions': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-essentials': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-interactions': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-links': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-storysource': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/addons': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/blocks': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/core-events': - specifier: 7.4.5 - version: 7.4.5 + specifier: 7.4.6 + version: 7.4.6 '@storybook/jest': - specifier: 0.2.2 - version: 0.2.2(vitest@0.34.5) + specifier: 0.2.3 + version: 0.2.3(vitest@0.34.6) '@storybook/manager-api': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/preview-api': - specifier: 7.4.5 - version: 7.4.5 + specifier: 7.4.6 + version: 7.4.6 '@storybook/react': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) '@storybook/react-vite': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0)(rollup@3.29.4)(typescript@5.2.2)(vite@4.4.9) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.0.2)(typescript@5.2.2)(vite@4.4.11) '@storybook/testing-library': - specifier: 0.2.1 - version: 0.2.1 + specifier: 0.2.2 + version: 0.2.2 '@storybook/theming': - specifier: 7.4.5 - version: 7.4.5(react-dom@18.2.0)(react@18.2.0) + specifier: 7.4.6 + version: 7.4.6(react-dom@18.2.0)(react@18.2.0) '@storybook/types': - specifier: 7.4.5 - version: 7.4.5 + specifier: 7.4.6 + version: 7.4.6 '@storybook/vue3': - specifier: 7.4.5 - version: 7.4.5(@vue/compiler-core@3.3.4)(vue@3.3.4) + specifier: 7.4.6 + version: 7.4.6(@vue/compiler-core@3.3.4)(vue@3.3.4) '@storybook/vue3-vite': - specifier: 7.4.5 - version: 7.4.5(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.9)(vue@3.3.4) + specifier: 7.4.6 + version: 7.4.6(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.11)(vue@3.3.4) '@testing-library/vue': specifier: 7.0.0 version: 7.0.0(@vue/compiler-sfc@3.3.4)(vue@3.3.4) @@ -973,8 +973,8 @@ importers: specifier: 4.0.3 version: 4.0.3 '@types/node': - specifier: 20.7.1 - version: 20.7.1 + specifier: 20.8.4 + version: 20.8.4 '@types/prismjs': specifier: ^1.26.0 version: 1.26.0 @@ -991,8 +991,8 @@ importers: specifier: 1.4.4 version: 1.4.4 '@types/uuid': - specifier: 9.0.4 - version: 9.0.4 + specifier: 9.0.5 + version: 9.0.5 '@types/websocket': specifier: 1.0.7 version: 1.0.7 @@ -1000,14 +1000,14 @@ importers: specifier: 8.5.6 version: 8.5.6 '@typescript-eslint/eslint-plugin': - specifier: 6.7.3 - version: 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: 6.7.3 - version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) '@vitest/coverage-v8': - specifier: 0.34.5 - version: 0.34.5(vitest@0.34.5) + specifier: 0.34.6 + version: 0.34.6(vitest@0.34.6) '@vue/runtime-core': specifier: 3.3.4 version: 3.3.4 @@ -1021,17 +1021,17 @@ importers: specifier: 13.3.0 version: 13.3.0 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0) eslint-plugin-storybook: specifier: ^0.6.13 - version: 0.6.13(eslint@8.50.0)(typescript@5.2.2) + version: 0.6.13(eslint@8.51.0)(typescript@5.2.2) eslint-plugin-vue: specifier: 9.17.0 - version: 9.17.0(eslint@8.50.0) + version: 9.17.0(eslint@8.51.0) fast-glob: specifier: 3.3.1 version: 3.3.1 @@ -1042,11 +1042,11 @@ importers: specifier: 4.0.5 version: 4.0.5 msw: - specifier: 1.3.1 - version: 1.3.1(typescript@5.2.2) + specifier: 1.3.2 + version: 1.3.2(typescript@5.2.2) msw-storybook-addon: specifier: 1.8.0 - version: 1.8.0(msw@1.3.1) + version: 1.8.0(msw@1.3.2) nodemon: specifier: 3.0.1 version: 3.0.1 @@ -1063,11 +1063,11 @@ importers: specifier: 2.0.1 version: 2.0.1 storybook: - specifier: 7.4.5 - version: 7.4.5 + specifier: 7.4.6 + version: 7.4.6 storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.5)(@storybook/components@7.4.4)(@storybook/core-events@7.4.5)(@storybook/manager-api@7.4.5)(@storybook/preview-api@7.4.5)(@storybook/theming@7.4.5)(@storybook/types@7.4.5)(react-dom@18.2.0)(react@18.2.0) + version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.6)(@storybook/components@7.4.5)(@storybook/core-events@7.4.6)(@storybook/manager-api@7.4.6)(@storybook/preview-api@7.4.6)(@storybook/theming@7.4.6)(@storybook/types@7.4.6)(react-dom@18.2.0)(react@18.2.0) summaly: specifier: github:misskey-dev/summaly version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7 @@ -1075,17 +1075,17 @@ importers: specifier: 1.0.3 version: 1.0.3 vitest: - specifier: 0.34.5 - version: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) + specifier: 0.34.6 + version: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) vitest-fetch-mock: specifier: 0.2.2 - version: 0.2.2(vitest@0.34.5) + version: 0.2.2(vitest@0.34.6) vue-eslint-parser: - specifier: 9.3.1 - version: 9.3.1(eslint@8.50.0) + specifier: 9.3.2 + version: 9.3.2(eslint@8.51.0) vue-tsc: - specifier: 1.8.15 - version: 1.8.15(typescript@5.2.2) + specifier: 1.8.18 + version: 1.8.18(typescript@5.2.2) packages/sw: dependencies: @@ -1100,17 +1100,17 @@ importers: version: 6.2.1 devDependencies: '@typescript-eslint/parser': - specifier: 6.7.3 - version: 6.7.3(eslint@8.50.0)(typescript@5.2.2) + specifier: 6.7.5 + version: 6.7.5(eslint@8.51.0)(typescript@5.2.2) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.67 version: /@types/serviceworker@0.0.67 eslint: - specifier: 8.50.0 - version: 8.50.0 + specifier: 8.51.0 + version: 8.51.0 eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0) typescript: specifier: 5.2.2 version: 5.2.2 @@ -3686,13 +3686,13 @@ packages: dev: false optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.51.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.50.0 + eslint: 8.51.0 eslint-visitor-keys: 3.4.3 dev: true @@ -3718,8 +3718,8 @@ packages: - supports-color dev: true - /@eslint/js@8.50.0: - resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} + /@eslint/js@8.51.0: + resolution: {integrity: sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -3996,7 +3996,7 @@ packages: engines: {node: ^8.13.0 || >=10.10.0} dependencies: '@grpc/proto-loader': 0.7.7 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: false /@grpc/proto-loader@0.7.7: @@ -4107,7 +4107,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -4128,14 +4128,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.7.1 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.7.1) + jest-config: 29.7.0(@types/node@20.8.4) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4170,7 +4170,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 jest-mock: 29.7.0 dev: true @@ -4197,7 +4197,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.7.1 + '@types/node': 20.8.4 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -4230,7 +4230,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 20.7.1 + '@types/node': 20.8.4 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -4331,7 +4331,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.7.1 + '@types/node': 20.8.4 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: true @@ -4343,12 +4343,12 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.7.1 + '@types/node': 20.8.4 '@types/yargs': 17.0.19 chalk: 4.1.2 dev: true - /@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.2.2)(vite@4.4.9): + /@joshwooding/vite-plugin-react-docgen-typescript@0.2.1(typescript@5.2.2)(vite@4.4.11): resolution: {integrity: sha512-ou4ZJSXMMWHqGS4g8uNRbC5TiTWxAgQZiVucoUrOCWuPrTbkpJbmVyIi9jU72SBry7gQtuMEDp4YR8EEXAg7VQ==} peerDependencies: typescript: '>= 4.3.x' @@ -4362,7 +4362,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.2.2) typescript: 5.2.2 - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) dev: true /@jridgewell/gen-mapping@0.3.2: @@ -4454,24 +4454,24 @@ packages: react: 18.2.0 dev: true - /@microsoft/api-extractor-model@7.28.2(@types/node@20.7.1): + /@microsoft/api-extractor-model@7.28.2(@types/node@20.8.4): resolution: {integrity: sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.61.0(@types/node@20.7.1) + '@rushstack/node-core-library': 3.61.0(@types/node@20.8.4) transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.37.2(@types/node@20.7.1): - resolution: {integrity: sha512-b4tr1rTto9/utTjbuqRwfQP2mzP0ACCmJMUY0JIOfOQ3tewGOkMCIRpIS5kcv5/nURekoAY06hNwHmkVsv/s1g==} + /@microsoft/api-extractor@7.38.0(@types/node@20.8.4): + resolution: {integrity: sha512-e1LhZYnfw+JEebuY2bzhw0imDCl1nwjSThTrQqBXl40hrVo6xm3j/1EpUr89QyzgjqmAwek2ZkIVZbrhaR+cqg==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.28.2(@types/node@20.7.1) + '@microsoft/api-extractor-model': 7.28.2(@types/node@20.8.4) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 3.61.0(@types/node@20.7.1) + '@rushstack/node-core-library': 3.61.0(@types/node@20.8.4) '@rushstack/rig-package': 0.5.1 '@rushstack/ts-command-line': 4.16.1 colors: 1.2.5 @@ -4591,8 +4591,8 @@ packages: tar-fs: 2.1.1 dev: true - /@nestjs/common@10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1): - resolution: {integrity: sha512-ma8R7n+FXsWM4XF9QXjjrsRceyRzid/xKmNKVOa/sTJntkVG8lL71BHBEfjtFvO6EJUqjs/15LbDc0iaN5nCwA==} + /@nestjs/common@10.2.7(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-cUtCRXiUstDmh4bSBhVbq4cI439Gngp4LgLGLBmd5dqFQodfXKnSD441ldYfFiLz4rbUsnoMJz/8ZjuIEI+B7A==} peerDependencies: class-transformer: '*' class-validator: '*' @@ -4611,8 +4611,8 @@ packages: uid: 2.0.2 dev: false - /@nestjs/core@10.2.6(@nestjs/common@10.2.6)(reflect-metadata@0.1.13)(rxjs@7.8.1): - resolution: {integrity: sha512-oGQ2CoBeFRT7egG47MFqS89xlXBTIRZBkRpKRTPMftEfL1RMXhXIcIIaGfzp11wx6qxrBVxBXpVLM09oaqHpaQ==} + /@nestjs/core@10.2.7(@nestjs/common@10.2.7)(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-5GSu53QUUcwX17sNmlJPa1I0wIeAZOKbedyVuQx0ZAwWVa9g0wJBbsNP+R4EJ+j5Dkdzt/8xkiZvnKt8RFRR8g==} requiresBuild: true peerDependencies: '@nestjs/common': ^10.0.0 @@ -4629,7 +4629,7 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.7(reflect-metadata@0.1.13)(rxjs@7.8.1) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -4642,8 +4642,8 @@ packages: - encoding dev: false - /@nestjs/testing@10.2.6(@nestjs/common@10.2.6)(@nestjs/core@10.2.6): - resolution: {integrity: sha512-uxlxHhpSvG4yDTPmuPneoQL1/UnBkOkzE+Zaz6bwURg7lc3uS4ZsXl75OL3pYaJH37rHYXYT9bGcYSpxVbwIrg==} + /@nestjs/testing@10.2.7(@nestjs/common@10.2.7)(@nestjs/core@10.2.7): + resolution: {integrity: sha512-d2SIqiJIf/7NSILeNNWSdRvTTpHSouGgisGHwf5PVDC7z4/yXZw/wPO9eJhegnxFlqk6n2LW4QBTmMzbqjAfHA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 @@ -4655,8 +4655,8 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1) - '@nestjs/core': 10.2.6(@nestjs/common@10.2.6)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/common': 10.2.7(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.7(@nestjs/common@10.2.7)(reflect-metadata@0.1.13)(rxjs@7.8.1) tslib: 2.6.2 dev: false @@ -5340,51 +5340,51 @@ packages: '@babel/runtime': 7.22.10 dev: true - /@rollup/plugin-alias@5.0.0(rollup@3.29.4): - resolution: {integrity: sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA==} + /@rollup/plugin-alias@5.0.1(rollup@4.0.2): + resolution: {integrity: sha512-JObvbWdOHoMy9W7SU0lvGhDtWq9PllP5mjpAy+TUslZG/WzOId9u80Hsqq1vCUn9pFJ0cxpdcnAv+QzU2zFH3Q==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true dependencies: - rollup: 3.29.4 + rollup: 4.0.2 slash: 4.0.0 dev: false - /@rollup/plugin-json@6.0.0(rollup@3.29.4): - resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} + /@rollup/plugin-json@6.0.1(rollup@4.0.2): + resolution: {integrity: sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.4(rollup@3.29.4) - rollup: 3.29.4 + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) + rollup: 4.0.2 dev: false - /@rollup/plugin-replace@5.0.2(rollup@3.29.4): - resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} + /@rollup/plugin-replace@5.0.3(rollup@4.0.2): + resolution: {integrity: sha512-je7fu05B800IrMlWjb2wzJcdXzHYW46iTipfChnBDbIbDXhASZs27W1B58T2Yf45jZtJUONegpbce+9Ut2Ti/Q==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.4(rollup@3.29.4) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) magic-string: 0.27.0 - rollup: 3.29.4 + rollup: 4.0.2 dev: false - /@rollup/pluginutils@5.0.4(rollup@3.29.4): - resolution: {integrity: sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==} + /@rollup/pluginutils@5.0.5(rollup@4.0.2): + resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true @@ -5392,9 +5392,93 @@ packages: '@types/estree': 1.0.2 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.29.4 + rollup: 4.0.2 - /@rushstack/node-core-library@3.61.0(@types/node@20.7.1): + /@rollup/rollup-android-arm-eabi@4.0.2: + resolution: {integrity: sha512-xDvk1pT4vaPU2BOLy0MqHMdYZyntqpaBf8RhBiezlqG9OjY8F50TyctHo8znigYKd+QCFhCmlmXHOL/LoaOl3w==} + cpu: [arm] + os: [android] + requiresBuild: true + optional: true + + /@rollup/rollup-android-arm64@4.0.2: + resolution: {integrity: sha512-lqCglytY3E6raze27DD9VQJWohbwCxzqs9aSHcj5X/8hJpzZfNdbsr4Ja9Hqp6iPyF53+5PtPx0pKRlkSvlHZg==} + cpu: [arm64] + os: [android] + requiresBuild: true + optional: true + + /@rollup/rollup-darwin-arm64@4.0.2: + resolution: {integrity: sha512-nkBKItS6E6CCzvRwgiKad+j+1ibmL7SIInj7oqMWmdkCjiSX6VeVZw2mLlRKIUL+JjsBgpATTfo7BiAXc1v0jA==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + optional: true + + /@rollup/rollup-darwin-x64@4.0.2: + resolution: {integrity: sha512-vX2C8xvWPIbpEgQht95+dY6BReKAvtDgPDGi0XN0kWJKkm4WdNmq5dnwscv/zxvi+n6jUTBhs6GtpkkWT4q8Gg==} + cpu: [x64] + os: [darwin] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.0.2: + resolution: {integrity: sha512-DVFIfcHOjgmeHOAqji4xNz2wczt1Bmzy9MwBZKBa83SjBVO/i38VHDR+9ixo8QpBOiEagmNw12DucG+v55tCrg==} + cpu: [arm] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.0.2: + resolution: {integrity: sha512-GCK/a9ItUxPI0V5hQEJjH4JtOJO90GF2Hja7TO+EZ8rmkGvEi8/ZDMhXmcuDpQT7/PWrTT9RvnG8snMd5SrhBQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.0.2: + resolution: {integrity: sha512-cLuBp7rOjIB1R2j/VazjCmHC7liWUur2e9mFflLJBAWCkrZ+X0+QwHLvOQakIwDymungzAKv6W9kHZnTp/Mqrg==} + cpu: [arm64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.0.2: + resolution: {integrity: sha512-Zqw4iVnJr2naoyQus0yLy7sLtisCQcpdMKUCeXPBjkJtpiflRime/TMojbnl8O3oxUAj92mxr+t7im/RbgA20w==} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.0.2: + resolution: {integrity: sha512-jJRU9TyUD/iMqjf8aLAp7XiN3pIj5v6Qcu+cdzBfVTKDD0Fvua4oUoK8eVJ9ZuKBEQKt3WdlcwJXFkpmMLk6kg==} + cpu: [x64] + os: [linux] + requiresBuild: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.0.2: + resolution: {integrity: sha512-ZkS2NixCxHKC4zbOnw64ztEGGDVIYP6nKkGBfOAxEPW71Sji9v8z3yaHNuae/JHPwXA+14oDefnOuVfxl59SmQ==} + cpu: [arm64] + os: [win32] + requiresBuild: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.0.2: + resolution: {integrity: sha512-3SKjj+tvnZ0oZq2BKB+fI+DqYI83VrRzk7eed8tJkxeZ4zxJZcLSE8YDQLYGq1tZAnAX+H076RHHB4gTZXsQzw==} + cpu: [ia32] + os: [win32] + requiresBuild: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.0.2: + resolution: {integrity: sha512-MBdJIOxRauKkry7t2q+rTHa3aWjVez2eioWg+etRVS3dE4tChhmt5oqZYr48R6bPmcwEhxQr96gVRfeQrLbqng==} + cpu: [x64] + os: [win32] + requiresBuild: true + optional: true + + /@rushstack/node-core-library@3.61.0(@types/node@20.8.4): resolution: {integrity: sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==} peerDependencies: '@types/node': '*' @@ -5402,7 +5486,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 colors: 1.2.5 fs-extra: 7.0.1 import-lazy: 4.0.0 @@ -6033,8 +6117,8 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@storybook/addon-actions@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-FkjJWmPN/+duLSkRwfa2bwlwjKfY6yCXYn7CRzn3rb64B8f50NB79zAgVLHjkJh9l6T3DIlWtol6vqPHj1aRpw==} + /@storybook/addon-actions@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-SsqZr3js5NinKPnC8AeNI7Ij+Q6fIl9tRdRmSulEgjksjOg7E5S1/Wsn5Bb2CCgj7MaX6VxGyC7s3XskQtDiIQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6044,14 +6128,14 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 dequal: 2.0.3 lodash: 4.17.21 polished: 4.2.2 @@ -6067,8 +6151,8 @@ packages: - '@types/react-dom' dev: true - /@storybook/addon-backgrounds@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-fTq9E1WrYH/9hwDemFVLVcaI2iSSuwWnvY/8tqGrY9xhQF5dIpeHf+z8+HWXpau7e6Z0/WiYR+1vwAcIKt95LQ==} + /@storybook/addon-backgrounds@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-+LHTZB/ZYMAzkyD5ZxSriBsqmsrvIaW/Nnd/BeuXGbkrVKKqM0qAKiFZAfjc2WchA1piVNy0/1Rsf+kuYCEiJw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6078,14 +6162,14 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 memoizerific: 1.11.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -6095,8 +6179,8 @@ packages: - '@types/react-dom' dev: true - /@storybook/addon-controls@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Mxs56jt44HIbZ4gJa0AII1U8GqEGFsvcM5Iob0ETNpxCW5Kj5iHly/4Ws0RFWPH/krrQKaLpWXaUxKmbtEzhJA==} + /@storybook/addon-controls@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-4lq3sycEUIsK8SUWDYc60QgF4vV9FZZ3lDr6M7j2W9bOnvGw49d2fbdlnq+bX1ZprZZ9VgglQpBAorQB3BXZRw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6106,16 +6190,16 @@ packages: react-dom: optional: true dependencies: - '@storybook/blocks': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-common': 7.4.5 - '@storybook/core-events': 7.4.5 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 7.4.5 - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/blocks': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.4.6 + '@storybook/core-events': 7.4.6 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 7.4.6 + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 lodash: 4.17.21 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -6127,27 +6211,27 @@ packages: - supports-color dev: true - /@storybook/addon-docs@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-KjFVeq8oL7ZC1gsk8iY3Nn0RrHHUpczmOTCd8FeVNmKD4vq+dkPb/8bJLy+jArmIZ8vRhknpTh6kp1BqB7qHGQ==} + /@storybook/addon-docs@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-dLaub+XWFq4hChw+xfuF9yYg0Txp77FUawKoAigccfjWXx+OOhRV3XTuAcknpXkYq94GWynHgUFXosXT9kbDNA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: '@jest/transform': 29.7.0 '@mdx-js/react': 2.3.0(react@18.2.0) - '@storybook/blocks': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/csf-plugin': 7.4.5 - '@storybook/csf-tools': 7.4.5 + '@storybook/blocks': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/csf-plugin': 7.4.6 + '@storybook/csf-tools': 7.4.6 '@storybook/global': 5.0.0 '@storybook/mdx2-csf': 1.0.0 - '@storybook/node-logger': 7.4.5 - '@storybook/postinstall': 7.4.5 - '@storybook/preview-api': 7.4.5 - '@storybook/react-dom-shim': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/node-logger': 7.4.6 + '@storybook/postinstall': 7.4.6 + '@storybook/preview-api': 7.4.6 + '@storybook/react-dom-shim': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 fs-extra: 11.1.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -6161,25 +6245,25 @@ packages: - supports-color dev: true - /@storybook/addon-essentials@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-H7zZWJXZP0UU2kXfo9zlQfjIKHuuqYBK7PZ2/SL5y08mTrbtt1BfqYScz3xRvHocaFcsBWCXdy8jJULT4KFUpw==} + /@storybook/addon-essentials@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-dWodufrt71TK7ELkeIvVae/x4PzECUlbOm57Iqqt4yQCyR291CgvI4PjeB8un2HbpcXCGZ+N/Oj3YkytvzBi4A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/addon-actions': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-backgrounds': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-controls': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-docs': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-highlight': 7.4.5 - '@storybook/addon-measure': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-outline': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-toolbars': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-viewport': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-common': 7.4.5 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 7.4.5 - '@storybook/preview-api': 7.4.5 + '@storybook/addon-actions': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-backgrounds': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-controls': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-docs': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-highlight': 7.4.6 + '@storybook/addon-measure': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-outline': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-toolbars': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/addon-viewport': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.4.6 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/node-logger': 7.4.6 + '@storybook/preview-api': 7.4.6 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) ts-dedent: 2.2.0 @@ -6190,16 +6274,16 @@ packages: - supports-color dev: true - /@storybook/addon-highlight@7.4.5: - resolution: {integrity: sha512-6Ru411+Iis4m2weKb8kB1eEssLvCHwFqAf4fjcOC//O5Vaf5+beHYZJUm/rzD0k/oUHfLCBwDBSBY5TLRegkdA==} + /@storybook/addon-highlight@7.4.6: + resolution: {integrity: sha512-zCufxxD2KS5VwczxfkcBxe1oR/juTTn2H1Qm8kYvWCJQx3UxzX0+G9cwafbpV7eivqaufLweEwROkH+0KjAtkQ==} dependencies: - '@storybook/core-events': 7.4.5 + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.4.5 + '@storybook/preview-api': 7.4.6 dev: true - /@storybook/addon-interactions@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-KDdV/THxj38VsuOevrUefev0rZPhzqUXCgrw1Jc2PsJGidHf9d9nnB7wbA9ZFYsxTz90M/Vk5sm7i1QkMmsquA==} + /@storybook/addon-interactions@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-zVZYrEPZPhNrXBuPqM7HbQvr6jwsje1sbCYj3wnp83U5wjciuqrngqHIlaSZ30zOWSfRVyzbyqL+JQZKA58BNA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6209,16 +6293,16 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-common': 7.4.5 - '@storybook/core-events': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-common': 7.4.6 + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/instrumenter': 7.4.5 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/instrumenter': 7.4.6 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 jest-mock: 27.5.1 polished: 4.2.2 react: 18.2.0 @@ -6231,8 +6315,8 @@ packages: - supports-color dev: true - /@storybook/addon-links@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-eKczq3U5KfPLaxMUzzVQQrGVtzDshUmrSEEuWKf9ZbK3mh5yVuagIBb88edgUX58vZ3TJMvqQzq1+BtUoPHQ6Q==} + /@storybook/addon-links@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-BPygElZKX+CPI9Se6GJNk1dYc5oxuhA+vHigO1tBqhiM6VkHyFP3cvezJNQvpNYhkUnu3cxnZXb3UJnlRbPY3g==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6242,22 +6326,22 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/core-events': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/core-events': 7.4.6 '@storybook/csf': 0.1.0 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/router': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/router': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) ts-dedent: 2.2.0 dev: true - /@storybook/addon-measure@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-FQGZniTH67nC1YPR4ep0p+isgxwLaNAmIAyCZWXPRTkZssIrnXVwNgi0A2QkHdxZvxj8yXGFTOVXLWEPT9YvFQ==} + /@storybook/addon-measure@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nCymMLaHnxv8TE3yEM1A9Tulb1NuRXRNmtsdHTkjv7P1aWCxZo8A/GZaottKe/GLT8jSRjZ+dnpYWrbAhw6wTQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6267,13 +6351,13 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/types': 7.4.6 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) tiny-invariant: 1.3.1 @@ -6282,8 +6366,8 @@ packages: - '@types/react-dom' dev: true - /@storybook/addon-outline@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-eOH9BZzpehUz5FXD98OLnWgzmBFMvEB2kFfw5JiO7IRx7Fan80fx/WDQuMSNDOgLBCTTvsZ4TBMMXZHpw91WAw==} + /@storybook/addon-outline@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-errNUblRVDLpuEaHQPr/nsrnsUkD2ARmXawkRvizgDWLIDMDJYjTON3MUCaVx3x+hlZ3I6X//G5TVcma8tCc8A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6293,13 +6377,13 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/types': 7.4.6 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) ts-dedent: 2.2.0 @@ -6308,8 +6392,8 @@ packages: - '@types/react-dom' dev: true - /@storybook/addon-storysource@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-aWQkW4IzDHRXdUyHPfksSdk4zK4gIJvXpxVCqX+oz3FuadmwZmhK1vWxNdm4Jo/0EZdwe2YZOBJwXHIwpZtigg==} + /@storybook/addon-storysource@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-qkfwvh/pgVBReuWqO25WyaD7jd6LVqhoIJ6rBWnmx+NBpTds+h3Yt3UJCHgvweIrfSF8J3IqzaTxmmNjnkcrRw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6319,13 +6403,13 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/router': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/source-loader': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/router': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/source-loader': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) estraverse: 5.3.0 prop-types: 15.8.1 react: 18.2.0 @@ -6337,8 +6421,8 @@ packages: - '@types/react-dom' dev: true - /@storybook/addon-toolbars@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-PZlwUTIdQ18de3zNb+627VSF4UrCGIXDdikyO9O5j2Cd0xfr5uhS6tgQ+3AT0DfUj0UIkKxilwcAt+agpNyicA==} + /@storybook/addon-toolbars@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-L9m2FBcKeteGq7qIYsMJr0LEfiH7Wdrv5IDcldZTn68eZUJTh1p4GdJZcOmzX1P5IFRr76hpu03iWsNlWQjpbQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6348,11 +6432,11 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: @@ -6360,8 +6444,8 @@ packages: - '@types/react-dom' dev: true - /@storybook/addon-viewport@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-SBLnUMIztVrqJ0fRCsVg9KZ29APLIxqAvTsYHF3twy5KB2naeCFuX3K9LxSH7vbROI6zHEfnPduz/Ykyvu9yUg==} + /@storybook/addon-viewport@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-INDtk54j7bi7NgxMfd2ATmbA0J7nAd6X8itMkLIyPuPJtx8bYHPDORyemDOd0AojgmAdTOAyUtDYdI/PFeo4Cw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6371,13 +6455,13 @@ packages: react-dom: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) memoizerific: 1.11.3 prop-types: 15.8.1 react: 18.2.0 @@ -6387,36 +6471,36 @@ packages: - '@types/react-dom' dev: true - /@storybook/addons@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-jmdQf39XhwVi8d0J99qpk51fOAwNhYlCtVctvFWPX4qC1cq1d1pxLmTb5OBV2VHQ11BKwlKLzA7coiOgAQmNRg==} + /@storybook/addons@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-c+4awrtwNlJayFdgLkEXa5H2Gj+KNlxuN+Z5oDAdZBLqXI8g0gn7eYO2F/eCSIDWdd/+zcU2uq57XPFKc8veHQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/types': 7.4.6 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/blocks@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-FhAIkCT2HrzJcKsC3mL5+uG3GrbS23mYAT1h3iyPjCliZzxfCCI9UCMUXqYx4Z/FmAGJgpsQQXiBFZuoTHO9aQ==} + /@storybook/blocks@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-HxBSAeOiTZW2jbHQlo1upRWFgoMsaAyKijUFf5MwwMNIesXCuuTGZDJ3xTABwAVLK2qC9Ektfbo0CZCiPVuDRQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/channels': 7.4.5 - '@storybook/client-logger': 7.4.5 - '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.4.5 + '@storybook/channels': 7.4.6 + '@storybook/client-logger': 7.4.6 + '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.4.6 '@storybook/csf': 0.1.0 - '@storybook/docs-tools': 7.4.5 + '@storybook/docs-tools': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 '@types/lodash': 4.14.191 color-convert: 2.0.1 dequal: 2.0.3 @@ -6438,13 +6522,13 @@ packages: - supports-color dev: true - /@storybook/builder-manager@7.4.5: - resolution: {integrity: sha512-Jhql8iZgK9cxDmG9NSTejsj5FptHni2TBa5Sea2Uz1NIBQ0OpzNdUfYVX6TN/PEq3QrWXTrAEKPqsL2qGjOrxw==} + /@storybook/builder-manager@7.4.6: + resolution: {integrity: sha512-zylZCD2rmyLOOFBFmUgtJg6UNUKmRNgXiig1XApzS2TkIbTZP827DsVEUl0ey/lskCe0uArkrEBR6ICba8p/Rw==} dependencies: '@fal-works/esbuild-plugin-global-externals': 2.1.2 - '@storybook/core-common': 7.4.5 - '@storybook/manager': 7.4.5 - '@storybook/node-logger': 7.4.5 + '@storybook/core-common': 7.4.6 + '@storybook/manager': 7.4.6 + '@storybook/node-logger': 7.4.6 '@types/ejs': 3.1.2 '@types/find-cache-dir': 3.2.1 '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.17) @@ -6462,8 +6546,8 @@ packages: - supports-color dev: true - /@storybook/builder-vite@7.4.5(typescript@5.2.2)(vite@4.4.9): - resolution: {integrity: sha512-0aIMvBIx2U/DhDjdjWCW/KIG3HAJpus8NIUIvkVAUCaA7Vn8XvnSsdaRSTTxaaJReFZcIxDf7MebHSCJ0UEKqQ==} + /@storybook/builder-vite@7.4.6(typescript@5.2.2)(vite@4.4.11): + resolution: {integrity: sha512-xV9STYK+TkqWWTf2ydm6jx+7P70fjD2UPd1XTUw08uKszIjhuuxk+bG/OF5R1E25mPunAKXm6kBFh351AKejBg==} peerDependencies: '@preact/preset-vite': '*' typescript: '>= 4.3.x' @@ -6477,15 +6561,15 @@ packages: vite-plugin-glimmerx: optional: true dependencies: - '@storybook/channels': 7.4.5 - '@storybook/client-logger': 7.4.5 - '@storybook/core-common': 7.4.5 - '@storybook/csf-plugin': 7.4.5 + '@storybook/channels': 7.4.6 + '@storybook/client-logger': 7.4.6 + '@storybook/core-common': 7.4.6 + '@storybook/csf-plugin': 7.4.6 '@storybook/mdx2-csf': 1.0.0 - '@storybook/node-logger': 7.4.5 - '@storybook/preview': 7.4.5 - '@storybook/preview-api': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/node-logger': 7.4.6 + '@storybook/preview': 7.4.6 + '@storybook/preview-api': 7.4.6 + '@storybook/types': 7.4.6 '@types/find-cache-dir': 3.2.1 browser-assert: 1.2.1 es-module-lexer: 0.9.3 @@ -6497,23 +6581,12 @@ packages: remark-slug: 6.1.0 rollup: 3.29.4 typescript: 5.2.2 - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - encoding - supports-color dev: true - /@storybook/channels@7.4.4: - resolution: {integrity: sha512-YA2T3hClL95nFBBelm8wMOyWFDzfxKvyHAPQi+8YeYpZcPivwg/P9YnRhTTMbiZNkfoWKq4ZRuc79UP1iNLi3g==} - dependencies: - '@storybook/client-logger': 7.4.4 - '@storybook/core-events': 7.4.4 - '@storybook/global': 5.0.0 - qs: 6.11.1 - telejson: 7.2.0 - tiny-invariant: 1.3.1 - dev: true - /@storybook/channels@7.4.5: resolution: {integrity: sha512-zWPZn4CxPFXsrrSRQ9JD8GmTeWeFYgr3sTBpe23hnhYookCXVNJ6AcaXogrT9b2ALfbB6MiFDbZIHHTgIgbWpg==} dependencies: @@ -6525,22 +6598,33 @@ packages: tiny-invariant: 1.3.1 dev: true - /@storybook/cli@7.4.5: - resolution: {integrity: sha512-PlTkcHdKCugg3pD1zkBP/oFazcZsr7F3wdEmTvygfH0Cx/sQWg5wXBZCYKmf0ONRK4RKL3LVM8DRpeYiQVEFWg==} + /@storybook/channels@7.4.6: + resolution: {integrity: sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==} + dependencies: + '@storybook/client-logger': 7.4.6 + '@storybook/core-events': 7.4.6 + '@storybook/global': 5.0.0 + qs: 6.11.1 + telejson: 7.2.0 + tiny-invariant: 1.3.1 + dev: true + + /@storybook/cli@7.4.6: + resolution: {integrity: sha512-rRwaH8pOL+FHz/pJMEkNpMH2xvZvWsrl7obBYw26NQiHmiVSAkfHJicndSN1mwc+p5w+9iXthrgzbLtSAOSvkA==} hasBin: true dependencies: '@babel/core': 7.22.15 '@babel/preset-env': 7.22.15(@babel/core@7.22.15) '@babel/types': 7.22.19 '@ndelangen/get-tarball': 3.0.7 - '@storybook/codemod': 7.4.5 - '@storybook/core-common': 7.4.5 - '@storybook/core-events': 7.4.5 - '@storybook/core-server': 7.4.5 - '@storybook/csf-tools': 7.4.5 - '@storybook/node-logger': 7.4.5 - '@storybook/telemetry': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/codemod': 7.4.6 + '@storybook/core-common': 7.4.6 + '@storybook/core-events': 7.4.6 + '@storybook/core-server': 7.4.6 + '@storybook/csf-tools': 7.4.6 + '@storybook/node-logger': 7.4.6 + '@storybook/telemetry': 7.4.6 + '@storybook/types': 7.4.6 '@types/semver': 7.5.3 '@yarnpkg/fslib': 2.10.3 '@yarnpkg/libzip': 2.3.0 @@ -6577,28 +6661,28 @@ packages: - utf-8-validate dev: true - /@storybook/client-logger@7.4.4: - resolution: {integrity: sha512-rC/GcCy3DLtTI+oOHLBc6rq/c3oGF/mvdeWrhMM+berQplHJrOCI2pcldjVw8Fc25gLPK0LUlaOp1dfgt2Ri3Q==} - dependencies: - '@storybook/global': 5.0.0 - dev: true - /@storybook/client-logger@7.4.5: resolution: {integrity: sha512-Bn6eTAjhPDUfLpvuxhKkpDpOtkadfkSmkBNBZRu3r0Dzk2J1nNyKV5K6D8dOU4PFVof4z/gXYj5bktT29jKsmw==} dependencies: '@storybook/global': 5.0.0 dev: true - /@storybook/codemod@7.4.5: - resolution: {integrity: sha512-gyI2xliSv4vvnfNQN+0e3tRmT7beiq8q8iGjcBtpOhA2xrStyCR7PjbOfLXtRx2I/b50MDZMRTcckzeM3BLoWQ==} + /@storybook/client-logger@7.4.6: + resolution: {integrity: sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==} + dependencies: + '@storybook/global': 5.0.0 + dev: true + + /@storybook/codemod@7.4.6: + resolution: {integrity: sha512-lxmwEpwksCaAq96APN2YlooSDfKjJ1vKzN5Ni2EqQzf2TEXl7XQjLacHd7OOaII1kfsy+D5gNG4N5wBo7Ub30g==} dependencies: '@babel/core': 7.22.15 '@babel/preset-env': 7.22.15(@babel/core@7.22.15) '@babel/types': 7.22.19 '@storybook/csf': 0.1.0 - '@storybook/csf-tools': 7.4.5 - '@storybook/node-logger': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/csf-tools': 7.4.6 + '@storybook/node-logger': 7.4.6 + '@storybook/types': 7.4.6 '@types/cross-spawn': 6.0.3 cross-spawn: 7.0.3 globby: 11.1.0 @@ -6610,29 +6694,6 @@ packages: - supports-color dev: true - /@storybook/components@7.4.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tFOSu3IoAab/0aY2TY66Go0Nba7AB/+ZB9GFet+dxWypIKGLcPjyX2POIumJU4swzK+4IA8GxgDQ2itS6EOISQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.4.4 - '@storybook/csf': 0.1.0 - '@storybook/global': 5.0.0 - '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.4 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - dev: true - /@storybook/components@7.4.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-boskkfvMBB8CFYY9+1ofFNyKrdWXTY/ghzt7oK80dz6f2Eseo/WXK3OsCdCq5vWbLRCdbgJ8zXG8pAFi4yBsxA==} peerDependencies: @@ -6656,19 +6717,42 @@ packages: - '@types/react-dom' dev: true - /@storybook/core-client@7.4.5: - resolution: {integrity: sha512-d/qiCUZeOKY0HX/YmomxlccxJ2NKC3ttRrAsAXzJGypClKabv20X+qbeO/E7Kp5UQxIEJx1wuwJPcnlCvjgPDA==} + /@storybook/components@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/preview-api': 7.4.5 + '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0) + '@storybook/client-logger': 7.4.6 + '@storybook/csf': 0.1.0 + '@storybook/global': 5.0.0 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' dev: true - /@storybook/core-common@7.4.5: - resolution: {integrity: sha512-c4pBuILMD4YhSpJ+QpKtsUZpK+/rfolwOvzXfJwlN5EpYzMz6FjVR/LyX0cCT2YLI3X5YWRoCdvMxy5Aeryb8g==} + /@storybook/core-client@7.4.6: + resolution: {integrity: sha512-tfgxAHeCvMcs6DsVgtb4hQSDaCHeAPJOsoyhb47eDQfk4OmxzriM0qWucJV5DePSMi+KutX/rN2u0JxfOuN68g==} dependencies: - '@storybook/core-events': 7.4.5 - '@storybook/node-logger': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/preview-api': 7.4.6 + dev: true + + /@storybook/core-common@7.4.6: + resolution: {integrity: sha512-05MJFmOM86qvTLtgDskokIFz9txe0Lbhq4L3by1FtF0GwgH+p+W6I94KI7c6ANER+kVZkXQZhiRzwBFnVTW+Cg==} + dependencies: + '@storybook/core-events': 7.4.6 + '@storybook/node-logger': 7.4.6 + '@storybook/types': 7.4.6 '@types/find-cache-dir': 3.2.1 '@types/node': 16.18.48 '@types/node-fetch': 2.6.4 @@ -6694,36 +6778,36 @@ packages: - supports-color dev: true - /@storybook/core-events@7.4.4: - resolution: {integrity: sha512-kOf4I/a1XC9CaGFwJG5WR2KnkwrOkWX68TLh7OlelKxdl/WjxA4zfzaFPC/8zyCSLdGFLPKNqr1w+ezkb+9Irw==} - dependencies: - ts-dedent: 2.2.0 - dev: true - /@storybook/core-events@7.4.5: resolution: {integrity: sha512-Jzy/adSC95saYCZlgXE5j7jmiMLAXYpnBFBxEtBdXwSWEBb0zt21n1nyWBEAv9s/k2gqDXlPHKHeL5Mn6y40zA==} dependencies: ts-dedent: 2.2.0 dev: true - /@storybook/core-server@7.4.5: - resolution: {integrity: sha512-cW+Qx9Ls823577bd/s9Kv4M1MdKS8mkk6/+nYbwtAwH3hkdlb077rlk/ue0X4O9NZmCrtaJ84KNrBkeDUdFyLQ==} + /@storybook/core-events@7.4.6: + resolution: {integrity: sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==} + dependencies: + ts-dedent: 2.2.0 + dev: true + + /@storybook/core-server@7.4.6: + resolution: {integrity: sha512-jqmRTGCJ1W0WReImivkisPVaLFT5sjtLnFoAk0feHp6QS5j7EYOPN7CYzliyQmARWTLUEXOVaFf3VD6nJZQhJQ==} dependencies: '@aw-web-design/x-default-browser': 1.4.126 '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-manager': 7.4.5 - '@storybook/channels': 7.4.5 - '@storybook/core-common': 7.4.5 - '@storybook/core-events': 7.4.5 + '@storybook/builder-manager': 7.4.6 + '@storybook/channels': 7.4.6 + '@storybook/core-common': 7.4.6 + '@storybook/core-events': 7.4.6 '@storybook/csf': 0.1.0 - '@storybook/csf-tools': 7.4.5 + '@storybook/csf-tools': 7.4.6 '@storybook/docs-mdx': 0.1.0 '@storybook/global': 5.0.0 - '@storybook/manager': 7.4.5 - '@storybook/node-logger': 7.4.5 - '@storybook/preview-api': 7.4.5 - '@storybook/telemetry': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/manager': 7.4.6 + '@storybook/node-logger': 7.4.6 + '@storybook/preview-api': 7.4.6 + '@storybook/telemetry': 7.4.6 + '@storybook/types': 7.4.6 '@types/detect-port': 1.3.2 '@types/node': 16.18.48 '@types/pretty-hrtime': 1.0.1 @@ -6743,7 +6827,6 @@ packages: prompts: 2.4.2 read-pkg-up: 7.0.1 semver: 7.5.4 - serve-favicon: 2.5.0 telejson: 7.2.0 tiny-invariant: 1.3.1 ts-dedent: 2.2.0 @@ -6758,24 +6841,24 @@ packages: - utf-8-validate dev: true - /@storybook/csf-plugin@7.4.5: - resolution: {integrity: sha512-8p3AnwIm3xXtQhiF7OQ0rBiP/Pn5OCMHRiT4FytRnNimGaw7gxRZ2xzM608QZHQ4A8rHfmgoM2FTwgxdC15ulA==} + /@storybook/csf-plugin@7.4.6: + resolution: {integrity: sha512-yi7Qa4NSqKOyiJTWCxlB0ih2ijXq6oY5qZKW6MuMMBP14xJNRGLbH5KabpfXgN2T7YECcOWG1uWaGj2veJb1KA==} dependencies: - '@storybook/csf-tools': 7.4.5 + '@storybook/csf-tools': 7.4.6 unplugin: 1.4.0 transitivePeerDependencies: - supports-color dev: true - /@storybook/csf-tools@7.4.5: - resolution: {integrity: sha512-xbm5HGYvlwF0Efivx37v9rO7Exel1/Tdb/Yv/vXn4D/hQeljNVLNz4Bomfy4EQ207rRsrGDSOHEhLUbHDimnxg==} + /@storybook/csf-tools@7.4.6: + resolution: {integrity: sha512-ocKpcIUtTBy6hlLY34RUFQyX403cWpB2gGfqvkHbpGe2BQj7EyV0zpWnjsfVxvw+M9OWlCdxHWDOPUgXM33ELw==} dependencies: '@babel/generator': 7.22.15 '@babel/parser': 7.22.16 '@babel/traverse': 7.22.15 '@babel/types': 7.22.19 '@storybook/csf': 0.1.0 - '@storybook/types': 7.4.5 + '@storybook/types': 7.4.6 fs-extra: 11.1.1 recast: 0.23.1 ts-dedent: 2.2.0 @@ -6799,12 +6882,12 @@ packages: resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==} dev: true - /@storybook/docs-tools@7.4.5: - resolution: {integrity: sha512-ctK+yGb2nvWISSvCCzj3ZhDaAb7I2BLjbxuBGTyNPvl4V9UQ9LBYzdJwR50q+DfscxdwSHMSOE/0OnzmJdaSJA==} + /@storybook/docs-tools@7.4.6: + resolution: {integrity: sha512-nZj1L/8WwKWWJ41FW4MaKGajZUtrhnr9UwflRCkQJaWhAKmDfOb5M5TqI93uCOULpFPOm5wpoMBz2IHInQ2Lrg==} dependencies: - '@storybook/core-common': 7.4.5 - '@storybook/preview-api': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/core-common': 7.4.6 + '@storybook/preview-api': 7.4.6 + '@storybook/types': 7.4.6 '@types/doctrine': 0.0.3 doctrine: 3.0.0 lodash: 4.17.21 @@ -6823,21 +6906,21 @@ packages: resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} dev: true - /@storybook/instrumenter@7.4.5: - resolution: {integrity: sha512-VLFOcmG75QhWa7MtmfEybIJEz5oT2Ry8xAy/pIVhQwyBaeW0kRT0MHWkixRTtWQmJs/78FmHE3FlgMnqpa5JoA==} + /@storybook/instrumenter@7.4.6: + resolution: {integrity: sha512-K5atRoVFCl6HEgkSxIbwygpzgE/iROc7BrtJ3z3a7E70sanFr6Jxt6Egu6fz2QkL3ef4EWpXMnle2vhEfG29pA==} dependencies: - '@storybook/channels': 7.4.5 - '@storybook/client-logger': 7.4.5 - '@storybook/core-events': 7.4.5 + '@storybook/channels': 7.4.6 + '@storybook/client-logger': 7.4.6 + '@storybook/core-events': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.4.5 + '@storybook/preview-api': 7.4.6 dev: true - /@storybook/jest@0.2.2(vitest@0.34.5): - resolution: {integrity: sha512-PUfp9WoqUA8NdAmiz3UahUsyAMr+g1Dv3BB0fqJZsE2IuE5o1Mgsv4iLGzFm+ohcQLIDQvwvvbQIpxe8eY7TNw==} + /@storybook/jest@0.2.3(vitest@0.34.6): + resolution: {integrity: sha512-ov5izrmbAFObzKeh9AOC5MlmFxAcf0o5i6YFGae9sDx6DGh6alXsRM+chIbucVkUwVHVlSzdfbLDEFGY/ShaYw==} dependencies: '@storybook/expect': 28.1.3-5 - '@testing-library/jest-dom': 6.1.2(@types/jest@28.1.3)(vitest@0.34.5) + '@testing-library/jest-dom': 6.1.2(@types/jest@28.1.3)(vitest@0.34.6) '@types/jest': 28.1.3 jest-mock: 27.5.1 transitivePeerDependencies: @@ -6846,20 +6929,20 @@ packages: - vitest dev: true - /@storybook/manager-api@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-8Hdh5Tutet8xRy2fAknczfvpshz09eVnLd8m34vcFceUOYvEnvDbWerufhlEzovsF4v7U32uqbDHKdKTamWEQQ==} + /@storybook/manager-api@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-inrm3DIbCp8wjXSN/wK6e6i2ysQ/IEmtC7IN0OJ7vdrp+USCooPT448SQTUmVctUGCFmOU3fxXByq8g77oIi7w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/channels': 7.4.5 - '@storybook/client-logger': 7.4.5 - '@storybook/core-events': 7.4.5 + '@storybook/channels': 7.4.6 + '@storybook/client-logger': 7.4.6 + '@storybook/core-events': 7.4.6 '@storybook/csf': 0.1.0 '@storybook/global': 5.0.0 - '@storybook/router': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/router': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 @@ -6871,31 +6954,31 @@ packages: ts-dedent: 2.2.0 dev: true - /@storybook/manager@7.4.5: - resolution: {integrity: sha512-yoqVktWzzC0f8cXsxErOEUfT+VFfWV/W7soytIPQuJFqNaq+BqR5A7WCeoY7BIv3mdpRjo4GKwerCsgoHYeHhg==} + /@storybook/manager@7.4.6: + resolution: {integrity: sha512-kA1hUDxpn1i2SO9OinvLvVXDeL4xgJkModp+pbE8IXv4NJWReNq1ecMeQCzPLS3Sil2gnrullQ9uYXsnZ9bxxA==} dev: true /@storybook/mdx2-csf@1.0.0: resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==} dev: true - /@storybook/node-logger@7.4.5: - resolution: {integrity: sha512-fJSykphbryuEYj1qihbaTH5oOzD4NkptRxyf2uyBrpgkr5tCTq9d7GHheqaBuIdi513dsjlcIR7z5iHxW7ZD+Q==} + /@storybook/node-logger@7.4.6: + resolution: {integrity: sha512-djZb310Q27GviDug1XBv0jOEDLCiwr4hhDE0aifCEKZpfNCi/EaP31nbWimFzZwxu4hE/YAPWExzScruR1zw9Q==} dev: true - /@storybook/postinstall@7.4.5: - resolution: {integrity: sha512-MWRjnKkUpEe2VkHNNpv3zkuMvxM2Zu9DMxFENQaEmhqUHkIFh5klfFwzhSBRexVLzIh7DA1p7mntIpY5A6lh+Q==} + /@storybook/postinstall@7.4.6: + resolution: {integrity: sha512-TqI5BucPAGRWrkh55BYiG2/gHLFtC0In4cuu0GsUzB/1jc4i51npLRorCwhmT7r7YliGl5F7JaP0Bni/qHN3Lg==} dev: true - /@storybook/preview-api@7.4.5: - resolution: {integrity: sha512-6xXQZPyilkGVddfZBI7tMbMMgOyIoZTYgTnwSPTMsXxO0f0TvtNDmGdwhn0I1nREHKfiQGpcQe6gwddEMnGtSg==} + /@storybook/preview-api@7.4.6: + resolution: {integrity: sha512-byUS/Opt3ytWD4cWz3sNEKw5Yks8MkQgRN+GDSyIomaEAQkLAM0rchPC0MYjwCeUSecV7IIQweNX5RbV4a34BA==} dependencies: - '@storybook/channels': 7.4.5 - '@storybook/client-logger': 7.4.5 - '@storybook/core-events': 7.4.5 + '@storybook/channels': 7.4.6 + '@storybook/client-logger': 7.4.6 + '@storybook/core-events': 7.4.6 '@storybook/csf': 0.1.0 '@storybook/global': 5.0.0 - '@storybook/types': 7.4.5 + '@storybook/types': 7.4.6 '@types/qs': 6.9.7 dequal: 2.0.3 lodash: 4.17.21 @@ -6906,12 +6989,12 @@ packages: util-deprecate: 1.0.2 dev: true - /@storybook/preview@7.4.5: - resolution: {integrity: sha512-hCVFoPJP0d7vFCJKaWEsDMa6LcRFcEikQ8Cy6Vo+trS8xXwvwE+vIBqyuPozl4O/MYD9iOlzjgZFNwaUUgX0Jg==} + /@storybook/preview@7.4.6: + resolution: {integrity: sha512-2RPXusJ4CTDrIipIKKvbotD7fP0+8VzoFjImunflIrzN9rni+2rq5eMjqlXAaB+77w064zIR4uDUzI9fxsMDeQ==} dev: true - /@storybook/react-dom-shim@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-/hGe8yuiWbT7L3ZsllmJPgxT9MEQE3k23FhliyKx6IGHsWoYaEsPYPZ9tygqtKY8RpqqMUKWz8+kbO79zUxaoQ==} + /@storybook/react-dom-shim@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-DSq8l9FDocUF1ooVI+TF83pddj1LynE/Hv0/y8XZhc3IgJ/HkuOQuUmfz29ezgfAi9gFYUR8raTIBi3/xdoRmw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6920,25 +7003,25 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/react-vite@7.4.5(react-dom@18.2.0)(react@18.2.0)(rollup@3.29.4)(typescript@5.2.2)(vite@4.4.9): - resolution: {integrity: sha512-VfEktqZlSiAcM0oqUnXvQDIFM/G3pOZSW9VCcdQp2NWbsG/UVH42++ZkT0qJmQtW+Kkr8mTofLK5H1v5si5Z1A==} + /@storybook/react-vite@7.4.6(react-dom@18.2.0)(react@18.2.0)(rollup@4.0.2)(typescript@5.2.2)(vite@4.4.11): + resolution: {integrity: sha512-jkjnrf3FxzR5wcmebXRPflrsM4WIDjWyW/NVFJwxi5PeIOk7fE7/QAPrm4NFRUu2Q7DeuH3oLKsw8bigvUI9RA==} engines: {node: '>=16'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^3.0.0 || ^4.0.0 dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.4.9) - '@rollup/pluginutils': 5.0.4(rollup@3.29.4) - '@storybook/builder-vite': 7.4.5(typescript@5.2.2)(vite@4.4.9) - '@storybook/react': 7.4.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) - '@vitejs/plugin-react': 3.1.0(vite@4.4.9) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.4.11) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) + '@storybook/builder-vite': 7.4.6(typescript@5.2.2)(vite@4.4.11) + '@storybook/react': 7.4.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) + '@vitejs/plugin-react': 3.1.0(vite@4.4.11) ast-types: 0.14.2 magic-string: 0.30.3 react: 18.2.0 react-docgen: 6.0.0-alpha.3 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -6948,8 +7031,8 @@ packages: - vite-plugin-glimmerx dev: true - /@storybook/react@7.4.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2): - resolution: {integrity: sha512-Tiylrs3uFO8QSvH1w3ueSxlAgh2fteH0edRVKaX01M/h47+QqEiZqq/dYkVDvLHngF+CCCwE3OY8nNe6L14Xkw==} + /@storybook/react@7.4.6(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2): + resolution: {integrity: sha512-w0dVo64baFFPTGpUOWFqkKsu6pQincoymegSNgqaBd5DxEyMDRiRoTWSJHMKE9BwgE8SyWhRkP1ak1mkccSOhQ==} engines: {node: '>=16.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -6959,13 +7042,13 @@ packages: typescript: optional: true dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/core-client': 7.4.5 - '@storybook/docs-tools': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/core-client': 7.4.6 + '@storybook/docs-tools': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.4.5 - '@storybook/react-dom-shim': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/preview-api': 7.4.6 + '@storybook/react-dom-shim': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 '@types/escodegen': 0.0.6 '@types/estree': 0.0.51 '@types/node': 16.18.48 @@ -6988,27 +7071,27 @@ packages: - supports-color dev: true - /@storybook/router@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-IM4IhiPiXsx3FAUeUOAB47uiuUS8Yd37VQcNlXLBO28GgHoTSYOrjS+VTGLIV5cAGKr8+H5pFB+q35BnlFUpkQ==} + /@storybook/router@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Vl1esrHkcHxDKqc+HY7+6JQpBPW3zYvGk0cQ2rxVMhWdLZTAz1hss9DqzN9tFnPyfn0a1Q77EpMySkUrvWKKNQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/client-logger': 7.4.5 + '@storybook/client-logger': 7.4.6 memoizerific: 1.11.3 qs: 6.11.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/source-loader@7.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ieo/aPgIXAJfg2raDtsboX43IXiXYHDm0MSXvNXoFE7F1jtRe7gXRi8z7O9xTX4hlIuYea0+kHe+198adgLlWA==} + /@storybook/source-loader@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-tBso55luaKIsZmIsgYyT7HJcjbgjxf0pdzbYqdThZhY3oSl3d56xbcFDCWW+yWjFONuFY8RGPCT7iGywwmaBdQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: '@storybook/csf': 0.1.0 - '@storybook/types': 7.4.5 + '@storybook/types': 7.4.6 estraverse: 5.3.0 lodash: 4.17.21 prettier: 2.8.8 @@ -7016,12 +7099,12 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/telemetry@7.4.5: - resolution: {integrity: sha512-JbhQXZF5sqS2c7Cf+vAtuKTdTSBDco+liUP2UGQFjqdacTRLVzxyj+YY2UH4aAQn7wpmnQ67iHnqFp0+fdYmAA==} + /@storybook/telemetry@7.4.6: + resolution: {integrity: sha512-c8p/C1NIH8EMBviZkBCx8MMDk6rrITJ+b29DEp5MaWSRlklIVyhGiC4RPIRv6sxJwlD41PnqWVFtfu2j2eXLdQ==} dependencies: - '@storybook/client-logger': 7.4.5 - '@storybook/core-common': 7.4.5 - '@storybook/csf-tools': 7.4.5 + '@storybook/client-logger': 7.4.6 + '@storybook/core-common': 7.4.6 + '@storybook/csf-tools': 7.4.6 chalk: 4.1.2 detect-package-manager: 2.0.1 fetch-retry: 5.0.4 @@ -7032,28 +7115,14 @@ packages: - supports-color dev: true - /@storybook/testing-library@0.2.1: - resolution: {integrity: sha512-AdbfLCm1C2nEFrhA3ScdicfW6Fjcorehr6RlGwECMiWwaXisnP971Wd4psqtWxlAqQo4tYBZ0f6rJ3J78JLtsg==} + /@storybook/testing-library@0.2.2: + resolution: {integrity: sha512-L8sXFJUHmrlyU2BsWWZGuAjv39Jl1uAqUHdxmN42JY15M4+XCMjGlArdCCjDe1wpTSW6USYISA9axjZojgtvnw==} dependencies: '@testing-library/dom': 9.2.0 '@testing-library/user-event': 14.4.3(@testing-library/dom@9.2.0) ts-dedent: 2.2.0 dev: true - /@storybook/theming@7.4.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ABIwLRUj2IZKMGxKq+fCCFcY7w52P1a+q8j7qrlELaTe4M74K6rwTgRF0/AFgWeiGRkNuA7z8DjQ73xQLoLqUg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) - '@storybook/client-logger': 7.4.4 - '@storybook/global': 5.0.0 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@storybook/theming@7.4.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-QSIJDIMzOegzlhubIBaYIovf4mlf+AVL0SmQOskPS8GZ6s9t77yUUI6gZTEjO+S4eB3djXRsfTTijQ8+z4XmRA==} peerDependencies: @@ -7068,13 +7137,18 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/types@7.4.4: - resolution: {integrity: sha512-B0VdgGb1XGEb9g3UuEd9xANCIhR3anvA3w0uYSG+7uMOflnEawwZksTSxvvoGM2hx9vC4pNT4Fci9sEC903UkA==} + /@storybook/theming@7.4.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-HW77iJ9ptCMqhoBOYFjRQw7VBap+38fkJGHP5KylEJCyYCgIAm2dEcQmtWpMVYFssSGcb6djfbtAMhYU4TL4Iw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@storybook/channels': 7.4.4 - '@types/babel__core': 7.20.0 - '@types/express': 4.17.17 - file-system-cache: 2.3.0 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) + '@storybook/client-logger': 7.4.6 + '@storybook/global': 5.0.0 + memoizerific: 1.11.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) dev: true /@storybook/types@7.4.5: @@ -7086,22 +7160,31 @@ packages: file-system-cache: 2.3.0 dev: true - /@storybook/vue3-vite@7.4.5(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.9)(vue@3.3.4): - resolution: {integrity: sha512-hNuzSd7EAGpLNGekKjOfuMpir1CpMbSvro4q+04G34CGw2O6awoQKqE+gaOeAHIsSPffio5eeaBR1nKjoKYEog==} + /@storybook/types@7.4.6: + resolution: {integrity: sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==} + dependencies: + '@storybook/channels': 7.4.6 + '@types/babel__core': 7.20.0 + '@types/express': 4.17.17 + file-system-cache: 2.3.0 + dev: true + + /@storybook/vue3-vite@7.4.6(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.11)(vue@3.3.4): + resolution: {integrity: sha512-r/mUDdCifpN99Cqmvm7IvPZGnur7lYiTxbQPhV8NdRBpQGxm3JC0life9yIvvHV9mYRCjn5MEzC65zWx03Nzig==} engines: {node: ^14.18 || >=16} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^3.0.0 || ^4.0.0 dependencies: - '@storybook/builder-vite': 7.4.5(typescript@5.2.2)(vite@4.4.9) - '@storybook/core-server': 7.4.5 - '@storybook/vue3': 7.4.5(@vue/compiler-core@3.3.4)(vue@3.3.4) - '@vitejs/plugin-vue': 4.3.4(vite@4.4.9)(vue@3.3.4) + '@storybook/builder-vite': 7.4.6(typescript@5.2.2)(vite@4.4.11) + '@storybook/core-server': 7.4.6 + '@storybook/vue3': 7.4.6(@vue/compiler-core@3.3.4)(vue@3.3.4) + '@vitejs/plugin-vue': 4.4.0(vite@4.4.11)(vue@3.3.4) magic-string: 0.30.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) vue-docgen-api: 4.64.1(vue@3.3.4) transitivePeerDependencies: - '@preact/preset-vite' @@ -7115,30 +7198,30 @@ packages: - vue dev: true - /@storybook/vue3@7.4.5(@vue/compiler-core@3.3.4)(vue@3.3.4): - resolution: {integrity: sha512-9vmGSg+jwpTYeBneC3XAL5zJW7/kfA/3tXNfIOkqA4oJ087TBoo5XztzbtT6pSNq8fB9AY8VyPTG8ZE5IaJ4xQ==} + /@storybook/vue3@7.4.6(@vue/compiler-core@3.3.4)(vue@3.3.4): + resolution: {integrity: sha512-Azv/GhmPlAUy8UbXZHKubrBlKhGimuJTT2O6zUvIzggR6sJdsRmdWaEv2S90ZpMBkVYyyM9oKS1fZ4eKi/Ds8g==} engines: {node: '>=16.0.0'} peerDependencies: '@vue/compiler-core': ^3.0.0 vue: ^3.0.0 dependencies: - '@storybook/core-client': 7.4.5 - '@storybook/docs-tools': 7.4.5 + '@storybook/core-client': 7.4.6 + '@storybook/docs-tools': 7.4.6 '@storybook/global': 5.0.0 - '@storybook/preview-api': 7.4.5 - '@storybook/types': 7.4.5 + '@storybook/preview-api': 7.4.6 + '@storybook/types': 7.4.6 '@vue/compiler-core': 3.3.4 lodash: 4.17.21 ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.3.4 - vue-component-type-helpers: 1.8.15 + vue-component-type-helpers: 1.8.18 transitivePeerDependencies: - encoding - supports-color dev: true - /@swc/cli@0.1.62(@swc/core@1.3.90)(chokidar@3.5.3): + /@swc/cli@0.1.62(@swc/core@1.3.92)(chokidar@3.5.3): resolution: {integrity: sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==} engines: {node: '>= 12.13'} hasBin: true @@ -7150,7 +7233,7 @@ packages: optional: true dependencies: '@mole-inc/bin-wrapper': 8.0.1 - '@swc/core': 1.3.90 + '@swc/core': 1.3.92 chokidar: 3.5.3 commander: 7.2.0 fast-glob: 3.2.12 @@ -7179,8 +7262,8 @@ packages: dev: false optional: true - /@swc/core-darwin-arm64@1.3.90: - resolution: {integrity: sha512-he0w74HvcoufE6CZrB/U/VGVbc7021IQvYrn1geMACnq/OqMBqjdczNtdNfJAy87LZ4AOUjHDKEIjsZZu7o8nQ==} + /@swc/core-darwin-arm64@1.3.92: + resolution: {integrity: sha512-v7PqZUBtIF6Q5Cp48gqUiG8zQQnEICpnfNdoiY3xjQAglCGIQCjJIDjreZBoeZQZspB27lQN4eZ43CX18+2SnA==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -7196,8 +7279,8 @@ packages: dev: false optional: true - /@swc/core-darwin-x64@1.3.90: - resolution: {integrity: sha512-hKNM0Ix0qMlAamPe0HUfaAhQVbZEL5uK6Iw8v9ew0FtVB4v7EifQ9n41wh+yCj0CjcHBPEBbQU0P6mNTxJu/RQ==} + /@swc/core-darwin-x64@1.3.92: + resolution: {integrity: sha512-Q3XIgQfXyxxxms3bPN+xGgvwk0TtG9l89IomApu+yTKzaIIlf051mS+lGngjnh9L0aUiCp6ICyjDLtutWP54fw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -7224,8 +7307,8 @@ packages: dev: false optional: true - /@swc/core-linux-arm-gnueabihf@1.3.90: - resolution: {integrity: sha512-HumvtrqTWE8rlFuKt7If0ZL7145H/jVc4AeziVjcd+/ajpqub7IyfrLCYd5PmKMtfeSVDMsxjG0BJ0HLRxrTJA==} + /@swc/core-linux-arm-gnueabihf@1.3.92: + resolution: {integrity: sha512-tnOCoCpNVXC+0FCfG84PBZJyLlz0Vfj9MQhyhCvlJz9hQmvpf8nTdKH7RHrOn8VfxtUBLdVi80dXgIFgbvl7qA==} engines: {node: '>=10'} cpu: [arm] os: [linux] @@ -7241,8 +7324,8 @@ packages: dev: false optional: true - /@swc/core-linux-arm64-gnu@1.3.90: - resolution: {integrity: sha512-tA7DqCS7YCwngwXZQeqQhhMm8BbydpaABw8Z/EDQ7KPK1iZ1rNjZw+aWvSpmNmEGmH1RmQ9QDS9mGRDp0faAeg==} + /@swc/core-linux-arm64-gnu@1.3.92: + resolution: {integrity: sha512-lFfGhX32w8h1j74Iyz0Wv7JByXIwX11OE9UxG+oT7lG0RyXkF4zKyxP8EoxfLrDXse4Oop434p95e3UNC3IfCw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -7258,8 +7341,8 @@ packages: dev: false optional: true - /@swc/core-linux-arm64-musl@1.3.90: - resolution: {integrity: sha512-p2Vtid5BZA36fJkNUwk5HP+HJlKgTru+Ghna7pRe45ghKkkRIUk3fhkgudEvfKfhT+3AvP+GTVQ+T9k0gc9S8w==} + /@swc/core-linux-arm64-musl@1.3.92: + resolution: {integrity: sha512-rOZtRcLj57MSAbiecMsqjzBcZDuaCZ8F6l6JDwGkQ7u1NYR57cqF0QDyU7RKS1Jq27Z/Vg21z5cwqoH5fLN+Sg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -7275,8 +7358,8 @@ packages: dev: false optional: true - /@swc/core-linux-x64-gnu@1.3.90: - resolution: {integrity: sha512-J6pDtWaulYGXuANERuvv4CqmUbZOQrRZBCRQGZQJ6a86RWpesZqckBelnYx48wYmkgvMkF95Y3xbI3WTfoSHzw==} + /@swc/core-linux-x64-gnu@1.3.92: + resolution: {integrity: sha512-qptoMGnBL6v89x/Qpn+l1TH1Y0ed+v0qhNfAEVzZvCvzEMTFXphhlhYbDdpxbzRmCjH6GOGq7Y+xrWt9T1/ARg==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -7292,8 +7375,8 @@ packages: dev: false optional: true - /@swc/core-linux-x64-musl@1.3.90: - resolution: {integrity: sha512-3Gh6EA3+0K+l3MqnRON7h5bZ32xLmfcVM6QiHHJ9dBttq7YOEeEoMOCdIPMaQxJmK1VfLgZCsPYRd66MhvUSkw==} + /@swc/core-linux-x64-musl@1.3.92: + resolution: {integrity: sha512-g2KrJ43bZkCZHH4zsIV5ErojuV1OIpUHaEyW1gf7JWKaFBpWYVyubzFPvPkjcxHGLbMsEzO7w/NVfxtGMlFH/Q==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -7309,8 +7392,8 @@ packages: dev: false optional: true - /@swc/core-win32-arm64-msvc@1.3.90: - resolution: {integrity: sha512-BNaw/iJloDyaNOFV23Sr53ULlnbmzSoerTJ10v0TjSZOEIpsS0Rw6xOK1iI0voDJnRXeZeWRSxEC9DhefNtN/g==} + /@swc/core-win32-arm64-msvc@1.3.92: + resolution: {integrity: sha512-3MCRGPAYDoQ8Yyd3WsCMc8eFSyKXY5kQLyg/R5zEqA0uthomo0m0F5/fxAJMZGaSdYkU1DgF73ctOWOf+Z/EzQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -7326,8 +7409,8 @@ packages: dev: false optional: true - /@swc/core-win32-ia32-msvc@1.3.90: - resolution: {integrity: sha512-SiyTethWAheE/JbxXCukAAciU//PLcmVZ2ME92MRuLMLmOhrwksjbaa7ukj9WEF3LWrherhSqTXnpj3VC1l/qw==} + /@swc/core-win32-ia32-msvc@1.3.92: + resolution: {integrity: sha512-zqTBKQhgfWm73SVGS8FKhFYDovyRl1f5dTX1IwSKynO0qHkRCqJwauFJv/yevkpJWsI2pFh03xsRs9HncTQKSA==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -7343,16 +7426,16 @@ packages: dev: false optional: true - /@swc/core-win32-x64-msvc@1.3.90: - resolution: {integrity: sha512-OpWAW5ljKcPJ3SQ0pUuKqYfwXv7ssIhVgrH9XP9ONtdgXKWZRL9hqJQkcL55FARw/gDjKanoCM47wsTNQL+ZZA==} + /@swc/core-win32-x64-msvc@1.3.92: + resolution: {integrity: sha512-41bE66ddr9o/Fi1FBh0sHdaKdENPTuDpv1IFHxSg0dJyM/jX8LbkjnpdInYXHBxhcLVAPraVRrNsC4SaoPw2Pg==} engines: {node: '>=10'} cpu: [x64] os: [win32] requiresBuild: true optional: true - /@swc/core@1.3.90: - resolution: {integrity: sha512-wptBxP4PldOnhmyDVj8qUcn++GRqyw1qc9wOTGtPNHz8cpuTfdfIgYGlhI4La0UYqecuaaIfLfokyuNePOMHPg==} + /@swc/core@1.3.92: + resolution: {integrity: sha512-vx0vUrf4YTEw59njOJ46Ha5i0cZTMYdRHQ7KXU29efN1MxcmJH2RajWLPlvQarOP1ab9iv9cApD7SMchDyx2vA==} engines: {node: '>=10'} requiresBuild: true peerDependencies: @@ -7364,28 +7447,28 @@ packages: '@swc/counter': 0.1.1 '@swc/types': 0.1.5 optionalDependencies: - '@swc/core-darwin-arm64': 1.3.90 - '@swc/core-darwin-x64': 1.3.90 - '@swc/core-linux-arm-gnueabihf': 1.3.90 - '@swc/core-linux-arm64-gnu': 1.3.90 - '@swc/core-linux-arm64-musl': 1.3.90 - '@swc/core-linux-x64-gnu': 1.3.90 - '@swc/core-linux-x64-musl': 1.3.90 - '@swc/core-win32-arm64-msvc': 1.3.90 - '@swc/core-win32-ia32-msvc': 1.3.90 - '@swc/core-win32-x64-msvc': 1.3.90 + '@swc/core-darwin-arm64': 1.3.92 + '@swc/core-darwin-x64': 1.3.92 + '@swc/core-linux-arm-gnueabihf': 1.3.92 + '@swc/core-linux-arm64-gnu': 1.3.92 + '@swc/core-linux-arm64-musl': 1.3.92 + '@swc/core-linux-x64-gnu': 1.3.92 + '@swc/core-linux-x64-musl': 1.3.92 + '@swc/core-win32-arm64-msvc': 1.3.92 + '@swc/core-win32-ia32-msvc': 1.3.92 + '@swc/core-win32-x64-msvc': 1.3.92 /@swc/counter@0.1.1: resolution: {integrity: sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==} - /@swc/jest@0.2.29(@swc/core@1.3.90): + /@swc/jest@0.2.29(@swc/core@1.3.92): resolution: {integrity: sha512-8reh5RvHBsSikDC3WGCd5ZTd2BXKkyOdK7QwynrCH58jk2cQFhhHhFBg/jvnWZehUQe/EoOImLENc9/DwbBFow==} engines: {npm: '>= 7.0.0'} peerDependencies: '@swc/core': '*' dependencies: '@jest/create-cache-key-function': 27.5.1 - '@swc/core': 1.3.90 + '@swc/core': 1.3.92 jsonc-parser: 3.2.0 dev: true @@ -7562,7 +7645,7 @@ packages: pretty-format: 27.5.1 dev: true - /@testing-library/jest-dom@6.1.2(@types/jest@28.1.3)(vitest@0.34.5): + /@testing-library/jest-dom@6.1.2(@types/jest@28.1.3)(vitest@0.34.6): resolution: {integrity: sha512-NP9jl1Q2qDDtx+cqogowtQtmgD2OVs37iMSIsTv5eN5ETRkf26Kj6ugVwA93/gZzzFWQAsgkKkcftDe91BJCkQ==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} peerDependencies: @@ -7581,7 +7664,7 @@ packages: optional: true dependencies: '@adobe/css-tools': 4.3.1 - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.22.10 '@types/jest': 28.1.3 aria-query: 5.1.3 chalk: 3.0.0 @@ -7589,7 +7672,7 @@ packages: dom-accessibility-api: 0.5.16 lodash: 4.17.21 redent: 3.0.0 - vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) dev: true /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0): @@ -7637,7 +7720,7 @@ packages: /@types/accepts@1.3.5: resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/archiver@5.3.3: @@ -7695,7 +7778,7 @@ packages: resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} dependencies: '@types/connect': 3.4.35 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/braces@3.0.1: @@ -7707,7 +7790,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 20.7.1 + '@types/node': 20.8.4 '@types/responselike': 1.0.0 dev: false @@ -7740,7 +7823,7 @@ packages: /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/content-disposition@0.5.6: @@ -7754,7 +7837,7 @@ packages: /@types/cross-spawn@6.0.3: resolution: {integrity: sha512-BDAkU7WHHRHnvBf5z89lcvACsvkz/n7Tv+HyD/uW76O29HoH1Tk/W6iQrepaZVbisvlEek4ygwT8IW7ow9XLAA==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/debug@4.1.7: @@ -7808,7 +7891,7 @@ packages: /@types/express-serve-static-core@4.17.33: resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -7829,27 +7912,27 @@ packages: /@types/fluent-ffmpeg@2.1.22: resolution: {integrity: sha512-ZZPDDrDOb2Ahp5fxZzuw64f0rCcviv+SDuCyJ1PIF/UFn9wNHtb/bY8Dj/2nrbQ7SNsGI7gaO2wJVkkU2HBcMg==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/glob@7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/glob@8.1.0: resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: false /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/hast@2.3.4: @@ -7868,7 +7951,7 @@ packages: /@types/http-link-header@1.0.3: resolution: {integrity: sha512-y8HkoD/vyid+5MrJ3aas0FvU3/BVBGcyG9kgxL0Zn4JwstA8CglFPnrR0RuzOjRCXwqzL5uxWC2IO7Ub0rMU2A==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/istanbul-lib-coverage@2.0.4: @@ -7912,7 +7995,7 @@ packages: /@types/jsdom@21.1.3: resolution: {integrity: sha512-1zzqSP+iHJYV4lB3lZhNBa012pubABkj9yG/GuXuf6LZH1cSPIJBqFDrm5JX65HHt6VOnNYdTui/0ySerRbMgA==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -7936,7 +8019,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: false /@types/linkify-it@3.0.2: @@ -7999,14 +8082,14 @@ packages: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} requiresBuild: true dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 form-data: 3.0.1 dev: false /@types/node-fetch@2.6.4: resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 form-data: 3.0.1 dev: true @@ -8024,13 +8107,15 @@ packages: resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==} dev: true - /@types/node@20.7.1: - resolution: {integrity: sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg==} + /@types/node@20.8.4: + resolution: {integrity: sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==} + dependencies: + undici-types: 5.25.3 /@types/nodemailer@6.4.11: resolution: {integrity: sha512-Ld2c0frwpGT4VseuoeboCXQ7UJIkK3X7Lx/4YsZEiUHtHsthWAOCYtf6PAiLhMtfwV0cWJRabLBS3+LD8x6Nrw==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/normalize-package-data@2.4.1: @@ -8047,13 +8132,13 @@ packages: resolution: {integrity: sha512-U3L0c4eQA6lTSZRgW4LYfhKlR084Aw19akmYHrMdYzaqg9mQDfc2b/1iyqm9+1FJDEnVS5ONi5fxdDrB4/7CpQ==} dependencies: '@types/express': 4.17.17 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/oauth@0.9.2: resolution: {integrity: sha512-Nu3/abQ6yR9VlsCdX3aiGsWFkj6OJvJqDvg/36t8Gwf2mFXdBZXPDN3K+2yfeA6Lo2m1Q12F8Qil9TZ48nWhOQ==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/offscreencanvas@2019.3.0: @@ -8066,10 +8151,10 @@ packages: requiresBuild: true dev: false - /@types/pg@8.10.3: - resolution: {integrity: sha512-BACzsw64lCZesclRpZGu55tnqgFAYcrCBP92xLh1KLypZLCOsvJTSTgaoFVTy3lCys/aZTQzfeDxtjwrvdzL2g==} + /@types/pg@8.10.4: + resolution: {integrity: sha512-6cxJPHzhlJxqAMkWl2w3KubTEM0UjGC0UrtIToa9J/CEuRFJ2bquKt+g9MhYBN9n1+U6UZZ8CW6Z4oLx/Tvh/w==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 pg-protocol: 1.6.0 pg-types: 4.0.1 dev: true @@ -8097,7 +8182,7 @@ packages: /@types/qrcode@1.5.2: resolution: {integrity: sha512-W4KDz75m7rJjFbyCctzCtRzZUj+PrUHV+YjqDp50sSRezTbrtEAIq2iTzC6lISARl3qw+8IlcCyljdcVJE0Wug==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/qs@6.9.7: @@ -8127,7 +8212,7 @@ packages: /@types/readdir-glob@1.1.1: resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/rename@1.0.5: @@ -8137,14 +8222,14 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: false /@types/rimraf@3.0.2: resolution: {integrity: sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==} dependencies: '@types/glob': 8.1.0 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: false /@types/sanitize-html@2.9.1: @@ -8170,7 +8255,7 @@ packages: resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 3.0.1 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/serviceworker@0.0.67: @@ -8180,7 +8265,7 @@ packages: /@types/set-cookie-parser@2.4.3: resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/sharp@0.32.0: @@ -8236,20 +8321,20 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true - /@types/uuid@9.0.4: - resolution: {integrity: sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==} + /@types/uuid@9.0.5: + resolution: {integrity: sha512-xfHdwa1FMJ082prjSJpoEI57GZITiQz10r3vEJCHa2khEFQjKy91aWKz6+zybzssCvXUwE1LQWgWVwZ4nYUvHQ==} dev: true /@types/vary@1.1.1: resolution: {integrity: sha512-XL8U62BpXBMMuFzFBYsWekQwo+dqcyN117IwFVMCkBCvc6HY1ODdRKNA0JHxnuTM5lX3kpqsnBH5OuEeXSN3aA==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/web-push@3.6.1: resolution: {integrity: sha512-Zu6Iju7c4IlE8I8eEeFLYRb7XFqvHFmWWAYr1cmug9EX3c6CDarxIXWN/GO0sxjbJLkHPwozUzp6cLdXsrq7Ew==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/webgl-ext@0.0.30: @@ -8260,13 +8345,13 @@ packages: /@types/websocket@1.0.7: resolution: {integrity: sha512-62Omr8U0PO+hgjLCpPnMsmjh2/FRwIGOktZHyYAUzooEJotwkXHMp7vCacdYi8haxBNOiw9bc2HIHI+b/MPNjA==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/ws@8.5.6: resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /@types/yargs-parser@21.0.0: @@ -8289,12 +8374,12 @@ packages: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true optional: true - /@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.50.0)(typescript@5.2.2): - resolution: {integrity: sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==} + /@typescript-eslint/eslint-plugin@6.7.5(@typescript-eslint/parser@6.7.5)(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-JhtAwTRhOUcP96D0Y6KYnwig/MRQbOoLGXTON2+LlyB/N35SP9j1boai2zzwXb7ypKELXMx3DVk9UTaEq1vHEw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -8305,13 +8390,13 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.7.3 - '@typescript-eslint/type-utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.7.3 + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/type-utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 + eslint: 8.51.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 @@ -8322,8 +8407,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.7.3(eslint@8.50.0)(typescript@5.2.2): - resolution: {integrity: sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==} + /@typescript-eslint/parser@6.7.5(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8332,12 +8417,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.7.3 - '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.7.3 + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 + eslint: 8.51.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -8351,16 +8436,16 @@ packages: '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/scope-manager@6.7.3: - resolution: {integrity: sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==} + /@typescript-eslint/scope-manager@6.7.5: + resolution: {integrity: sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/visitor-keys': 6.7.3 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/visitor-keys': 6.7.5 dev: true - /@typescript-eslint/type-utils@6.7.3(eslint@8.50.0)(typescript@5.2.2): - resolution: {integrity: sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==} + /@typescript-eslint/type-utils@6.7.5(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-Gs0qos5wqxnQrvpYv+pf3XfcRXW6jiAn9zE/K+DlmYf6FcpxeNYN0AIETaPR7rHO4K2UY+D0CIbDP9Ut0U4m1g==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -8369,10 +8454,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) - '@typescript-eslint/utils': 6.7.3(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) + '@typescript-eslint/utils': 6.7.5(eslint@8.51.0)(typescript@5.2.2) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 + eslint: 8.51.0 ts-api-utils: 1.0.1(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -8384,8 +8469,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types@6.7.3: - resolution: {integrity: sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==} + /@typescript-eslint/types@6.7.5: + resolution: {integrity: sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -8410,8 +8495,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.7.3(typescript@5.2.2): - resolution: {integrity: sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==} + /@typescript-eslint/typescript-estree@6.7.5(typescript@5.2.2): + resolution: {integrity: sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -8419,8 +8504,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/visitor-keys': 6.7.3 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 @@ -8431,19 +8516,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/utils@5.62.0(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) - eslint: 8.50.0 + eslint: 8.51.0 eslint-scope: 5.1.1 semver: 7.5.4 transitivePeerDependencies: @@ -8451,19 +8536,19 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.7.3(eslint@8.50.0)(typescript@5.2.2): - resolution: {integrity: sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==} + /@typescript-eslint/utils@6.7.5(eslint@8.51.0)(typescript@5.2.2): + resolution: {integrity: sha512-pfRRrH20thJbzPPlPc4j0UNGvH1PjPlhlCMq4Yx7EGjV7lvEeGX0U6MJYe8+SyFutWgSHsdbJ3BXzZccYggezA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.3 - '@typescript-eslint/scope-manager': 6.7.3 - '@typescript-eslint/types': 6.7.3 - '@typescript-eslint/typescript-estree': 6.7.3(typescript@5.2.2) - eslint: 8.50.0 + '@typescript-eslint/scope-manager': 6.7.5 + '@typescript-eslint/types': 6.7.5 + '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) + eslint: 8.51.0 semver: 7.5.4 transitivePeerDependencies: - supports-color @@ -8478,11 +8563,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@6.7.3: - resolution: {integrity: sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==} + /@typescript-eslint/visitor-keys@6.7.5: + resolution: {integrity: sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.7.3 + '@typescript-eslint/types': 6.7.5 eslint-visitor-keys: 3.4.3 dev: true @@ -8497,7 +8582,7 @@ packages: - encoding dev: false - /@vitejs/plugin-react@3.1.0(vite@4.4.9): + /@vitejs/plugin-react@3.1.0(vite@4.4.11): resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -8508,23 +8593,23 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.22.15) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - supports-color dev: true - /@vitejs/plugin-vue@4.3.4(vite@4.4.9)(vue@3.3.4): - resolution: {integrity: sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==} + /@vitejs/plugin-vue@4.4.0(vite@4.4.11)(vue@3.3.4): + resolution: {integrity: sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) vue: 3.3.4 - /@vitest/coverage-v8@0.34.5(vitest@0.34.5): - resolution: {integrity: sha512-97xjhRTSdmeeHCm2nNHhT3hLsMYkAhHXm/rwj6SZ3voka8xiCJrwgtfIjoZIFEL4OO0KezGmVuHWQXcMunULIA==} + /@vitest/coverage-v8@0.34.6(vitest@0.34.6): + resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==} peerDependencies: vitest: '>=0.32.0 <1' dependencies: @@ -8539,68 +8624,68 @@ packages: std-env: 3.3.3 test-exclude: 6.0.0 v8-to-istanbul: 9.1.0 - vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - supports-color dev: true - /@vitest/expect@0.34.5: - resolution: {integrity: sha512-/3RBIV9XEH+nRpRMqDJBufKIOQaYUH2X6bt0rKSCW0MfKhXFLYsR5ivHifeajRSTsln0FwJbitxLKHSQz/Xwkw==} + /@vitest/expect@0.34.6: + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} dependencies: - '@vitest/spy': 0.34.5 - '@vitest/utils': 0.34.5 - chai: 4.3.7 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.3.10 dev: true - /@vitest/runner@0.34.5: - resolution: {integrity: sha512-RDEE3ViVvl7jFSCbnBRyYuu23XxmvRTSZWW6W4M7eC5dOsK75d5LIf6uhE5Fqf809DQ1+9ICZZNxhIolWHU4og==} + /@vitest/runner@0.34.6: + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} dependencies: - '@vitest/utils': 0.34.5 + '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.1 dev: true - /@vitest/snapshot@0.34.5: - resolution: {integrity: sha512-+ikwSbhu6z2yOdtKmk/aeoDZ9QPm2g/ZO5rXT58RR9Vmu/kB2MamyDSx77dctqdZfP3Diqv4mbc/yw2kPT8rmA==} + /@vitest/snapshot@0.34.6: + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} dependencies: magic-string: 0.30.3 pathe: 1.1.1 pretty-format: 29.7.0 dev: true - /@vitest/spy@0.34.5: - resolution: {integrity: sha512-epsicsfhvBjRjCMOC/3k00mP/TBGQy8/P0DxOFiWyLt55gnZ99dqCfCiAsKO17BWVjn4eZRIjKvcqNmSz8gvmg==} + /@vitest/spy@0.34.6: + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} dependencies: tinyspy: 2.1.1 dev: true - /@vitest/utils@0.34.5: - resolution: {integrity: sha512-ur6CmmYQoeHMwmGb0v+qwkwN3yopZuZyf4xt1DBBSGBed8Hf9Gmbm/5dEWqgpLPdRx6Av6jcWXrjcKfkTzg/pw==} + /@vitest/utils@0.34.6: + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} dependencies: diff-sequences: 29.6.3 loupe: 2.3.6 pretty-format: 29.7.0 dev: true - /@volar/language-core@1.10.0: - resolution: {integrity: sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==} + /@volar/language-core@1.10.3: + resolution: {integrity: sha512-7Qgwu9bWUHN+cLrOkCbIVBkL+RVPREhvY07wY89dGxi4mY9mQCsUVRRp64F61lX7Nc27meMnvy0sWlzY0x6oQQ==} dependencies: - '@volar/source-map': 1.10.0 + '@volar/source-map': 1.10.3 dev: true - /@volar/source-map@1.10.0: - resolution: {integrity: sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==} + /@volar/source-map@1.10.3: + resolution: {integrity: sha512-QE9nwK3xsdBQGongHnC9SCR0itx7xUKQFsUDn5HbZY3pHpyXxdY1hSBG0eh9mE+aTKoM4KlqMvrb+19Tv9vS1Q==} dependencies: muggle-string: 0.3.1 dev: true - /@volar/typescript@1.10.0: - resolution: {integrity: sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==} + /@volar/typescript@1.10.3: + resolution: {integrity: sha512-n0ar6xGYpRoSvgGMetm/JXP0QAXx+NOUvxCaWCfCjiFivQRSLJeydYDijhoGBUl5KSKosqq9In5L3e/m2TqTcQ==} dependencies: - '@volar/language-core': 1.10.0 + '@volar/language-core': 1.10.3 dev: true - /@vue-macros/common@1.8.0(rollup@3.29.4)(vue@3.3.4): + /@vue-macros/common@1.8.0(rollup@4.0.2)(vue@3.3.4): resolution: {integrity: sha512-auDJJzE0z3uRe3867e0DsqcseKImktNf5ojCZgUKqiVxb2yTlwlgOVAYCgoep9oITqxkXQymSvFeKhedi8PhaA==} engines: {node: '>=16.14.0'} peerDependencies: @@ -8610,9 +8695,9 @@ packages: optional: true dependencies: '@babel/types': 7.22.19 - '@rollup/pluginutils': 5.0.4(rollup@3.29.4) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) '@vue/compiler-sfc': 3.3.4 - ast-kit: 0.11.2(rollup@3.29.4) + ast-kit: 0.11.2(rollup@4.0.2) local-pkg: 0.4.3 magic-string-ast: 0.3.0 vue: 3.3.4 @@ -8620,14 +8705,14 @@ packages: - rollup dev: false - /@vue-macros/reactivity-transform@0.3.23(rollup@3.29.4)(vue@3.3.4): + /@vue-macros/reactivity-transform@0.3.23(rollup@4.0.2)(vue@3.3.4): resolution: {integrity: sha512-SubIg1GsNpQdIDJusrcA2FWBgwSY+4jmL0j6SJ6PU85r3rlS+uDhn6AUkqxeZRAdmJnrbGHXDyWUdygOZmWrSg==} engines: {node: '>=16.14.0'} peerDependencies: vue: ^2.7.0 || ^3.2.25 dependencies: '@babel/parser': 7.22.16 - '@vue-macros/common': 1.8.0(rollup@3.29.4)(vue@3.3.4) + '@vue-macros/common': 1.8.0(rollup@4.0.2)(vue@3.3.4) '@vue/compiler-core': 3.3.4 '@vue/shared': 3.3.4 magic-string: 0.30.3 @@ -8671,20 +8756,20 @@ packages: '@vue/compiler-dom': 3.3.4 '@vue/shared': 3.3.4 - /@vue/language-core@1.8.15(typescript@5.2.2): - resolution: {integrity: sha512-zche5Aw8kkvp3YaghuLiOZyVIpoWHjSQ0EfjxGSsqHOPMamdCoa9x3HtbenpR38UMUoKJ88wiWuiOrV3B/Yq+A==} + /@vue/language-core@1.8.18(typescript@5.2.2): + resolution: {integrity: sha512-byTi+mwSL7XnVRtfWE3MJy3HQryoVSQ3lymauXviegn3G1wwwlSOUljzQe3w5PyesOnBEIxYoavfKzMJnExrBA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@volar/language-core': 1.10.0 - '@volar/source-map': 1.10.0 + '@volar/language-core': 1.10.3 + '@volar/source-map': 1.10.3 '@vue/compiler-dom': 3.3.4 '@vue/reactivity': 3.3.4 '@vue/shared': 3.3.4 - minimatch: 9.0.2 + minimatch: 9.0.3 muggle-string: 0.3.1 typescript: 5.2.2 vue-template-compiler: 2.7.14 @@ -8741,11 +8826,11 @@ packages: '@vue/server-renderer': 3.3.4(vue@3.3.4) dev: true - /@vue/typescript@1.8.15(typescript@5.2.2): - resolution: {integrity: sha512-qWyanQKXOsK84S8rP7QBrqsvUdQ0nZABZmTjXMpb3ox4Bp5IbkscREA3OPUrkgl64mAxwwCzIWcOc3BPTCPjQw==} + /@vue/typescript@1.8.18(typescript@5.2.2): + resolution: {integrity: sha512-3M+lu+DUwJW0fNwd/rLE0FenmELxcC6zxgm/YZ25jSTi+uNGj9L5XvXvf20guC69gQvZ+cg49tTxbepfFVuNNQ==} dependencies: - '@volar/typescript': 1.10.0 - '@vue/language-core': 1.8.15(typescript@5.2.2) + '@volar/typescript': 1.10.3 + '@vue/language-core': 1.8.18(typescript@5.2.2) transitivePeerDependencies: - typescript dev: true @@ -9206,12 +9291,12 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true - /ast-kit@0.11.2(rollup@3.29.4): + /ast-kit@0.11.2(rollup@4.0.2): resolution: {integrity: sha512-Q0DjXK4ApbVoIf9GLyCo252tUH44iTnD/hiJ2TQaJeydYWSpKk0sI34+WMel8S9Wt5pbLgG02oJ+gkgX5DV3sQ==} engines: {node: '>=16.14.0'} dependencies: '@babel/parser': 7.22.16 - '@rollup/pluginutils': 5.0.4(rollup@3.29.4) + '@rollup/pluginutils': 5.0.5(rollup@4.0.2) pathe: 1.1.1 transitivePeerDependencies: - rollup @@ -9678,8 +9763,8 @@ packages: dependencies: node-gyp-build: 4.6.0 - /bullmq@4.11.4: - resolution: {integrity: sha512-LuCR3ILngYa3CLC5jyf8DU4Yokj9T12MWwBogP3S4IiJUtbJsQ9GTGFxho3imRxXfcd9DUfrABT/pSoqVigXiQ==} + /bullmq@4.12.3: + resolution: {integrity: sha512-4uPp4NQTALFF+eFK7g8VJM+rt0aiduQdzBomgiEO1OK4OE+TdgC6cjGXooKI/asuB8iDhSZ+pSnGYy5Xyr6qRA==} dependencies: cron-parser: 4.8.1 glob: 8.1.0 @@ -9882,14 +9967,14 @@ packages: dependencies: nofilter: 3.1.0 - /chai@4.3.7: - resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} dependencies: assertion-error: 1.1.0 - check-error: 1.0.2 + check-error: 1.0.3 deep-eql: 4.1.3 - get-func-name: 2.0.0 + get-func-name: 2.0.2 loupe: 2.3.6 pathval: 1.1.1 type-detect: 4.0.8 @@ -9998,8 +10083,10 @@ packages: hammerjs: 2.0.8 dev: false - /check-error@1.0.2: - resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 dev: true /check-more-types@2.24.0: @@ -10051,8 +10138,8 @@ packages: engines: {node: '>=10'} requiresBuild: true - /chromatic@7.2.0: - resolution: {integrity: sha512-EbuvmsM6XAVFC4EQpqR2AT2PaXY4IS8qWxxg6N10AhpRulfX2b2AtW1hUc88cCosRyztd6esxkBdj3FSKR7zVw==} + /chromatic@7.2.3: + resolution: {integrity: sha512-UEcHB1nkPoHWoRybPzv6BOVqPr7PqDNuz3u8NCRg7KJciouOb20HjiUQx4Dh9mgA7JUsb2WeGHE2SG/0fHH0PA==} hasBin: true dev: false @@ -10389,7 +10476,7 @@ packages: readable-stream: 3.6.0 dev: false - /create-jest@29.7.0(@types/node@20.7.1): + /create-jest@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10398,7 +10485,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.7.1) + jest-config: 29.7.0(@types/node@20.8.4) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -11459,7 +11546,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.7)(eslint@8.50.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -11480,15 +11567,15 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) debug: 3.2.7(supports-color@5.5.0) - eslint: 8.50.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3)(eslint@8.50.0): + /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.5)(eslint@8.51.0): resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} engines: {node: '>=4'} peerDependencies: @@ -11498,16 +11585,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.51.0)(typescript@5.2.2) array-includes: 3.1.6 array.prototype.findlastindex: 1.2.2 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7(supports-color@5.5.0) doctrine: 2.1.0 - eslint: 8.50.0 + eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.7)(eslint@8.50.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.7)(eslint@8.51.0) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -11523,15 +11610,15 @@ packages: - supports-color dev: true - /eslint-plugin-storybook@0.6.13(eslint@8.50.0)(typescript@5.2.2): + /eslint-plugin-storybook@0.6.13(eslint@8.51.0)(typescript@5.2.2): resolution: {integrity: sha512-smd+CS0WH1jBqUEJ3znGS7DU4ayBE9z6lkQAK2yrSUv1+rq8BT/tiI5C/rKE7rmiqiAfojtNYZRhzo5HrulccQ==} engines: {node: 12.x || 14.x || >= 16} peerDependencies: eslint: '>=6' dependencies: '@storybook/csf': 0.0.1 - '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) - eslint: 8.50.0 + '@typescript-eslint/utils': 5.62.0(eslint@8.51.0)(typescript@5.2.2) + eslint: 8.51.0 requireindex: 1.2.0 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -11539,19 +11626,19 @@ packages: - typescript dev: true - /eslint-plugin-vue@9.17.0(eslint@8.50.0): + /eslint-plugin-vue@9.17.0(eslint@8.51.0): resolution: {integrity: sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) - eslint: 8.50.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + eslint: 8.51.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.13 semver: 7.5.4 - vue-eslint-parser: 9.3.1(eslint@8.50.0) + vue-eslint-parser: 9.3.2(eslint@8.51.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -11569,14 +11656,6 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope@7.2.0: - resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -11585,24 +11664,19 @@ packages: estraverse: 5.3.0 dev: true - /eslint-visitor-keys@3.4.1: - resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /eslint@8.50.0: - resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} + /eslint@8.51.0: + resolution: {integrity: sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.50.0 + '@eslint/js': 8.51.0 '@humanwhocodes/config-array': 0.11.11 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -11640,15 +11714,6 @@ packages: - supports-color dev: true - /espree@9.5.2: - resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) - eslint-visitor-keys: 3.4.1 - dev: true - /espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -12506,8 +12571,8 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - /get-func-name@2.0.0: - resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true /get-intrinsic@1.2.0: @@ -12844,8 +12909,8 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true - /graphql@16.6.0: - resolution: {integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==} + /graphql@16.8.1: + resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} dev: true @@ -13834,7 +13899,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -13855,7 +13920,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.7.1): + /jest-cli@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -13869,10 +13934,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.7.1) + create-jest: 29.7.0(@types/node@20.8.4) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.7.1) + jest-config: 29.7.0(@types/node@20.8.4) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -13883,7 +13948,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.7.1): + /jest-config@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -13898,7 +13963,7 @@ packages: '@babel/core': 7.22.15 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 babel-jest: 29.7.0(@babel/core@7.22.15) chalk: 4.1.2 ci-info: 3.7.1 @@ -13988,7 +14053,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -14023,7 +14088,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.6 - '@types/node': 20.7.1 + '@types/node': 20.8.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -14084,7 +14149,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.7.1 + '@types/node': 20.8.4 dev: true /jest-mock@29.7.0: @@ -14092,7 +14157,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 jest-util: 29.7.0 dev: true @@ -14147,7 +14212,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -14178,7 +14243,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 chalk: 4.1.2 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 @@ -14230,7 +14295,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 chalk: 4.1.2 ci-info: 3.7.1 graceful-fs: 4.2.11 @@ -14255,7 +14320,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.7.1 + '@types/node': 20.8.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -14274,13 +14339,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@20.7.1): + /jest@29.7.0(@types/node@20.8.4): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -14293,7 +14358,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.7.1) + jest-cli: 29.7.0(@types/node@20.8.4) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -14590,8 +14655,8 @@ packages: resolution: {integrity: sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw==} dev: false - /jssha@3.3.0: - resolution: {integrity: sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w==} + /jssha@3.3.1: + resolution: {integrity: sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==} dev: false /jstransformer@1.0.0: @@ -14848,7 +14913,7 @@ packages: /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: - get-func-name: 2.0.0 + get-func-name: 2.0.2 dev: true /lowercase-keys@2.0.0: @@ -14920,7 +14985,7 @@ packages: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} dependencies: - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 /magic-string@0.30.0: resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} @@ -15206,6 +15271,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -15348,10 +15420,6 @@ packages: /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - /ms@2.1.1: - resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==} - dev: true - /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -15385,17 +15453,17 @@ packages: msgpackr-extract: 3.0.2 dev: false - /msw-storybook-addon@1.8.0(msw@1.3.1): + /msw-storybook-addon@1.8.0(msw@1.3.2): resolution: {integrity: sha512-dw3vZwqjixmiur0vouRSOax7wPSu9Og2Hspy9JZFHf49bZRjwDiLF0Pfn2NXEkGviYJOJiGxS1ejoTiUwoSg4A==} peerDependencies: msw: '>=0.35.0 <2.0.0' dependencies: is-node-process: 1.0.1 - msw: 1.3.1(typescript@5.2.2) + msw: 1.3.2(typescript@5.2.2) dev: true - /msw@1.3.1(typescript@5.2.2): - resolution: {integrity: sha512-GhP5lHSTXNlZb9EaKgPRJ01YAnVXwzkvnTzRn4W8fxU2DXuJrRO+Nb6OHdYqB4fCkwSNpIJH9JkON5Y6rHqJMQ==} + /msw@1.3.2(typescript@5.2.2): + resolution: {integrity: sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==} engines: {node: '>=14'} hasBin: true requiresBuild: true @@ -15413,7 +15481,7 @@ packages: chalk: 4.1.2 chokidar: 3.5.3 cookie: 0.4.2 - graphql: 16.6.0 + graphql: 16.8.1 headers-polyfill: 3.2.5 inquirer: 8.2.5 is-node-process: 1.2.0 @@ -15670,8 +15738,8 @@ packages: /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} - /nodemailer@6.9.5: - resolution: {integrity: sha512-/dmdWo62XjumuLc5+AYQZeiRj+PRR8y8qKtFCOyuOl1k/hckZd8durUUHs/ucKx6/8kN+wFxqKJlQ/LK/qR5FA==} + /nodemailer@6.9.6: + resolution: {integrity: sha512-s7pDtWwe5fLMkQUhw8TkWB/wnZ7SRdd9HRZslq/s24hlZvBP3j32N/ETLmnqTpmj4xoBZL9fOWyCIZ7r2HORHg==} engines: {node: '>=6.0.0'} dev: false @@ -16023,10 +16091,10 @@ packages: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} dev: true - /otpauth@9.1.4: - resolution: {integrity: sha512-T6T0E1WlzwKWESq8K0Ja47u01XjmDmRY/AiUoMAc6xZI/OsTsD4cqBrfpt2WfJ29W5pRiWkuUuyHdNQl0/Ic+Q==} + /otpauth@9.1.5: + resolution: {integrity: sha512-mnic91MZxvj04Ir7FN8Xi6wF3FU8D+s6M5p6FQaSS91/csKswoOI9Dk7kKSnGFAoBYgGTTO+OWScV0nJuzrbPg==} dependencies: - jssha: 3.3.0 + jssha: 3.3.1 dev: false /outvariant@1.4.0: @@ -16356,8 +16424,8 @@ packages: split2: 4.1.0 dev: false - /photoswipe@5.4.1: - resolution: {integrity: sha512-iauO0fP4oMdZvjlXzeIe8um1fZatkGE0bqdoIwpb65jlo/KK1KhfD7Z51+0YhS2tC4FOoOtE1p0c4o/HbY1s2Q==} + /photoswipe@5.4.2: + resolution: {integrity: sha512-z5hr36nAIPOZbHJPbCJ/mQ3+ZlizttF9za5gKXKH/us1k4KNHaRbC63K1Px5sVVKUtGb/2+ixHpKqtwl0WAwvA==} engines: {node: '>= 0.12.0'} dev: false @@ -17063,7 +17131,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.7.1 + '@types/node': 20.8.4 long: 5.2.3 dev: false @@ -17969,6 +18037,25 @@ packages: optionalDependencies: fsevents: 2.3.2 + /rollup@4.0.2: + resolution: {integrity: sha512-MCScu4usMPCeVFaiLcgMDaBQeYi1z6vpWxz0r0hq0Hv77Y2YuOTZldkuNJ54BdYBH3e+nkrk6j0Rre/NLDBYzg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.0.2 + '@rollup/rollup-android-arm64': 4.0.2 + '@rollup/rollup-darwin-arm64': 4.0.2 + '@rollup/rollup-darwin-x64': 4.0.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.0.2 + '@rollup/rollup-linux-arm64-gnu': 4.0.2 + '@rollup/rollup-linux-arm64-musl': 4.0.2 + '@rollup/rollup-linux-x64-gnu': 4.0.2 + '@rollup/rollup-linux-x64-musl': 4.0.2 + '@rollup/rollup-win32-arm64-msvc': 4.0.2 + '@rollup/rollup-win32-ia32-msvc': 4.0.2 + '@rollup/rollup-win32-x64-msvc': 4.0.2 + fsevents: 2.3.2 + /rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} dev: false @@ -18005,10 +18092,6 @@ packages: isarray: 2.0.5 dev: true - /safe-buffer@5.1.1: - resolution: {integrity: sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==} - dev: true - /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -18048,8 +18131,8 @@ packages: postcss: 8.4.31 dev: false - /sass@1.68.0: - resolution: {integrity: sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==} + /sass@1.69.1: + resolution: {integrity: sha512-nc969GvTVz38oqKgYYVHM/Iq7Yl33IILy5uqaH2CWSiSUmRCvw+UR7tA3845Sp4BD5ykCUimvrT3k1EjTwpVUA==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -18130,17 +18213,6 @@ packages: transitivePeerDependencies: - supports-color - /serve-favicon@2.5.0: - resolution: {integrity: sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==} - engines: {node: '>= 0.8.0'} - dependencies: - etag: 1.8.1 - fresh: 0.5.2 - ms: 2.1.1 - parseurl: 1.3.3 - safe-buffer: 5.1.1 - dev: true - /serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -18659,11 +18731,11 @@ packages: resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} dev: true - /storybook@7.4.5: - resolution: {integrity: sha512-J7fidphTJ6SJHlR8f/USQE30K6ipbynLVLsTOz0bNYW/0Ua2t9u6dAYGbbq6bLikl3zxzQbdm9lXMUzmaYAdIA==} + /storybook@7.4.6: + resolution: {integrity: sha512-YkFSpnR47j5zz7yElA+2axLjXN7K7TxDGJRHHlqXmG5iQ0PXzmjrj2RxMDKFz4Ybp/QjEUoJ4rx//ESEY0Nb5A==} hasBin: true dependencies: - '@storybook/cli': 7.4.5 + '@storybook/cli': 7.4.6 transitivePeerDependencies: - bufferutil - encoding @@ -18943,8 +19015,8 @@ packages: resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} dev: true - /systeminformation@5.21.9: - resolution: {integrity: sha512-7pI4mu9P/2MGDV0T49B52E7IULBGj+kRVk6JSYUj5qfAk7N7C7aNX15fXziqrbgZntc6/jjYzWeb/x41jhg/eA==} + /systeminformation@5.21.11: + resolution: {integrity: sha512-dIJEGoP5W7k4JJGje/b+inJrOL5hV9LPsUi5ndBvJydI80CVEcu2DZYgt6prdRErDi2SA4SqYd/WMR4b+u34mA==} engines: {node: '>=8.0.0'} os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true @@ -19056,8 +19128,8 @@ packages: unique-string: 2.0.0 dev: true - /terser@5.20.0: - resolution: {integrity: sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ==} + /terser@5.21.0: + resolution: {integrity: sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==} engines: {node: '>=10'} hasBin: true dependencies: @@ -19614,6 +19686,9 @@ packages: resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} dev: false + /undici-types@5.25.3: + resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} + /undici@5.22.1: resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} engines: {node: '>=14.0'} @@ -19883,8 +19958,8 @@ packages: core-util-is: 1.0.2 extsprintf: 1.3.0 - /vite-node@0.34.5(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0): - resolution: {integrity: sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==} + /vite-node@0.34.6(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0): + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: @@ -19893,7 +19968,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - '@types/node' - less @@ -19909,8 +19984,8 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0): - resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} + /vite@4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0): + resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -19937,29 +20012,29 @@ packages: terser: optional: true dependencies: - '@types/node': 20.7.1 + '@types/node': 20.8.4 esbuild: 0.18.17 postcss: 8.4.31 rollup: 3.29.4 - sass: 1.68.0 - terser: 5.20.0 + sass: 1.69.1 + terser: 5.21.0 optionalDependencies: fsevents: 2.3.2 - /vitest-fetch-mock@0.2.2(vitest@0.34.5): + /vitest-fetch-mock@0.2.2(vitest@0.34.6): resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==} engines: {node: '>=14.14.0'} peerDependencies: vitest: '>=0.16.0' dependencies: cross-fetch: 3.1.5 - vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) + vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0) transitivePeerDependencies: - encoding dev: true - /vitest@0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0): - resolution: {integrity: sha512-CPI68mmnr2DThSB3frSuE5RLm9wo5wU4fbDrDwWQQB1CWgq9jQVoQwnQSzYAjdoBOPoH2UtXpOgHVge/uScfZg==} + /vitest@0.34.6(happy-dom@10.0.3)(sass@1.69.1)(terser@5.21.0): + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} hasBin: true peerDependencies: @@ -19991,16 +20066,16 @@ packages: dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 - '@types/node': 20.7.1 - '@vitest/expect': 0.34.5 - '@vitest/runner': 0.34.5 - '@vitest/snapshot': 0.34.5 - '@vitest/spy': 0.34.5 - '@vitest/utils': 0.34.5 + '@types/node': 20.8.4 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 acorn: 8.10.0 acorn-walk: 8.2.0 cac: 6.7.14 - chai: 4.3.7 + chai: 4.3.10 debug: 4.3.4(supports-color@8.1.1) happy-dom: 10.0.3 local-pkg: 0.4.3 @@ -20011,8 +20086,8 @@ packages: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.4.9(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) - vite-node: 0.34.5(@types/node@20.7.1)(sass@1.68.0)(terser@5.20.0) + vite: 4.4.11(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) + vite-node: 0.34.6(@types/node@20.8.4)(sass@1.69.1)(terser@5.21.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -20028,8 +20103,8 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} - /vue-component-type-helpers@1.8.15: - resolution: {integrity: sha512-RKiPRKW4BdwgmQ9vaNkHYKAThdTbgU4TOphVyyzqxRwsOJOoRIrb+vB49XLvs5CKPNrvxMXZMwPe5FyJCqFWyg==} + /vue-component-type-helpers@1.8.18: + resolution: {integrity: sha512-SklLIg782E5Ff0qdE68AUrRBhT2YGW97edBewNEjCWCw+RSETcGOjA8m1/6T68CXkymWBSk+KDpPXqIGthqCDg==} dev: true /vue-demi@0.13.11(vue@3.3.4): @@ -20065,17 +20140,17 @@ packages: - vue dev: true - /vue-eslint-parser@9.3.1(eslint@8.50.0): - resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} + /vue-eslint-parser@9.3.2(eslint@8.51.0): + resolution: {integrity: sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' dependencies: debug: 4.3.4(supports-color@8.1.1) - eslint: 8.50.0 - eslint-scope: 7.2.0 - eslint-visitor-keys: 3.4.1 - espree: 9.5.2 + eslint: 8.51.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 esquery: 1.4.2 lodash: 4.17.21 semver: 7.5.4 @@ -20107,14 +20182,14 @@ packages: he: 1.2.0 dev: true - /vue-tsc@1.8.15(typescript@5.2.2): - resolution: {integrity: sha512-4DoB3LUj7IToLmggoCxRiFG+QU5lem0nv03m1ocqugXA9rSVoTOEoYYaP8vu8b99Eh+/cCVdYOeIAQ+RsgUYUw==} + /vue-tsc@1.8.18(typescript@5.2.2): + resolution: {integrity: sha512-AwQxBB9SZX308TLL1932P1JByuMsXC2jLfRBGt8SBdm1e3cXkDlFaXUAqibfKnoQ1ZC2zO2NSbeBNdSjOcdvJw==} hasBin: true peerDependencies: typescript: '*' dependencies: - '@vue/language-core': 1.8.15(typescript@5.2.2) - '@vue/typescript': 1.8.15(typescript@5.2.2) + '@vue/language-core': 1.8.18(typescript@5.2.2) + '@vue/typescript': 1.8.18(typescript@5.2.2) semver: 7.5.4 typescript: 5.2.2 dev: true @@ -20600,7 +20675,7 @@ packages: sharp: 0.31.3 dev: false - github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.5)(@storybook/components@7.4.4)(@storybook/core-events@7.4.5)(@storybook/manager-api@7.4.5)(@storybook/preview-api@7.4.5)(@storybook/theming@7.4.5)(@storybook/types@7.4.5)(react-dom@18.2.0)(react@18.2.0): + github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.6)(@storybook/components@7.4.5)(@storybook/core-events@7.4.6)(@storybook/manager-api@7.4.6)(@storybook/preview-api@7.4.6)(@storybook/theming@7.4.6)(@storybook/types@7.4.6)(react-dom@18.2.0)(react@18.2.0): resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640} id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640 name: storybook-addon-misskey-theme @@ -20621,13 +20696,13 @@ packages: react-dom: optional: true dependencies: - '@storybook/blocks': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 7.4.5 - '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 7.4.5 - '@storybook/theming': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.5 + '@storybook/blocks': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) + '@storybook/core-events': 7.4.6 + '@storybook/manager-api': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/preview-api': 7.4.6 + '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.4.6 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: true