Merge remote-tracking branch 'upstream/main'
All checks were successful
continuous-integration/drone Build is passing
continuous-integration/drone/push Build is passing

This commit is contained in:
Dalite 2023-12-17 14:40:41 +01:00
commit 63a70ea1f1
51 changed files with 769 additions and 651 deletions

View file

@ -105,6 +105,10 @@ Rails/Exit:
- 'config/boot.rb'
- 'lib/mastodon/cli/*.rb'
Rails/SkipsModelValidations:
Exclude:
- 'db/*migrate/**/*'
# Reason: Some single letter camel case files shouldn't be split
# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
RSpec/FilePath:

View file

@ -26,7 +26,7 @@ Lint/NonLocalExitFromIterator:
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 125
Max: 100
# Configuration parameters: CountBlocks, Max.
Metrics/BlockNesting:
@ -181,22 +181,6 @@ Rails/SkipsModelValidations:
- 'app/workers/move_worker.rb'
- 'app/workers/scheduler/ip_cleanup_scheduler.rb'
- 'app/workers/scheduler/scheduled_statuses_scheduler.rb'
- 'db/migrate/20161203164520_add_from_account_id_to_notifications.rb'
- 'db/migrate/20170105224407_add_shortcode_to_media_attachments.rb'
- 'db/migrate/20170209184350_add_reply_to_statuses.rb'
- 'db/migrate/20170304202101_add_type_to_media_attachments.rb'
- 'db/migrate/20180528141303_fix_accounts_unique_index.rb'
- 'db/migrate/20180609104432_migrate_web_push_subscriptions2.rb'
- 'db/migrate/20181207011115_downcase_custom_emoji_domains.rb'
- 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
- 'db/migrate/20191007013357_update_pt_locales.rb'
- 'db/migrate/20220316233212_update_kurdish_locales.rb'
- 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb'
- 'db/post_migrate/20200917193528_migrate_notifications_type.rb'
- 'db/post_migrate/20201017234926_fill_account_suspension_origin.rb'
- 'db/post_migrate/20220617202502_migrate_roles.rb'
- 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
- 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
- 'lib/mastodon/cli/accounts.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'spec/lib/activitypub/activity/follow_spec.rb'
@ -378,22 +362,6 @@ Style/IfUnlessModifier:
- 'config/initializers/devise.rb'
- 'config/initializers/ffmpeg.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: InverseMethods, InverseBlocks.
Style/InverseMethods:
Exclude:
- 'app/models/custom_filter.rb'
- 'app/services/update_account_service.rb'
- 'spec/controllers/activitypub/replies_controller_spec.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: line_count_dependent, lambda, literal
Style/Lambda:
Exclude:
- 'config/initializers/simple_form.rb'
- 'config/routes.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).
Style/MapToHash:
Exclude:

View file

@ -168,7 +168,7 @@ GEM
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
bigdecimal (3.1.4)
bigdecimal (3.1.5)
bindata (2.4.15)
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
@ -197,7 +197,7 @@ GEM
activesupport
cbor (0.5.9.6)
charlock_holmes (0.7.7)
chewy (7.3.5)
chewy (7.4.0)
activesupport (>= 5.2)
elasticsearch (>= 7.12.0, < 7.14.0)
elasticsearch-dsl
@ -326,7 +326,7 @@ GEM
ruby-progressbar (~> 1.4)
globalid (1.2.1)
activesupport (>= 6.1)
haml (6.2.0)
haml (6.3.0)
temple (>= 0.8.2)
thor
tilt
@ -335,7 +335,7 @@ GEM
activesupport (>= 5.1)
haml (>= 4.0.6)
railties (>= 5.1)
haml_lint (0.51.0)
haml_lint (0.52.0)
haml (>= 4.0)
parallel (~> 1.10)
rainbow
@ -381,7 +381,7 @@ GEM
rdoc
reline (>= 0.3.8)
jmespath (1.6.2)
json (2.7.0)
json (2.7.1)
json-canonicalization (1.0.0)
json-jwt (1.15.3)
activesupport (>= 4.2)
@ -484,8 +484,8 @@ GEM
nokogiri (1.15.5)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oj (3.16.2)
bigdecimal (~> 3.1)
oj (3.16.3)
bigdecimal (>= 3.0)
omniauth (2.1.1)
hashie (>= 3.4.6)
rack (>= 2.2.3)
@ -622,7 +622,7 @@ GEM
redis (>= 4)
redlock (1.3.2)
redis (>= 3.0.0, < 6.0)
regexp_parser (2.8.2)
regexp_parser (2.8.3)
reline (0.4.1)
io-console (~> 0.5)
request_store (1.5.1)
@ -662,7 +662,7 @@ GEM
rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8)
rspec-support (3.12.1)
rubocop (1.58.0)
rubocop (1.59.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
@ -759,7 +759,7 @@ GEM
unicode-display_width (>= 1.1.1, < 3)
terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0)
test-prof (1.3.0)
test-prof (1.3.1)
thor (1.3.0)
tilt (2.3.0)
timeout (0.4.1)

View file

@ -606,6 +606,7 @@
"search.quick_action.status_search": "Sobivad postitused {x}",
"search.search_or_paste": "Otsi või kleebi URL",
"search_popout.full_text_search_disabled_message": "Pole saadaval kohas {domain}.",
"search_popout.full_text_search_logged_out_message": "Saadaval vaid kui sisse logitud.",
"search_popout.language_code": "Keele ISO-kood",
"search_popout.options": "Otsimisvalikud",
"search_popout.quick_actions": "Kiirtegevused",

View file

@ -21,7 +21,7 @@
"account.blocked": "ブロック済み",
"account.browse_more_on_origin_server": "リモートで表示",
"account.cancel_follow_request": "フォローリクエストの取り消し",
"account.copy": "プロフィールのリンクをコピーして下さい",
"account.copy": "プロフィールのリンクをコピー",
"account.direct": "@{name}さんに非公開でメンション",
"account.disable_notifications": "@{name}さんの投稿時の通知を停止",
"account.domain_blocked": "ドメインブロック中",
@ -192,7 +192,7 @@
"conversation.mark_as_read": "既読にする",
"conversation.open": "会話を表示",
"conversation.with": "{names}",
"copy_icon_button.copied": "クリップボードにコピーされた",
"copy_icon_button.copied": "コピーしました",
"copypaste.copied": "コピーしました",
"copypaste.copy_to_clipboard": "クリップボードにコピー",
"directory.federated": "既知の連合より",
@ -392,7 +392,7 @@
"lists.search": "フォローしている人の中から検索",
"lists.subheading": "あなたのリスト",
"load_pending": "{count}件の新着",
"loading_indicator.label": "",
"loading_indicator.label": "読み込み中…",
"media_gallery.toggle_visible": "{number, plural, one {画像を閉じる} other {画像を閉じる}}",
"moved_to_account_banner.text": "あなたのアカウント『{disabledAccount}』は『{movedToAccount}』に移動したため現在無効になっています。",
"mute_modal.duration": "ミュートする期間",
@ -481,17 +481,17 @@
"onboarding.follows.empty": "表示できる結果はありません。検索やエクスプローラーを使ったり、ほかのアカウントをフォローしたり、後でもう一度試しください。",
"onboarding.follows.lead": "ホームタイムラインはMastodonの軸足となる場所です。たくさんのユーザーをフォローすることで、ホームタイムラインはよりにぎやかでおもしろいものになります。手はじめに、おすすめのアカウントから何人かフォローしてみましょう:",
"onboarding.follows.title": "ホームタイムラインを埋める",
"onboarding.profile.discoverable": "自分のプロフィールが発見できないようにする",
"onboarding.profile.discoverable_hint": "マストドンの見つけやすくする機能が個人情報を利用することに承諾すると、あなたの投稿が検索結果やトレンドに表示されることがあります。また、あなたのプロフィールがあなたと似た興味関心を持つ人に提案されることがあります。",
"onboarding.profile.discoverable": "自分のプロフィールが見つけられるようにする",
"onboarding.profile.discoverable_hint": "Mastodonの「見つける」機能にオプトインすると、あなたの投稿が検索結果やトレンドに表示されることがあります。また、あなたに似た関心を持つ人にプロフィールがおすすめされることがあります。",
"onboarding.profile.display_name": "表示名",
"onboarding.profile.display_name_hint": "あなたのフルネーム、または楽しい名前…",
"onboarding.profile.lead": "このことは後でいつでも設定から完了させることが出来ますし、設定では更に多くのカスタマイズが利用可能になっています。",
"onboarding.profile.display_name_hint": "フルネーム、あるいは面白い名前など",
"onboarding.profile.lead": "あとでいつでも修正できますし、設定画面にはこれ以外のカスタマイズ項目もあります。",
"onboarding.profile.note": "自己紹介",
"onboarding.profile.note_hint": "@を使用して他の人々にメンションをすることができます。また#でハッシュタグが使用できます",
"onboarding.profile.save_and_continue": "保存してから続行して下さい",
"onboarding.profile.note_hint": "ほかの人に @言及 したり、#ハッシュタグ を付けたりできます",
"onboarding.profile.save_and_continue": "保存して続ける",
"onboarding.profile.title": "プロフィールの設定",
"onboarding.profile.upload_avatar": "プロフィール画像をアップロードしてください",
"onboarding.profile.upload_header": "プロフィールのヘッダー画像をアップロードして下さい",
"onboarding.profile.upload_avatar": "プロフィール画像をアップロード",
"onboarding.profile.upload_header": "プロフィールのヘッダー画像をアップロード",
"onboarding.share.lead": "新しいMastodonのアカウントをみんなに紹介しましょう。",
"onboarding.share.message": "「{username}」で #Mastodon はじめました! {url}",
"onboarding.share.next_steps": "次のステップに進む:",
@ -606,6 +606,7 @@
"search.quick_action.status_search": "{x}に該当する投稿",
"search.search_or_paste": "検索またはURLを入力",
"search_popout.full_text_search_disabled_message": "{domain}では利用できません。",
"search_popout.full_text_search_logged_out_message": "ログイン時のみ利用できます。",
"search_popout.language_code": "ISO言語コード",
"search_popout.options": "検索オプション",
"search_popout.quick_actions": "クイック操作",

View file

@ -49,5 +49,32 @@
"admin.dashboard.retention.average": "औसत",
"admin.dashboard.retention.cohort_size": "नयाँ प्रयोगकर्ताहरू",
"alert.rate_limited.message": "कृपया {retry_time, time, medium} पछि पुन: प्रयास गर्नुहोस्।",
"alert.unexpected.message": "एउटा अनपेक्षित त्रुटि भयो।"
"alert.unexpected.message": "एउटा अनपेक्षित त्रुटि भयो।",
"bundle_column_error.retry": "पुन: प्रयास गर्नुहोस्",
"bundle_modal_error.close": "बन्द गर्नुहोस्",
"bundle_modal_error.message": "यो कम्पोनेन्ट लोड गर्दा केही गडबड भयो।",
"bundle_modal_error.retry": "Try again",
"closed_registrations.other_server_instructions": "Mastodon विकेन्द्रीकृत भएकोले, तपाइँ अर्को सर्भरमा खाता खोल्न सक्नुहुन्छ र पनि यो सर्भरसँग अन्तरक्रिया गर्न सक्नुहुन्छ।",
"closed_registrations_modal.description": "हाल {domain} मा खाता सिर्जना गर्न सम्भव छैन, तर कृपया ध्यान राख्नुहोस् कि तपाईंले Mastodon प्रयोग गर्नको लागि {domain} मा नै खाता खोल्न आवश्यक छैन।",
"closed_registrations_modal.find_another_server": "अर्को सर्भर खोज्नुहोस्",
"closed_registrations_modal.title": "Mastodon मा साइन अप गर्दै",
"column.blocks": "ब्लक गरिएको प्रयोगकर्ताहरु",
"column.directory": "प्रोफाइल ब्राउज गर्नुहोस्",
"column.domain_blocks": "ब्लक गरिएको डोमेन",
"column.follow_requests": "फलो अनुरोधहरू",
"column.lists": "सूचीहरू",
"column.notifications": "सूचनाहरू",
"column_header.hide_settings": "सेटिङ्हरू लुकाउनुहोस्",
"column_subheading.settings": "सेटिङहरू",
"compose.language.change": "भाषा परिवर्तन गर्नुहोस्",
"compose.language.search": "भाषाहरू खोज्नुहोस्...",
"compose_form.direct_message_warning_learn_more": "थप जान्नुहोस्",
"compose_form.poll.add_option": "विकल्प थप्नुहोस्",
"compose_form.poll.remove_option": "यो विकल्प हटाउनुहोस्",
"compose_form.publish_form": "नयाँ पोस्ट",
"compose_form.save_changes": "परिवर्तनहरू सेभ गर्नुहोस",
"compose_form.sensitive.hide": "{count, plural, one {संवेदनशील मिडियाको रूपमा चिन्ह लगाउनुहोस्} other {संवेदनशील मिडियाहरूको रूपमा चिन्ह लगाउनुहोस्}}",
"compose_form.sensitive.marked": "{count, plural, one {मिडियालाई संवेदनशील रूपमा चिन्ह लगाइएको छ} other {मिडियाहरूलाई संवेदनशील रूपमा चिन्ह लगाइएको छ}}",
"compose_form.sensitive.unmarked": "{count, plural, one {मिडियालाई संवेदनशील रूपमा चिन्ह लगाइएको छैन} other {मिडियाहरूलाई संवेदनशील रूपमा चिन्ह लगाइएको छैन}}",
"compose_form.spoiler_placeholder": "यहाँ आफ्नो चेतावनी लेख्नुहोस्"
}

View file

@ -8,7 +8,7 @@
"about.domain_blocks.silenced.title": "已受限",
"about.domain_blocks.suspended.explanation": "來自此伺服器的資料都不會被處理、儲存或交換,也無法與此伺服器上的使用者互動或交流。",
"about.domain_blocks.suspended.title": "已停權",
"about.not_available": "無法於伺服器上使用此資訊。",
"about.not_available": "無法於伺服器上使用此資訊。",
"about.powered_by": "由 {mastodon} 提供的去中心化社群媒體",
"about.rules": "伺服器規則",
"account.account_note_header": "備註",
@ -34,9 +34,9 @@
"account.follow": "跟隨",
"account.followers": "跟隨者",
"account.followers.empty": "尚未有人跟隨這位使用者。",
"account.followers_counter": "被 {count, plural,one {{counter} 人}other {{counter} 人}}跟隨",
"account.followers_counter": "被 {count, plural, other {{counter} 人}}跟隨",
"account.following": "跟隨中",
"account.following_counter": "正在跟隨 {count, plural, one {{counter} 人} other {{counter} 人}}",
"account.following_counter": "正在跟隨 {count,plural,other {{counter} 人}}",
"account.follows.empty": "這位使用者尚未跟隨任何人。",
"account.follows_you": "跟隨了您",
"account.go_to_profile": "前往個人檔案",
@ -72,8 +72,8 @@
"account.unmute_notifications_short": "取消靜音推播通知",
"account.unmute_short": "解除靜音",
"account_note.placeholder": "按此新增備註",
"admin.dashboard.daily_retention": "註冊後使用者存留率(日)",
"admin.dashboard.monthly_retention": "註冊後使用者存留率(月)",
"admin.dashboard.daily_retention": "註冊後使用者存留率(日)",
"admin.dashboard.monthly_retention": "註冊後使用者存留率(月)",
"admin.dashboard.retention.average": "平均",
"admin.dashboard.retention.cohort": "註冊月份",
"admin.dashboard.retention.cohort_size": "新使用者",
@ -103,9 +103,9 @@
"bundle_modal_error.message": "載入此元件時發生錯誤。",
"bundle_modal_error.retry": "重試",
"closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與這個伺服器互動。",
"closed_registrations_modal.description": "目前無法於 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon 。",
"closed_registrations_modal.description": "目前無法於 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon。",
"closed_registrations_modal.find_another_server": "尋找另一個伺服器",
"closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架一個自己的伺服器!",
"closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您於哪個伺服器新增帳號,都可以與此伺服器上的任何人跟隨及互動。您甚至能自行架一個自己的伺服器!",
"closed_registrations_modal.title": "註冊 Mastodon",
"column.about": "關於",
"column.blocks": "已封鎖的使用者",
@ -155,7 +155,7 @@
"compose_form.publish_form": "嘟出去",
"compose_form.publish_loud": "{publish}",
"compose_form.save_changes": "儲存變更",
"compose_form.sensitive.hide": "標記媒體為敏感內容",
"compose_form.sensitive.hide": "{count, plural, other {將媒體標記為敏感內容}}",
"compose_form.sensitive.marked": "此媒體被標記為敏感內容",
"compose_form.sensitive.unmarked": "此媒體未被標記為敏感內容",
"compose_form.spoiler.marked": "移除內容警告",
@ -207,14 +207,14 @@
"dismissable_banner.explore_statuses": "這些於此伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。越多不同人轉嘟及最愛排名更高。",
"dismissable_banner.explore_tags": "這些主題標籤正在被此伺服器以及去中心化網路上的人們熱烈討論著。越多不同人所嘟出的主題標籤排名更高。",
"dismissable_banner.public_timeline": "這些是來自 {domain} 使用者們跟隨中帳號所發表之最新公開嘟文。",
"embed.instructions": "若您欲於您的網站嵌入此嘟文,請複製以下程式碼。",
"embed.instructions": "如要將此嘟文嵌入您的網站,請複製以下程式碼。",
"embed.preview": "它將顯示成這樣:",
"emoji_button.activity": "活動",
"emoji_button.clear": "清除",
"emoji_button.custom": "自訂",
"emoji_button.flags": "旗幟",
"emoji_button.food": "食物 & 飲料",
"emoji_button.label": "插入表情符號",
"emoji_button.label": "插入表情圖案",
"emoji_button.nature": "自然",
"emoji_button.not_found": "啊就沒這表情符號吼!! (╯°□°)╯︵ ┻━┻",
"emoji_button.objects": "物件",
@ -353,11 +353,11 @@
"keyboard_shortcuts.legend": "顯示此說明選單",
"keyboard_shortcuts.local": "開啟本站時間軸",
"keyboard_shortcuts.mention": "提及作者",
"keyboard_shortcuts.muted": "開啟靜音使用者列表",
"keyboard_shortcuts.muted": "開啟靜音使用者清單",
"keyboard_shortcuts.my_profile": "開啟個人檔案頁面",
"keyboard_shortcuts.notifications": "開啟通知欄",
"keyboard_shortcuts.open_media": "開啟媒體",
"keyboard_shortcuts.pinned": "開啟釘選的嘟文列表",
"keyboard_shortcuts.pinned": "開啟釘選的嘟文清單",
"keyboard_shortcuts.profile": "開啟作者的個人檔案頁面",
"keyboard_shortcuts.reply": "回應嘟文",
"keyboard_shortcuts.requests": "開啟跟隨請求列表",
@ -386,7 +386,7 @@
"lists.new.create": "新增列表",
"lists.new.title_placeholder": "新列表標題",
"lists.replies_policy.followed": "任何跟隨的使用者",
"lists.replies_policy.list": "列表成員",
"lists.replies_policy.list": "成員清單",
"lists.replies_policy.none": "沒有人",
"lists.replies_policy.title": "顯示回覆:",
"lists.search": "搜尋您跟隨的使用者",
@ -452,7 +452,7 @@
"notifications.column_settings.push": "推播通知",
"notifications.column_settings.reblog": "轉嘟:",
"notifications.column_settings.show": "於欄位中顯示",
"notifications.column_settings.sound": "播放音",
"notifications.column_settings.sound": "播放",
"notifications.column_settings.status": "新嘟文:",
"notifications.column_settings.unread_notifications.category": "未讀通知",
"notifications.column_settings.unread_notifications.highlight": "突顯未讀通知",
@ -477,7 +477,7 @@
"onboarding.actions.back": "返回",
"onboarding.actions.go_to_explore": "看看發生什麼新鮮事",
"onboarding.actions.go_to_home": "前往您的首頁時間軸",
"onboarding.compose.template": "哈囉 #Mastodon",
"onboarding.compose.template": "你好 #Mastodon",
"onboarding.follows.empty": "很遺憾,目前未能顯示任何結果。您可以嘗試使用搜尋、瀏覽探索頁面以找尋人們跟隨、或稍候再試。",
"onboarding.follows.lead": "您的首頁時間軸是 Mastodon 的核心體驗。若您跟隨更多人的話,它將會變得更活躍有趣。這些個人檔案也許是個好起點,您可以隨時取消跟隨他們!",
"onboarding.follows.title": "客製化您的首頁時間軸",
@ -540,7 +540,7 @@
"regeneration_indicator.label": "載入中…",
"regeneration_indicator.sublabel": "您的首頁時間軸正在準備中!",
"relative_time.days": "{number} 天",
"relative_time.full.days": "{number, plural, one {# 天} other {# 天}}前",
"relative_time.full.days": "{number, plural, other {# 天}}前",
"relative_time.full.hours": "{number, plural, one {# 小時} other {# 小時}}前",
"relative_time.full.just_now": "剛剛",
"relative_time.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}前",
@ -620,7 +620,7 @@
"search_results.see_all": "檢視全部",
"search_results.statuses": "嘟文",
"search_results.title": "搜尋:{q}",
"server_banner.about_active_users": "最近三十日內使用此伺服器的人 (月活躍使用者)",
"server_banner.about_active_users": "最近三十日內使用此伺服器的人(月活躍使用者)",
"server_banner.active_users": "活躍使用者",
"server_banner.administered_by": "管理者:",
"server_banner.introduction": "{domain} 是由 {mastodon} 提供之去中心化社群網路一部分。",
@ -687,7 +687,7 @@
"status.translated_from_with": "透過 {provider} 翻譯 {lang}",
"status.uncached_media_warning": "無法預覽",
"status.unmute_conversation": "解除此對話的靜音",
"status.unpin": "個人檔案頁面取消釘選",
"status.unpin": "個人檔案頁面取消釘選",
"subscribed_languages.lead": "僅選定語言的嘟文才會出現於您的首頁上,並於變更後列出時間軸。選取「無」以接收所有語言的嘟文。",
"subscribed_languages.save": "儲存變更",
"subscribed_languages.target": "變更 {target} 的訂閱語言",

View file

@ -4398,11 +4398,6 @@ a.status-card {
align-items: center;
justify-content: center;
@supports (display: grid) {
// hack to fix Chrome <57
contain: strict;
}
& > span {
max-width: 500px;
}

View file

@ -37,13 +37,13 @@ class InlineRenderer
private
def preload_associations_for_status
ActiveRecord::Associations::Preloader.new(records: @object, associations: {
ActiveRecord::Associations::Preloader.new(records: [@object], associations: {
active_mentions: :account,
reblog: {
active_mentions: :account,
},
})
}).call
end
def current_user

View file

@ -78,9 +78,9 @@ class Announcement < ApplicationRecord
else
scope.select("name, custom_emoji_id, count(*) as count, exists(select 1 from announcement_reactions r where r.account_id = #{account.id} and r.announcement_id = announcement_reactions.announcement_id and r.name = announcement_reactions.name) as me")
end
end
end.to_a
ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji)
ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji).call
records
end

View file

@ -124,7 +124,7 @@ module Account::Search
tsquery = generate_query_for_search(terms)
find_by_sql([BASIC_SEARCH_SQL, { limit: limit, offset: offset, tsquery: tsquery }]).tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat)
ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
end
end
@ -133,7 +133,7 @@ module Account::Search
sql_template = following ? ADVANCED_SEARCH_WITH_FOLLOWING : ADVANCED_SEARCH_WITHOUT_FOLLOWING
find_by_sql([sql_template, { id: account.id, limit: limit, offset: offset, tsquery: tsquery }]).tap do |records|
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat)
ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
end
end

View file

@ -91,7 +91,7 @@ class CustomFilter < ApplicationRecord
filters_hash.values.map { |cache| [cache.delete(:filter), cache] }
end.to_a
active_filters.select { |custom_filter, _| !custom_filter.expired? }
active_filters.reject { |custom_filter, _| custom_filter.expired? }
end
def self.apply_cached_filters(cached_filters, status)

View file

@ -111,7 +111,7 @@ class Notification < ApplicationRecord
# Instead of using the usual `includes`, manually preload each type.
# If polymorphic associations are loaded with the usual `includes`, other types of associations will be loaded more.
ActiveRecord::Associations::Preloader.new(records: grouped_notifications, associations: associations)
ActiveRecord::Associations::Preloader.new(records: grouped_notifications, associations: associations).call
end
unique_target_statuses = notifications.filter_map(&:target_status).uniq

View file

@ -13,29 +13,7 @@ class InitialStateSerializer < ActiveModel::Serializer
has_one :role, serializer: REST::RoleSerializer
def meta
store = {
streaming_api_base_url: Rails.configuration.x.streaming_api_base_url,
access_token: object.token,
locale: I18n.locale,
domain: Addressable::IDNA.to_unicode(instance_presenter.domain),
title: instance_presenter.title,
admin: object.admin&.id&.to_s,
search_enabled: Chewy.enabled?,
repository: Mastodon::Version.repository,
source_url: instance_presenter.source_url,
version: instance_presenter.version,
limited_federation_mode: Rails.configuration.x.limited_federation_mode,
mascot: instance_presenter.mascot&.file&.url,
profile_directory: Setting.profile_directory,
trends_enabled: Setting.trends,
registrations_open: Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode,
timeline_preview: Setting.timeline_preview,
activity_api_enabled: Setting.activity_api_enabled,
single_user_mode: Rails.configuration.x.single_user_mode,
trends_as_landing_page: Setting.trends_as_landing_page,
status_page_url: Setting.status_page_url,
sso_redirect: sso_redirect,
}
store = default_meta_store
if object.current_account
store[:me] = object.current_account.id.to_s
@ -86,8 +64,8 @@ class InitialStateSerializer < ActiveModel::Serializer
ActiveRecord::Associations::Preloader.new(
records: [object.current_account, object.admin, object.owner, object.disabled_account, object.moved_to_account].compact,
associations: [:account_stat, :user, { moved_to_account: [:account_stat, :user] }]
)
associations: [:account_stat, { user: :role, moved_to_account: [:account_stat, { user: :role }] }]
).call
store[object.current_account.id.to_s] = serialized_account(object.current_account) if object.current_account
store[object.admin.id.to_s] = serialized_account(object.admin) if object.admin
@ -108,6 +86,32 @@ class InitialStateSerializer < ActiveModel::Serializer
private
def default_meta_store
{
access_token: object.token,
activity_api_enabled: Setting.activity_api_enabled,
admin: object.admin&.id&.to_s,
domain: Addressable::IDNA.to_unicode(instance_presenter.domain),
limited_federation_mode: Rails.configuration.x.limited_federation_mode,
locale: I18n.locale,
mascot: instance_presenter.mascot&.file&.url,
profile_directory: Setting.profile_directory,
registrations_open: Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode,
repository: Mastodon::Version.repository,
search_enabled: Chewy.enabled?,
single_user_mode: Rails.configuration.x.single_user_mode,
source_url: instance_presenter.source_url,
sso_redirect: sso_redirect,
status_page_url: Setting.status_page_url,
streaming_api_base_url: Rails.configuration.x.streaming_api_base_url,
timeline_preview: Setting.timeline_preview,
title: instance_presenter.title,
trends_as_landing_page: Setting.trends_as_landing_page,
trends_enabled: Setting.trends,
version: instance_presenter.version,
}
end
def object_account_user
object.current_account.user
end

View file

@ -218,7 +218,7 @@ class AccountSearchService < BaseService
records = query_builder.build.limit(limit_for_non_exact_results).offset(offset).objects.compact
ActiveRecord::Associations::Preloader.new(records: records, associations: :account_stat)
ActiveRecord::Associations::Preloader.new(records: records, associations: [:account_stat, { user: :role }]).call
records
rescue Faraday::ConnectionFailed, Parslet::ParseFailed

View file

@ -11,7 +11,7 @@ class BatchedRemoveStatusService < BaseService
ActiveRecord::Associations::Preloader.new(
records: statuses,
associations: options[:skip_side_effects] ? :reblogs : [:account, :tags, reblogs: :account]
)
).call
statuses_and_reblogs = statuses.flat_map { |status| [status] + status.reblogs }
@ -23,7 +23,7 @@ class BatchedRemoveStatusService < BaseService
ActiveRecord::Associations::Preloader.new(
records: statuses_with_account_conversations,
associations: [mentions: :account]
)
).call
statuses_with_account_conversations.each(&:unlink_from_conversations!)

View file

@ -74,6 +74,15 @@ class FanOutOnWriteService < BaseService
LocalNotificationWorker.push_bulk(mentions) do |mention|
[mention.account_id, mention.id, 'Mention', 'mention']
end
next unless update?
# This may result in duplicate update payloads, but this ensures clients
# are aware of edits to posts only appearing in mention notifications
# (e.g. private mentions or mentions by people they do not follow)
PushUpdateWorker.push_bulk(mentions.filter { |mention| subscribed_to_streaming_api?(mention.account_id) }) do |mention|
[mention.account_id, @status.id, "timeline:#{mention.account_id}:notifications", { 'update' => true }]
end
end
end
@ -162,4 +171,8 @@ class FanOutOnWriteService < BaseService
def broadcastable?
@status.public_visibility? && !@status.reblog? && !@account.silenced?
end
def subscribed_to_streaming_api?(account_id)
redis.exists?("subscribed:timeline:#{account_id}") || redis.exists?("subscribed:timeline:#{account_id}:notifications")
end
end

View file

@ -100,7 +100,7 @@ class FetchOEmbedService
end
def validate(oembed)
oembed if oembed[:version].to_s == '1.0' && oembed[:type].present?
oembed if oembed.present? && oembed[:version].to_s == '1.0' && oembed[:type].present?
end
def html

View file

@ -21,7 +21,7 @@ class UpdateAccountService < BaseService
def authorize_all_follow_requests(account)
follow_requests = FollowRequest.where(target_account: account)
follow_requests = follow_requests.preload(:account).select { |req| !req.account.silenced? }
follow_requests = follow_requests.preload(:account).reject { |req| req.account.silenced? }
AuthorizeFollowWorker.push_bulk(follow_requests, limit: 1_000) do |req|
[req.account_id, req.target_account_id]
end

View file

@ -63,13 +63,6 @@ module Mastodon
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
# TODO: Release a version which uses the 7.0 defaults as specified above,
# but preserves the 6.1 cache format as set below. In a subsequent change,
# remove this line setting to 6.1 cache format, and then release another version.
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activesupport-cache-serialization-format
# https://github.com/mastodon/mastodon/pull/24241#discussion_r1162890242
config.active_support.cache_format_version = 6.1
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.

View file

@ -1,9 +1,6 @@
# frozen_string_literal: true
# TODO: Remove after 4.2.0
Rails.application.configure do
config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA1
end
# TODO: remove this file some time after 4.3.0
Rails.application.config.after_initialize do
Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
@ -12,9 +9,8 @@ Rails.application.config.after_initialize do
secret_key_base = Rails.application.secret_key_base
# TODO: Switch to SHA1 after 4.2.0
key_generator = ActiveSupport::KeyGenerator.new(
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA256
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
)
key_len = ActiveSupport::MessageEncryptor.key_len

View file

@ -164,7 +164,7 @@ SimpleForm.setup do |config|
# config.item_wrapper_class = nil
# How the label text should be generated altogether with the required text.
config.label_text = lambda { |label, required, _explicit_label| "#{label} #{required}" }
config.label_text = ->(label, required, _explicit_label) { "#{label} #{required}" }
# You can define the class to use on all labels. Default is nil.
# config.label_class = nil

View file

@ -1,13 +1,8 @@
# frozen_string_literal: true
module Rack
class Request
def trusted_proxy?(ip)
if Rails.application.config.action_dispatch.trusted_proxies.nil?
super
else
Rails.application.config.action_dispatch.trusted_proxies.any? { |proxy| proxy === ip }
end
end
end
unless Rails.application.config.action_dispatch.trusted_proxies.nil?
# Rack is configured with a default collection of trusted proxies
# If Rails has been configured to use a specific list, configure
# Rack to use this Proc, which enforces the Rails-configured list.
Rack::Request.ip_filter = ->(ip) { Rails.application.config.action_dispatch.trusted_proxies.include?(ip) }
end

View file

@ -28,7 +28,7 @@ zh-TW:
doorkeeper/application:
attributes:
website:
invalid: 不是有效的 URL
invalid: 不是有效的網址
import:
attributes:
data:

View file

@ -29,7 +29,7 @@ zh-TW:
edit:
title: 編輯應用程式
form:
error: 唉呦!請看看表單以排查錯誤
error: 糟糕!請檢查表單以排查錯誤
help:
native_redirect_uri: 請使用 %{native_redirect_uri} 作本站測試
redirect_uri: 每行輸入一個 URI

View file

@ -534,6 +534,7 @@ et:
total_reported: Nende kohta teateid
total_storage: Lisatud meedia
totals_time_period_hint_html: Allpool kuvatud summad sisaldavad andmed kogu aja kohta.
unknown_instance: Hetkel pole selle domeeni jaoks siin serveris kirjet.
invites:
deactivate_all: Peata kõik
filter:
@ -610,6 +611,7 @@ et:
created_at: Teavitatud
delete_and_resolve: Kustuta postitused
forwarded: Edastatud
forwarded_replies_explanation: See aruanne pärineb kaugkasutajalt ja käsitleb kaugsisu. See on edastatud sulle, sest raporteeritud sisu on vastus ühele sinu kasutajale.
forwarded_to: Edastatud %{domain} domeeni
mark_as_resolved: Märgi lahendatuks
mark_as_sensitive: Märgi kui tundlik sisu
@ -832,6 +834,20 @@ et:
system_checks:
database_schema_check:
message_html: On ootel andmebaasi migreerimisi. Rakenduse ootuspäraseks toimimiseks palun käivita need
elasticsearch_health_red:
message_html: Elasticsearch klaster on ebaterve (punane staatus), otsingufunktsioonid ei ole saadaval
elasticsearch_health_yellow:
message_html: Elasticsearch klaster on ebaterve (kollane staatus), võiksid uurida põhjust
elasticsearch_index_mismatch:
message_html: Elasticsearchi indeksite kaardistused on vananenud. Palun käivita <code>tootctl search deploy --only=%{value}</code>
elasticsearch_preset:
action: Vaata dokumentatsiooni
message_html: Elasticsearchi klastris on rohkem kui üks sõlme, kuid Mastodon ei ole nende kasutamiseks konfigureeritud.
elasticsearch_preset_single_node:
action: Vaata dokumentatsiooni
message_html: Elasticsearchi klastris on ainult üks sõlm <code>ES_PRESET</code> peaks olema seatud väärtusele <code>single_node_cluster</code>.
elasticsearch_reset_chewy:
message_html: Elasticsearchi süsteemiindeks on seadistuse muutumise tõttu vananenud. Palun käivita <code>tootctl search deploy --reset-chewy</code> selle uuendamiseks.
elasticsearch_running_check:
message_html: Elasticsearch ei vasta. Kontrolli, kas see töötab või keela täistekstiotsing
elasticsearch_version_check:
@ -1024,6 +1040,14 @@ et:
hint_html: Üks asi veel! Me peame veenduma, et oled inimene (et me saaksime spämmi väljaspoole jätta!). Lahenda allpool olev CAPTCHA ja klõpsa "Jätka".
title: Turvalisuse kontroll
confirmations:
awaiting_review: E-posti aadress on kinnitatud! %{domain} töötajad vaatavad praegu registreeringut läbi. Saad e-kirja, kui nad kiidavad konto heaks!
awaiting_review_title: Su registreeringut vaadatakse läbi
clicking_this_link: klõpsates seda linki
login_link: logi sisse
proceed_to_login_html: Saad nüüd jätkata valikuga %{login_link}.
redirect_to_app_html: Sind oleks pidanud suunatama rakendusse <strong>%{app_name}</strong>. Kui seda ei juhtunud, proovi %{clicking_this_link} või naase käsitsi rakendusse.
registration_complete: Sinu registreering domeenil %{domain} on nüüd valmis!
welcome_title: Tere tulemast, %{name}!
wrong_email_hint: Kui see e-postiaadress pole korrektne, saad seda kontosätetes muuta.
delete_account: Konto kustutamine
delete_account_html: Kui soovid oma konto kustutada, siis <a href="%{path}">jätka siit</a>. Pead kustutamise eraldi kinnitama.
@ -1085,6 +1109,7 @@ et:
functional: Konto on täies mahus kasutatav.
pending: Taotlus ootab ülevaatamist meie personali poolt. See võib võtta mõne aja. Kui taotlus on vastu võetud, saadetakse sulle e-kiri.
redirecting_to: See konto pole aktiivne, sest on suunatud aadressile %{acct}.
self_destruct: Kuna %{domain} on sulgemisel, saad oma kontole vaid piiratud ligipääsu.
view_strikes: Vaata enda eelnevaid juhtumeid
too_fast: Vorm esitatud liiga kiirelt, proovi uuesti.
use_security_key: Kasuta turvavõtit
@ -1342,6 +1367,7 @@ et:
'86400': 1 päev
expires_in_prompt: Mitte kunagi
generate: Loo
invalid: See kutse pole kehtiv
invited_by: 'Sind kutsus:'
max_uses:
one: 1 kasutus
@ -1554,6 +1580,9 @@ et:
over_daily_limit: Lubatud ajastatud postituste arv %{limit} päevas on tänaseks ületatud
over_total_limit: Oled jõudnud ajastatud postituste lubatud maksimumarvuni %{limit}
too_soon: Ajastatud kuupäev peab olema tukevikus
self_destruct:
lead_html: Kahjuks suletakse <strong>%{domain}</strong> lõplikult. Kui sul oli seal konto, ei saa sa seda enam kasutada, kuid siiski võid taotleda oma andmete varukoopiat.
title: See server suletakse
sessions:
activity: Viimane aktiivsus
browser: Veebilehitseja

View file

@ -599,7 +599,7 @@ ja:
created_at: 通報日時
delete_and_resolve: 投稿を削除
forwarded: 転送済み
forwarded_replies_explanation: の報告はリモートユーザーからのものであり、またリモートコンテンツに関するものです。報告されたコンテンツがあなたのユーザーの内の一人に対するリプライの中にあるため、あなたに転送されました。
forwarded_replies_explanation: れはリモートユーザーによる、リモートコンテンツについての報告です。問題のコンテンツはあなたのサーバー利用者への返信なので、こちらにも転送されて来ました。
forwarded_to: "%{domain}に転送されました"
mark_as_resolved: 解決済みとしてマーク
mark_as_sensitive: 閲覧注意にする

View file

@ -16,7 +16,7 @@ zh-TW:
acct: 指定要移動至的帳號的「使用者名稱@網域名稱」
account_warning_preset:
text: 您可使用嘟文語法,例如網址、「#」標籤與提及功能
title: 可選。不會向收件者顯示
title: 可選。不會向收件者顯示
admin_account_action:
include_statuses: 使用者可看到導致檢舉或警告的嘟文
send_email_notification: 使用者將收到帳號發生之事情的解釋

View file

@ -467,7 +467,7 @@ zh-TW:
other: 錯誤嘗試於 %{count} 天。
no_failures_recorded: 報告中沒有錯誤。
title: 可用狀態
warning: 上一次嘗試連線至伺服器失敗
warning: 上一次嘗試連線至伺服器失敗
back_to_all: 所有
back_to_limited: 受限制的
back_to_warning: 警告
@ -876,7 +876,7 @@ zh-TW:
publishers:
no_publisher_selected: 因未選取任何發行者,所以什麼事都沒發生
shared_by_over_week:
other: 上週被 %{count} 使用者分享
other: 上週被 %{count} 使用者分享
title: 熱門連結
usage_comparison: 於今日被 %{today} 人分享,相較於昨日 %{yesterday} 人
not_allowed_to_trend: 不允許登上熱門
@ -1273,7 +1273,7 @@ zh-TW:
other: 選取 %{count} 個符合您搜尋的項目。
today: 今天
validation_errors:
other: 恩...似乎不太對勁耶?請檢查以下 %{count} 項錯誤
other: 恩...似乎發生了點錯誤?請檢查以下 %{count} 項錯誤
imports:
errors:
empty: 空的 CSV 檔案
@ -1796,7 +1796,7 @@ zh-TW:
welcome:
edit_profile_action: 設定個人檔案
edit_profile_step: 您可以設定您的個人檔案,包括上傳大頭貼、變更顯示名稱等等。您也可以選擇於新的跟隨者跟隨前,先對他們進行審核。
explanation: 下面是幾個小幫助,希望它們能幫到您
explanation: 以下是幾個小技巧,希望它們能幫到您
final_action: 開始嘟嘟
final_step: '開始嘟嘟吧!即使您現在沒有跟隨者,其他人仍然能於本站時間軸、主題標籤等地方,看到您的公開嘟文。試著用 #introductions 這個主題標籤介紹一下自己吧。'
full_handle: 您的完整帳號名稱
@ -1805,7 +1805,7 @@ zh-TW:
title: "%{name} 誠摯歡迎您的加入!"
users:
follow_limit_reached: 您無法跟隨多於 %{limit} 個人
go_to_sso_account_settings: 前往您的身分提供商 (identity provider) 之帳號設定
go_to_sso_account_settings: 前往您的身分識別提供者IdP之帳號設定
invalid_otp_token: 兩階段認證碼不正確
otp_lost_help_html: 如果您無法存取這兩者,您可以透過 %{email} 與我們聯繫
seamless_external_login: 由於您是由外部系統登入,所以不能設定密碼與電子郵件。

View file

@ -51,7 +51,7 @@ Rails.application.routes.draw do
get 'health', to: 'health#show'
authenticate :user, lambda { |u| u.role&.can?(:view_devops) } do
authenticate :user, ->(user) { user.role&.can?(:view_devops) } do
mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq
mount PgHero::Engine, at: 'pghero', as: :pghero
end
@ -105,10 +105,10 @@ Rails.application.routes.draw do
}
# rubocop:disable Style/FormatStringToken - those do not go through the usual formatting functions and are not safe to correct
get '/users/:username', to: redirect_with_vary('/@%{username}'), constraints: lambda { |req| req.format.nil? || req.format.html? }
get '/users/:username/following', to: redirect_with_vary('/@%{username}/following'), constraints: lambda { |req| req.format.nil? || req.format.html? }
get '/users/:username/followers', to: redirect_with_vary('/@%{username}/followers'), constraints: lambda { |req| req.format.nil? || req.format.html? }
get '/users/:username/statuses/:id', to: redirect_with_vary('/@%{username}/%{id}'), constraints: lambda { |req| req.format.nil? || req.format.html? }
get '/users/:username', to: redirect_with_vary('/@%{username}'), constraints: ->(req) { req.format.nil? || req.format.html? }
get '/users/:username/following', to: redirect_with_vary('/@%{username}/following'), constraints: ->(req) { req.format.nil? || req.format.html? }
get '/users/:username/followers', to: redirect_with_vary('/@%{username}/followers'), constraints: ->(req) { req.format.nil? || req.format.html? }
get '/users/:username/statuses/:id', to: redirect_with_vary('/@%{username}/%{id}'), constraints: ->(req) { req.format.nil? || req.format.html? }
# rubocop:enable Style/FormatStringToken
get '/authorize_follow', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }

View file

@ -80,7 +80,7 @@ class MoveUserSettings < ActiveRecord::Migration[6.1]
end
end
user.update_column('settings', Oj.dump(user_settings)) # rubocop:disable Rails/SkipsModelValidations
user.update_column('settings', Oj.dump(user_settings))
end
end
end

View file

@ -185,15 +185,15 @@ module Mastodon::CLI
end
def schema_has_instances_view?
ActiveRecord::Migrator.current_version >= 2020_12_06_004238
migrator_version >= 2020_12_06_004238
end
def verify_schema_version!
if ActiveRecord::Migrator.current_version < MIN_SUPPORTED_VERSION
if migrator_version < MIN_SUPPORTED_VERSION
say 'Your version of the database schema is too old and is not supported by this script.', :red
say 'Please update to at least Mastodon 3.0.0 before running this script.', :red
exit(1)
elsif ActiveRecord::Migrator.current_version > MAX_SUPPORTED_VERSION
elsif migrator_version > MAX_SUPPORTED_VERSION
say 'Your version of the database schema is more recent than this script, this may cause unexpected errors.', :yellow
exit(1) unless yes?('Continue anyway? (Yes/No)')
end
@ -228,7 +228,7 @@ module Mastodon::CLI
end
say 'Restoring index_accounts_on_username_and_domain_lower…'
if ActiveRecord::Migrator.current_version < 2020_06_20_164023
if migrator_version < 2020_06_20_164023
ActiveRecord::Base.connection.add_index :accounts, 'lower (username), lower(domain)', name: 'index_accounts_on_username_and_domain_lower', unique: true
else
ActiveRecord::Base.connection.add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true
@ -238,7 +238,7 @@ module Mastodon::CLI
ActiveRecord::Base.connection.execute('REINDEX INDEX search_index;')
ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_uri;')
ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_url;')
ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_domain_and_id;') if ActiveRecord::Migrator.current_version >= 2023_05_24_190515
ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_domain_and_id;') if migrator_version >= 2023_05_24_190515
end
def deduplicate_users!
@ -254,7 +254,7 @@ module Mastodon::CLI
users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse
ref_user = users.shift
say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow
say "e-mail will be disabled for the following accounts: #{user.map { |user| user.account.acct }.join(', ')}", :yellow
say "e-mail will be disabled for the following accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow
say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow
users.each_with_index do |user, index|
@ -269,15 +269,15 @@ module Mastodon::CLI
say 'Restoring users indexes…'
ActiveRecord::Base.connection.add_index :users, ['confirmation_token'], name: 'index_users_on_confirmation_token', unique: true
ActiveRecord::Base.connection.add_index :users, ['email'], name: 'index_users_on_email', unique: true
ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if ActiveRecord::Migrator.current_version < 2022_01_18_183010
ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if migrator_version < 2022_01_18_183010
if ActiveRecord::Migrator.current_version < 2022_03_10_060641
if migrator_version < 2022_03_10_060641
ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true
else
ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true, where: 'reset_password_token IS NOT NULL', opclass: :text_pattern_ops
end
ActiveRecord::Base.connection.execute('REINDEX INDEX index_users_on_unconfirmed_email;') if ActiveRecord::Migrator.current_version >= 2023_07_02_151753
ActiveRecord::Base.connection.execute('REINDEX INDEX index_users_on_unconfirmed_email;') if migrator_version >= 2023_07_02_151753
end
def deduplicate_users_process_confirmation_token
@ -292,7 +292,7 @@ module Mastodon::CLI
end
def deduplicate_users_process_remember_token
if ActiveRecord::Migrator.current_version < 2022_01_18_183010
if migrator_version < 2022_01_18_183010
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE remember_token IS NOT NULL GROUP BY remember_token HAVING count(*) > 1").each do |row|
users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1)
say "Unsetting remember token for those accounts: #{users.map { |user| user.account.acct }.join(', ')}", :yellow
@ -371,7 +371,7 @@ module Mastodon::CLI
end
say 'Restoring conversations indexes…'
if ActiveRecord::Migrator.current_version < 2022_03_07_083603
if migrator_version < 2022_03_07_083603
ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true
else
ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops
@ -488,7 +488,7 @@ module Mastodon::CLI
end
say 'Restoring media_attachments indexes…'
if ActiveRecord::Migrator.current_version < 2022_03_10_060626
if migrator_version < 2022_03_10_060626
ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true
else
ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true, where: 'shortcode IS NOT NULL', opclass: :text_pattern_ops
@ -521,7 +521,7 @@ module Mastodon::CLI
end
say 'Restoring statuses indexes…'
if ActiveRecord::Migrator.current_version < 2022_03_10_060706
if migrator_version < 2022_03_10_060706
ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true
else
ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops
@ -543,7 +543,7 @@ module Mastodon::CLI
end
say 'Restoring tags indexes…'
if ActiveRecord::Migrator.current_version < 2021_04_21_121431
if migrator_version < 2021_04_21_121431
ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true
else
ActiveRecord::Base.connection.execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)'
@ -707,6 +707,10 @@ module Mastodon::CLI
end
end
def migrator_version
ActiveRecord::Migrator.current_version
end
def find_duplicate_accounts
ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM accounts GROUP BY lower(username), COALESCE(lower(domain), '') HAVING count(*) > 1")
end

View file

@ -42,7 +42,13 @@ module Mastodon::CLI
pool = Concurrent::FixedThreadPool.new(options[:concurrency], max_queue: options[:concurrency] * 10)
importers = indices.index_with { |index| "Importer::#{index.name}Importer".constantize.new(batch_size: options[:batch_size], executor: pool) }
progress = ProgressBar.create(total: nil, format: '%t%c/%u |%b%i| %e (%r docs/s)', autofinish: false)
progress = ProgressBar.create(
{
total: nil,
format: '%t%c/%u |%b%i| %e (%r docs/s)',
autofinish: false,
}.merge(progress_output_options)
)
Chewy::Stash::Specification.reset! if options[:reset_chewy]
@ -116,5 +122,9 @@ module Mastodon::CLI
say('Cannot run with this batch_size setting, must be at least 1', :red)
exit(1)
end
def progress_output_options
Rails.env.test? ? { output: ProgressBar::Outputs::Null } : {}
end
end
end

View file

@ -201,8 +201,8 @@
"lint-staged": "^15.0.0",
"prettier": "^3.0.0",
"react-test-renderer": "^18.2.0",
"stylelint": "^15.10.1",
"stylelint-config-standard-scss": "^11.0.0",
"stylelint": "^16.0.2",
"stylelint-config-standard-scss": "^12.0.0",
"typescript": "^5.0.4",
"webpack-dev-server": "^3.11.3",
"yargs": "^17.7.2"

View file

@ -123,7 +123,7 @@ RSpec.describe ActivityPub::RepliesController do
end
it 'uses ids for remote toots' do
remote_replies = page_json[:items].select { |x| !x.is_a?(Hash) }
remote_replies = page_json[:items].reject { |x| x.is_a?(Hash) }
expect(remote_replies.all? { |item| item.is_a?(String) && !ActivityPub::TagManager.instance.local_uri?(item) }).to be true
end

View file

@ -5,7 +5,7 @@ require 'rails_helper'
describe EmojisController do
render_views
let(:emoji) { Fabricate(:custom_emoji) }
let(:emoji) { Fabricate(:custom_emoji, shortcode: 'coolcat') }
describe 'GET #show' do
let(:response) { get :show, params: { id: emoji.id, format: :json } }

View file

@ -194,60 +194,49 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
add_webauthn_credential(user)
end
context 'when creation succeeds' do
it 'adds a new credential to user credentials and does not change webauthn_id', :aggregate_failures do
controller.session[:webauthn_challenge] = challenge
expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to change { user.webauthn_credentials.count }.by(1)
.and not_change(user, :webauthn_id)
expect(response).to have_http_status(200)
end
end
context 'when the nickname is already used' do
it 'fails' do
controller.session[:webauthn_challenge] = challenge
post :create, params: { credential: new_webauthn_credential, nickname: 'USB Key' }
expect(response).to have_http_status(422)
expect(flash[:error]).to be_present
end
end
context 'when the credential already exists' do
before do
user2 = Fabricate(:user)
public_key_credential = WebAuthn::Credential.from_create(new_webauthn_credential)
Fabricate(:webauthn_credential,
user_id: user2.id,
external_id: public_key_credential.id,
public_key: public_key_credential.public_key)
end
it 'fails' do
controller.session[:webauthn_challenge] = challenge
it 'adds a new credential to user credentials and does not change webauthn_id when creation succeeds', :aggregate_failures do
controller.session[:webauthn_challenge] = challenge
expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to change { user.webauthn_credentials.count }.by(1)
.and not_change(user, :webauthn_id)
expect(response).to have_http_status(422)
expect(flash[:error]).to be_present
end
expect(response).to have_http_status(200)
end
it 'fails when the nickname is already used' do
controller.session[:webauthn_challenge] = challenge
post :create, params: { credential: new_webauthn_credential, nickname: 'USB Key' }
expect(response).to have_http_status(422)
expect(flash[:error]).to be_present
end
it 'fails when the credential already exists' do
public_key_credential = WebAuthn::Credential.from_create(new_webauthn_credential)
Fabricate(:webauthn_credential,
user_id: Fabricate(:user).id,
external_id: public_key_credential.id,
public_key: public_key_credential.public_key)
controller.session[:webauthn_challenge] = challenge
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
expect(response).to have_http_status(422)
expect(flash[:error]).to be_present
end
end
context 'when user have not enabled webauthn' do
context 'when creation succeeds' do
it 'creates a webauthn credential' do
controller.session[:webauthn_challenge] = challenge
context 'when user have not enabled webauthn and creation succeeds' do
it 'creates a webauthn credential' do
controller.session[:webauthn_challenge] = challenge
expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to change { user.webauthn_credentials.count }.by(1)
end
expect do
post :create, params: { credential: new_webauthn_credential, nickname: nickname }
end.to change { user.webauthn_credentials.count }.by(1)
end
end
end
@ -292,15 +281,13 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
add_webauthn_credential(user)
end
context 'when deletion succeeds' do
it 'redirects to 2FA methods list and shows flash success and deletes the credential', :aggregate_failures do
expect do
delete :destroy, params: { id: user.webauthn_credentials.take.id }
end.to change { user.webauthn_credentials.count }.by(-1)
it 'redirects to 2FA methods list and shows flash success and deletes the credential when deletion succeeds', :aggregate_failures do
expect do
delete :destroy, params: { id: user.webauthn_credentials.take.id }
end.to change { user.webauthn_credentials.count }.by(-1)
expect(response).to redirect_to settings_two_factor_authentication_methods_path
expect(flash[:success]).to be_present
end
expect(response).to redirect_to settings_two_factor_authentication_methods_path
expect(flash[:success]).to be_present
end
end

View file

@ -0,0 +1,6 @@
# frozen_string_literal: true
Fabricator(:announcement_mute) do
announcement { Fabricate.build(:announcement) }
account { Fabricate.build(:account) }
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
Fabricator(:announcement_reaction) do
account { Fabricate.build(:account) }
announcement { Fabricate.build(:announcement) }
name { Fabricate(:custom_emoji).shortcode }
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
Fabricator(:custom_emoji) do
shortcode 'coolcat'
shortcode { sequence(:shortcode) { |i| "code_#{i}" } }
domain nil
image { Rails.root.join('spec', 'fixtures', 'files', 'emojo.png').open }
end

View file

@ -77,7 +77,8 @@ describe Mastodon::CLI::Accounts do
it_behaves_like 'a new user with given email address and username'
it 'creates a new user with confirmed status' do
subject
expect { subject }
.to output_results('New password')
user = User.find_by(email: options[:email])
@ -95,7 +96,8 @@ describe Mastodon::CLI::Accounts do
it_behaves_like 'a new user with given email address and username'
it 'creates a new user with approved status' do
subject
expect { subject }
.to output_results('New password')
user = User.find_by(email: options[:email])
@ -111,7 +113,8 @@ describe Mastodon::CLI::Accounts do
it_behaves_like 'a new user with given email address and username'
it 'creates a new user and assigns the specified role' do
subject
expect { subject }
.to output_results('New password')
role = User.find_by(email: options[:email])&.role
@ -148,7 +151,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: 'tootctl_new@example.com', reattach: true, force: true } }
it 'reattaches the account to the new user and deletes the previous user' do
subject
expect { subject }
.to output_results('New password')
user = Account.find_local('tootctl_username')&.user
@ -220,7 +224,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { role: default_role.name } }
it "updates the user's role to the specified role" do
subject
expect { subject }
.to output_results('OK')
role = user.reload.role
@ -235,7 +240,8 @@ describe Mastodon::CLI::Accounts do
let(:user) { Fabricate(:user, role: role) }
it "removes the user's role successfully" do
subject
expect { subject }
.to output_results('OK')
role = user.reload.role
@ -248,13 +254,15 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: 'new_email@email.com' } }
it "sets the user's unconfirmed email to the provided email address" do
subject
expect { subject }
.to output_results('OK')
expect(user.reload.unconfirmed_email).to eq(options[:email])
end
it "does not update the user's original email address" do
subject
expect { subject }
.to output_results('OK')
expect(user.reload.email).to eq('old_email@email.com')
end
@ -264,13 +272,15 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: 'new_email@email.com', confirm: true } }
it "updates the user's email address to the provided email" do
subject
expect { subject }
.to output_results('OK')
expect(user.reload.email).to eq(options[:email])
end
it "sets the user's email address as confirmed" do
subject
expect { subject }
.to output_results('OK')
expect(user.reload.confirmed?).to be(true)
end
@ -282,7 +292,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { confirm: true } }
it "confirms the user's email address" do
subject
expect { subject }
.to output_results('OK')
expect(user.reload.confirmed?).to be(true)
end
@ -297,7 +308,9 @@ describe Mastodon::CLI::Accounts do
end
it 'approves the user' do
expect { subject }.to change { user.reload.approved }.from(false).to(true)
expect { subject }
.to output_results('OK')
.and change { user.reload.approved }.from(false).to(true)
end
end
@ -306,7 +319,9 @@ describe Mastodon::CLI::Accounts do
let(:options) { { disable: true } }
it 'disables the user' do
expect { subject }.to change { user.reload.disabled }.from(false).to(true)
expect { subject }
.to output_results('OK')
.and change { user.reload.disabled }.from(false).to(true)
end
end
@ -315,7 +330,9 @@ describe Mastodon::CLI::Accounts do
let(:options) { { enable: true } }
it 'enables the user' do
expect { subject }.to change { user.reload.disabled }.from(true).to(false)
expect { subject }
.to output_results('OK')
.and change { user.reload.disabled }.from(true).to(false)
end
end
@ -335,7 +352,9 @@ describe Mastodon::CLI::Accounts do
let(:options) { { disable_2fa: true } }
it 'disables the two-factor authentication for the user' do
expect { subject }.to change { user.reload.otp_required_for_login }.from(true).to(false)
expect { subject }
.to output_results('OK')
.and change { user.reload.otp_required_for_login }.from(true).to(false)
end
end
@ -385,7 +404,8 @@ describe Mastodon::CLI::Accounts do
let(:arguments) { [account.username] }
it 'deletes the specified user successfully' do
subject
expect { subject }
.to output_results('Deleting')
expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
end
@ -415,7 +435,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { email: account.user.email } }
it 'deletes the specified user successfully' do
subject
expect { subject }
.to output_results('Deleting')
expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
end
@ -457,7 +478,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { all: true } }
it 'approves all pending registrations' do
subject
expect { subject }
.to output_results('OK')
expect(User.pluck(:approved).all?(true)).to be(true)
end
@ -468,7 +490,8 @@ describe Mastodon::CLI::Accounts do
let(:options) { { number: 2 } }
it 'approves the earliest n pending registrations but not the remaining ones' do
subject
expect { subject }
.to output_results('OK')
expect(n_earliest_pending_registrations.all?(&:approved?)).to be(true)
expect(pending_registrations.all?(&:approved?)).to be(false)
@ -498,7 +521,7 @@ describe Mastodon::CLI::Accounts do
it 'approves all users and does not raise any error' do
expect { subject }
.to_not raise_error
.to output_results('OK')
expect(User.pluck(:approved).all?(true)).to be(true)
end
end
@ -510,7 +533,8 @@ describe Mastodon::CLI::Accounts do
let(:arguments) { [user.account.username] }
it 'approves the specified user successfully' do
subject
expect { subject }
.to output_results('OK')
expect(user.reload.approved?).to be(true)
end
@ -655,7 +679,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_avatar!)
allow(account_example_net).to receive(:reset_avatar!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(remote_account_example_com).to have_received(:reset_avatar!).once
@ -665,7 +690,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh avatar for local accounts' do
allow(local_account).to receive(:reset_avatar!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_avatar!)
@ -675,7 +701,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_header!)
allow(account_example_net).to receive(:reset_header!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(remote_account_example_com).to have_received(:reset_header!).once
@ -685,7 +712,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh the header for local accounts' do
allow(local_account).to receive(:reset_header!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_header!)
@ -706,7 +734,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_avatar!)
allow(account_example_net).to receive(:reset_avatar!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_avatar!)
@ -719,7 +748,8 @@ describe Mastodon::CLI::Accounts do
allow(remote_account_example_com).to receive(:reset_header!)
allow(account_example_net).to receive(:reset_header!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(local_account).to_not have_received(:reset_header!)
@ -752,7 +782,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_avatar!)
allow(account_example_com_b).to receive(:reset_avatar!)
cli.refresh(*arguments)
expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_com_a).to have_received(:reset_avatar!).once
expect(account_example_com_b).to have_received(:reset_avatar!).once
@ -761,7 +792,8 @@ describe Mastodon::CLI::Accounts do
it 'does not reset the avatar for unspecified accounts' do
allow(account_example_net).to receive(:reset_avatar!)
cli.refresh(*arguments)
expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_net).to_not have_received(:reset_avatar!)
end
@ -770,7 +802,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_header!)
allow(account_example_com_b).to receive(:reset_header!)
cli.refresh(*arguments)
expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_com_a).to have_received(:reset_header!).once
expect(account_example_com_b).to have_received(:reset_header!).once
@ -779,7 +812,8 @@ describe Mastodon::CLI::Accounts do
it 'does not reset the header for unspecified accounts' do
allow(account_example_net).to receive(:reset_header!)
cli.refresh(*arguments)
expect { cli.refresh(*arguments) }
.to output_results('OK')
expect(account_example_net).to_not have_received(:reset_header!)
end
@ -812,7 +846,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_avatar!)
allow(account_example_com_b).to receive(:reset_avatar!)
cli.refresh(*arguments)
expect { cli.refresh(*arguments) }
.to output_results('OK (DRY RUN)')
expect(account_example_com_a).to_not have_received(:reset_avatar!)
expect(account_example_com_b).to_not have_received(:reset_avatar!)
@ -822,7 +857,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_header!)
allow(account_example_com_b).to receive(:reset_header!)
cli.refresh(*arguments)
expect { cli.refresh(*arguments) }
.to output_results('OK (DRY RUN)')
expect(account_example_com_a).to_not have_received(:reset_header!)
expect(account_example_com_b).to_not have_received(:reset_header!)
@ -848,7 +884,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_avatar!)
allow(account_example_com_b).to receive(:reset_avatar!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(account_example_com_a).to have_received(:reset_avatar!).once
@ -858,7 +895,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh the avatar for accounts outside specified domain' do
allow(account_example_net).to receive(:reset_avatar!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(account_example_net).to_not have_received(:reset_avatar!)
@ -868,7 +906,8 @@ describe Mastodon::CLI::Accounts do
allow(account_example_com_a).to receive(:reset_header!)
allow(account_example_com_b).to receive(:reset_header!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope)
expect(account_example_com_a).to have_received(:reset_header!).once
@ -878,7 +917,8 @@ describe Mastodon::CLI::Accounts do
it 'does not refresh the header for accounts outside specified domain' do
allow(account_example_net).to receive(:reset_header!)
cli.refresh
expect { cli.refresh }
.to output_results('Refreshed 2 accounts')
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(account_example_net).to_not have_received(:reset_header!)
@ -913,7 +953,8 @@ describe Mastodon::CLI::Accounts do
old_private_key = account.private_key
old_public_key = account.public_key
subject
expect { subject }
.to output_results('OK')
account.reload
expect(account.private_key).to_not eq(old_private_key)
@ -923,7 +964,8 @@ describe Mastodon::CLI::Accounts do
it 'broadcasts the new keys for the specified account' do
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
subject
expect { subject }
.to output_results('OK')
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
end
@ -947,7 +989,8 @@ describe Mastodon::CLI::Accounts do
old_private_keys = accounts.map(&:private_key)
old_public_keys = accounts.map(&:public_key)
subject
expect { subject }
.to output_results('rotated')
accounts.each(&:reload)
expect(accounts.map(&:private_key)).to_not eq(old_private_keys)
@ -957,7 +1000,8 @@ describe Mastodon::CLI::Accounts do
it 'broadcasts the new keys for each account' do
allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
subject
expect { subject }
.to output_results('rotated')
accounts.each do |account|
expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
@ -1036,7 +1080,8 @@ describe Mastodon::CLI::Accounts do
end
it 'merges `from_account` into `to_account` and deletes `from_account`' do
subject
expect { subject }
.to output_results('OK')
expect(to_account).to have_received(:merge_with!).with(from_account).once
expect(from_account).to have_received(:destroy).once
@ -1059,7 +1104,8 @@ describe Mastodon::CLI::Accounts do
end
it 'merges "from_account" into "to_account" and deletes from_account' do
subject
expect { subject }
.to output_results('OK')
expect(to_account).to have_received(:merge_with!).with(from_account).once
expect(from_account).to have_received(:destroy)
@ -1339,7 +1385,8 @@ describe Mastodon::CLI::Accounts do
shared_examples 'a successful migration' do
it 'calls the MoveService for the last migration' do
subject
expect { subject }
.to output_results('OK')
last_migration = source_account.migrations.last
@ -1449,7 +1496,8 @@ describe Mastodon::CLI::Accounts do
end
it 'creates a migration for the specified account with the target account' do
subject
expect { subject }
.to output_results('migrated')
last_migration = source_account.migrations.last

View file

@ -78,7 +78,8 @@ describe Mastodon::CLI::IpBlocks do
it 'overwrites the existing IP block record' do
expect { subject }
.to change { blocked_ip.reload.severity }
.to output_results('Added 11')
.and change { blocked_ip.reload.severity }
.from('no_access')
.to('sign_up_requires_approval')
end
@ -189,7 +190,8 @@ describe Mastodon::CLI::IpBlocks do
let(:options) { { force: true } }
it 'removes blocks for IP ranges that cover given IP(s) and keeps other ranges' do
subject
expect { subject }
.to output_results('Removed 2')
expect(covered_ranges).to_not exist
expect(other_ranges).to exist

View file

@ -41,7 +41,8 @@ describe Mastodon::CLI::Settings do
it 'changes registrations_mode and require_invite_text' do
expect { subject }
.to change(Setting, :registrations_mode).from(nil).to('approved')
.to output_results('OK')
.and change(Setting, :registrations_mode).from(nil).to('approved')
.and change(Setting, :require_invite_text).from(false).to(true)
end
end

View file

@ -0,0 +1,210 @@
# frozen_string_literal: true
require 'rails_helper'
describe Announcement do
describe 'Scopes' do
context 'with published and unpublished records' do
let!(:published) { Fabricate(:announcement, published: true) }
let!(:unpublished) { Fabricate(:announcement, published: false, scheduled_at: 10.days.from_now) }
describe '#unpublished' do
it 'returns records with published false' do
results = described_class.unpublished
expect(results).to eq([unpublished])
end
end
describe '#published' do
it 'returns records with published true' do
results = described_class.published
expect(results).to eq([published])
end
end
end
describe '#without_muted' do
let!(:announcement) { Fabricate(:announcement) }
let(:account) { Fabricate(:account) }
let(:muted_announcement) { Fabricate(:announcement) }
before do
Fabricate(:announcement_mute, account: account, announcement: muted_announcement)
end
it 'returns the announcements not muted by the account' do
results = described_class.without_muted(account)
expect(results).to include(announcement)
expect(results).to_not include(muted_announcement)
end
end
context 'with timestamped announcements' do
let!(:adam_announcement) { Fabricate(:announcement, starts_at: 100.days.ago, scheduled_at: 10.days.ago, published_at: 10.days.ago, ends_at: 5.days.from_now) }
let!(:brenda_announcement) { Fabricate(:announcement, starts_at: 10.days.ago, scheduled_at: 100.days.ago, published_at: 10.days.ago, ends_at: 5.days.from_now) }
let!(:clara_announcement) { Fabricate(:announcement, starts_at: 10.days.ago, scheduled_at: 10.days.ago, published_at: 100.days.ago, ends_at: 5.days.from_now) }
let!(:darnelle_announcement) { Fabricate(:announcement, starts_at: 10.days.ago, scheduled_at: 10.days.ago, published_at: 10.days.ago, ends_at: 5.days.from_now, created_at: 100.days.ago) }
describe '#chronological' do
it 'orders the records correctly' do
results = described_class.chronological
expect(results).to eq(
[
adam_announcement,
brenda_announcement,
clara_announcement,
darnelle_announcement,
]
)
end
end
describe '#reverse_chronological' do
it 'orders the records correctly' do
results = described_class.reverse_chronological
expect(results).to eq(
[
darnelle_announcement,
clara_announcement,
brenda_announcement,
adam_announcement,
]
)
end
end
end
end
describe 'Validations' do
describe 'text' do
it 'validates presence of attribute' do
record = Fabricate.build(:announcement, text: nil)
expect(record).to_not be_valid
expect(record.errors[:text]).to be_present
end
end
describe 'ends_at' do
it 'validates presence when starts_at is present' do
record = Fabricate.build(:announcement, starts_at: 1.day.ago)
expect(record).to_not be_valid
expect(record.errors[:ends_at]).to be_present
end
it 'does not validate presence when starts_at is missing' do
record = Fabricate.build(:announcement, starts_at: nil)
expect(record).to be_valid
expect(record.errors[:ends_at]).to_not be_present
end
end
end
describe '#publish!' do
it 'publishes an unpublished record' do
announcement = Fabricate(:announcement, published: false, scheduled_at: 10.days.from_now)
announcement.publish!
expect(announcement).to be_published
expect(announcement.published_at).to_not be_nil
expect(announcement.scheduled_at).to be_nil
end
end
describe '#unpublish!' do
it 'unpublishes a published record' do
announcement = Fabricate(:announcement, published: true)
announcement.unpublish!
expect(announcement).to_not be_published
expect(announcement.scheduled_at).to be_nil
end
end
describe '#time_range?' do
it 'returns false when starts_at and ends_at are missing' do
record = Fabricate.build(:announcement, starts_at: nil, ends_at: nil)
expect(record.time_range?).to be(false)
end
it 'returns false when starts_at is present and ends_at is missing' do
record = Fabricate.build(:announcement, starts_at: 5.days.from_now, ends_at: nil)
expect(record.time_range?).to be(false)
end
it 'returns false when starts_at is missing and ends_at is present' do
record = Fabricate.build(:announcement, starts_at: nil, ends_at: 5.days.from_now)
expect(record.time_range?).to be(false)
end
it 'returns true when starts_at and ends_at are present' do
record = Fabricate.build(:announcement, starts_at: 5.days.from_now, ends_at: 10.days.from_now)
expect(record.time_range?).to be(true)
end
end
describe '#reactions' do
context 'with announcement_reactions present' do
let!(:account) { Fabricate(:account) }
let!(:announcement) { Fabricate(:announcement) }
let!(:announcement_reaction) { Fabricate(:announcement_reaction, announcement: announcement, created_at: 10.days.ago) }
let!(:announcement_reaction_account) { Fabricate(:announcement_reaction, announcement: announcement, created_at: 5.days.ago, account: account) }
before do
Fabricate(:announcement_reaction)
end
it 'returns the announcement reactions for the announcement' do
results = announcement.reactions
expect(results.first.name).to eq(announcement_reaction.name)
expect(results.last.name).to eq(announcement_reaction_account.name)
end
it 'returns the announcement reactions for the announcement limited to account' do
results = announcement.reactions(account)
expect(results.first.name).to eq(announcement_reaction.name)
end
end
end
describe '#statuses' do
let(:announcement) { Fabricate(:announcement, status_ids: status_ids) }
context 'with empty status_ids' do
let(:status_ids) { nil }
it 'returns empty array' do
results = announcement.statuses
expect(results).to eq([])
end
end
context 'with relevant status_ids' do
let(:status) { Fabricate(:status, visibility: :public) }
let(:direct_status) { Fabricate(:status, visibility: :direct) }
let(:status_ids) { [status.id, direct_status.id] }
it 'returns public and unlisted statuses' do
results = announcement.statuses
expect(results).to include(status)
expect(results).to_not include(direct_status)
end
end
end
end

View file

@ -59,7 +59,7 @@ RSpec.describe CustomEmoji do
describe '.from_text' do
subject { described_class.from_text(text, nil) }
let!(:emojo) { Fabricate(:custom_emoji) }
let!(:emojo) { Fabricate(:custom_emoji, shortcode: 'coolcat') }
context 'with plain text' do
let(:text) { 'Hello :coolcat:' }

View file

@ -104,7 +104,6 @@ RSpec.configure do |config|
end
config.before :each, type: :cli do
stub_stdout
stub_reset_connection_pools
end
@ -163,14 +162,6 @@ def attachment_fixture(name)
Rails.root.join('spec', 'fixtures', 'files', name).open
end
def stub_stdout
# TODO: Is there a bettery way to:
# - Avoid CLI command output being printed out
# - Allow rspec to assert things against STDOUT
# - Avoid disabling stdout for other desirable output (deprecation warnings, for example)
allow($stdout).to receive(:write)
end
def stub_reset_connection_pools
# TODO: Is there a better way to correctly run specs without stubbing this?
# (Avoids reset_connection_pools! in test env)

View file

@ -9,7 +9,7 @@ describe 'Custom Emojis' do
describe 'GET /api/v1/custom_emojis' do
before do
Fabricate(:custom_emoji, domain: nil, disabled: false, visible_in_picker: true)
Fabricate(:custom_emoji, domain: nil, disabled: false, visible_in_picker: true, shortcode: 'coolcat')
end
context 'when logged out' do

View file

@ -2,17 +2,13 @@
require 'rails_helper'
describe Api::V1::DirectoriesController do
render_views
describe 'Directories API' do
let(:user) { Fabricate(:user, confirmed_at: nil) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'read:follows' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #show' do
describe 'GET /api/v1/directories' do
context 'with no params' do
before do
local_unconfirmed_account = Fabricate(
@ -58,27 +54,32 @@ describe Api::V1::DirectoriesController do
)
domain_blocked_account.create_account_stat!
Fabricate(:account_domain_block, account: user.account, domain: 'test.example')
local_discoverable_account.create_account_stat!
eligible_remote_account.create_account_stat!
end
it 'returns the local discoverable account and the remote discoverable account' do
local_discoverable_account = Fabricate(
let(:local_discoverable_account) do
Fabricate(
:account,
domain: nil,
user: Fabricate(:user, confirmed_at: 10.days.ago, approved: true),
discoverable: true,
username: 'local_discoverable'
)
local_discoverable_account.create_account_stat!
end
eligible_remote_account = Fabricate(
let(:eligible_remote_account) do
Fabricate(
:account,
domain: 'host.example',
discoverable: true,
username: 'eligible_remote'
)
eligible_remote_account.create_account_stat!
end
get :show
it 'returns the local discoverable account and the remote discoverable account' do
get '/api/v1/directory', headers: headers
expect(response).to have_http_status(200)
expect(body_as_json.size).to eq(2)
@ -87,14 +88,17 @@ describe Api::V1::DirectoriesController do
end
context 'when asking for local accounts only' do
it 'returns only the local accounts' do
user = Fabricate(:user, confirmed_at: 10.days.ago, approved: true)
local_account = Fabricate(:account, domain: nil, user: user)
remote_account = Fabricate(:account, domain: 'host.example')
let(:user) { Fabricate(:user, confirmed_at: 10.days.ago, approved: true) }
let(:local_account) { Fabricate(:account, domain: nil, user: user) }
let(:remote_account) { Fabricate(:account, domain: 'host.example') }
before do
local_account.create_account_stat!
remote_account.create_account_stat!
end
get :show, params: { local: '1' }
it 'returns only the local accounts' do
get '/api/v1/directory', headers: headers, params: { local: '1' }
expect(response).to have_http_status(200)
expect(body_as_json.size).to eq(1)
@ -108,7 +112,7 @@ describe Api::V1::DirectoriesController do
old_stat = Fabricate(:account_stat, last_status_at: 1.day.ago)
new_stat = Fabricate(:account_stat, last_status_at: 1.minute.ago)
get :show, params: { order: 'active' }
get '/api/v1/directory', headers: headers, params: { order: 'active' }
expect(response).to have_http_status(200)
expect(body_as_json.size).to eq(2)
@ -123,7 +127,7 @@ describe Api::V1::DirectoriesController do
travel_to 10.seconds.from_now
account_new = Fabricate(:account_stat).account
get :show, params: { order: 'new' }
get '/api/v1/directory', headers: headers, params: { order: 'new' }
expect(response).to have_http_status(200)
expect(body_as_json.size).to eq(2)

View file

@ -50,11 +50,11 @@ RSpec.describe DeleteAccountService, type: :service do
end
def delete_associated_target_records
change do
[
AccountPin.where(target_account: account),
].map(&:count)
end.from([1]).to([0])
change(account_pins_for_account, :count).from(1).to(0)
end
def account_pins_for_account
AccountPin.where(target_account: account)
end
def delete_associated_target_notifications
@ -100,28 +100,34 @@ RSpec.describe DeleteAccountService, type: :service do
it 'sends expected activities to followed and follower inboxes' do
subject
expect(a_request(:post, account.inbox_url).with(
body:
hash_including({
'type' => 'Reject',
'object' => hash_including({
'type' => 'Follow',
'actor' => account.uri,
'object' => ActivityPub::TagManager.instance.uri_for(local_follower),
}),
})
)).to have_been_made.once
expect(post_to_inbox_with_reject).to have_been_made.once
expect(post_to_inbox_with_undo).to have_been_made.once
end
expect(a_request(:post, account.inbox_url).with(
body: hash_including({
'type' => 'Undo',
'object' => hash_including({
'type' => 'Follow',
'actor' => ActivityPub::TagManager.instance.uri_for(local_follower),
'object' => account.uri,
}),
})
)).to have_been_made.once
def post_to_inbox_with_undo
a_request(:post, account.inbox_url).with(
body: hash_including({
'type' => 'Undo',
'object' => hash_including({
'type' => 'Follow',
'actor' => ActivityPub::TagManager.instance.uri_for(local_follower),
'object' => account.uri,
}),
})
)
end
def post_to_inbox_with_reject
a_request(:post, account.inbox_url).with(
body: hash_including({
'type' => 'Reject',
'object' => hash_including({
'type' => 'Follow',
'actor' => account.uri,
'object' => ActivityPub::TagManager.instance.uri_for(local_follower),
}),
})
)
end
end
end

View file

@ -6,11 +6,12 @@ RSpec.describe FanOutOnWriteService, type: :service do
subject { described_class.new }
let(:last_active_at) { Time.now.utc }
let(:status) { Fabricate(:status, account: alice, visibility: visibility, text: 'Hello @bob #hoge') }
let(:status) { Fabricate(:status, account: alice, visibility: visibility, text: 'Hello @bob @eve #hoge') }
let!(:alice) { Fabricate(:user, current_sign_in_at: last_active_at).account }
let!(:bob) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'bob' }).account }
let!(:tom) { Fabricate(:user, current_sign_in_at: last_active_at).account }
let!(:eve) { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'eve' }).account }
before do
bob.follow!(alice)
@ -109,5 +110,24 @@ RSpec.describe FanOutOnWriteService, type: :service do
expect(redis).to_not have_received(:publish).with('timeline:hashtag:hoge', anything)
expect(redis).to_not have_received(:publish).with('timeline:public', anything)
end
context 'when handling status updates', :sidekiq_fake do
before do
subject.call(status)
status.snapshot!(at_time: status.created_at, rate_limit: false)
status.update!(text: 'Hello @bob @eve #hoge (edited)')
status.snapshot!(account_id: status.account_id)
redis.set("subscribed:timeline:#{eve.id}:notifications", '1')
Sidekiq::Worker.clear_all
end
it 'pushes the update to mentioned users through the notifications streaming channel' do
subject.call(status, update: true)
expect(PushUpdateWorker).to have_enqueued_sidekiq_job(anything, status.id, "timeline:#{eve.id}:notifications", { 'update' => true })
end
end
end
end

422
yarn.lock
View file

@ -1539,7 +1539,7 @@ __metadata:
languageName: node
linkType: hard
"@csstools/css-parser-algorithms@npm:^2.3.1":
"@csstools/css-parser-algorithms@npm:^2.3.2":
version: 2.3.2
resolution: "@csstools/css-parser-algorithms@npm:2.3.2"
peerDependencies:
@ -1548,14 +1548,14 @@ __metadata:
languageName: node
linkType: hard
"@csstools/css-tokenizer@npm:^2.2.0":
"@csstools/css-tokenizer@npm:^2.2.1":
version: 2.2.1
resolution: "@csstools/css-tokenizer@npm:2.2.1"
checksum: 0c6901d291e99c567893846a47068057c2a28b3edc4219b6da589a530f55f51ddd4675f906f707b393bfe7a508ab2604bf3f75708f064db857bb277636bd5a44
languageName: node
linkType: hard
"@csstools/media-query-list-parser@npm:^2.1.4":
"@csstools/media-query-list-parser@npm:^2.1.5":
version: 2.1.5
resolution: "@csstools/media-query-list-parser@npm:2.1.5"
peerDependencies:
@ -2432,8 +2432,8 @@ __metadata:
sass-loader: "npm:^10.2.0"
stacktrace-js: "npm:^2.0.2"
stringz: "npm:^2.1.0"
stylelint: "npm:^15.10.1"
stylelint-config-standard-scss: "npm:^11.0.0"
stylelint: "npm:^16.0.2"
stylelint-config-standard-scss: "npm:^12.0.0"
substring-trie: "npm:^1.0.2"
terser-webpack-plugin: "npm:^4.2.3"
tesseract.js: "npm:^2.1.5"
@ -3252,13 +3252,6 @@ __metadata:
languageName: node
linkType: hard
"@types/minimist@npm:^1.2.2":
version: 1.2.4
resolution: "@types/minimist@npm:1.2.4"
checksum: 01403652c09de17b8c6d7d9959cb7a244deccf31e9e7a1a7011fba73fa2724c14fe935718e0fdc48dcd30403fd76a916cb991d4c0ddf229748ccc6c4920c3371
languageName: node
linkType: hard
"@types/node@npm:*":
version: 20.8.10
resolution: "@types/node@npm:20.8.10"
@ -3275,13 +3268,6 @@ __metadata:
languageName: node
linkType: hard
"@types/normalize-package-data@npm:^2.4.0":
version: 2.4.3
resolution: "@types/normalize-package-data@npm:2.4.3"
checksum: 9ad94568b53f65d0c7fffed61c74e4a7b8625b1ebbc549f1de25287c2d20e6bca9d9cdc5826e508c9d95e02a48ac69d0282121c300667071661f37090224416b
languageName: node
linkType: hard
"@types/npmlog@npm:^7.0.0":
version: 7.0.0
resolution: "@types/npmlog@npm:7.0.0"
@ -4491,13 +4477,6 @@ __metadata:
languageName: node
linkType: hard
"arrify@npm:^1.0.1":
version: 1.0.1
resolution: "arrify@npm:1.0.1"
checksum: c35c8d1a81bcd5474c0c57fe3f4bad1a4d46a5fa353cedcff7a54da315df60db71829e69104b859dff96c5d68af46bd2be259fe5e50dc6aa9df3b36bea0383ab
languageName: node
linkType: hard
"arrow-key-navigation@npm:^1.2.0":
version: 1.2.0
resolution: "arrow-key-navigation@npm:1.2.0"
@ -5371,18 +5350,6 @@ __metadata:
languageName: node
linkType: hard
"camelcase-keys@npm:^7.0.0":
version: 7.0.2
resolution: "camelcase-keys@npm:7.0.2"
dependencies:
camelcase: "npm:^6.3.0"
map-obj: "npm:^4.1.0"
quick-lru: "npm:^5.1.1"
type-fest: "npm:^1.2.1"
checksum: ae86a51168643e9e8a2f2c7bfa17850729979ec3dafc5253056a7d97931cbb0e3ef5b4185e59d54b7a56c54405dee2874b0c82033498d8626e512ff9034cb05c
languageName: node
linkType: hard
"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1":
version: 5.3.1
resolution: "camelcase@npm:5.3.1"
@ -5390,7 +5357,7 @@ __metadata:
languageName: node
linkType: hard
"camelcase@npm:^6.2.0, camelcase@npm:^6.3.0":
"camelcase@npm:^6.2.0":
version: 6.3.0
resolution: "camelcase@npm:6.3.0"
checksum: 0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710
@ -5987,15 +5954,20 @@ __metadata:
languageName: node
linkType: hard
"cosmiconfig@npm:^8.2.0":
version: 8.2.0
resolution: "cosmiconfig@npm:8.2.0"
"cosmiconfig@npm:^9.0.0":
version: 9.0.0
resolution: "cosmiconfig@npm:9.0.0"
dependencies:
import-fresh: "npm:^3.2.1"
env-paths: "npm:^2.2.1"
import-fresh: "npm:^3.3.0"
js-yaml: "npm:^4.1.0"
parse-json: "npm:^5.0.0"
path-type: "npm:^4.0.0"
checksum: 4180aa6d1881b75ba591b2fc04b022741a3a4b67e9e243c0eb8d169b6e1efbd3cdf7e8ca19243c0f2e53a9d59ac3eccd5cad5f95f487fcbf4e740f9e86745747
parse-json: "npm:^5.2.0"
peerDependencies:
typescript: ">=4.9.5"
peerDependenciesMeta:
typescript:
optional: true
checksum: 1c1703be4f02a250b1d6ca3267e408ce16abfe8364193891afc94c2d5c060b69611fdc8d97af74b7e6d5d1aac0ab2fb94d6b079573146bc2d756c2484ce5f0ee
languageName: node
linkType: hard
@ -6435,30 +6407,13 @@ __metadata:
languageName: node
linkType: hard
"decamelize-keys@npm:^1.1.0":
version: 1.1.1
resolution: "decamelize-keys@npm:1.1.1"
dependencies:
decamelize: "npm:^1.1.0"
map-obj: "npm:^1.0.0"
checksum: 4ca385933127437658338c65fb9aead5f21b28d3dd3ccd7956eb29aab0953b5d3c047fbc207111672220c71ecf7a4d34f36c92851b7bbde6fca1a02c541bdd7d
languageName: node
linkType: hard
"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0":
"decamelize@npm:^1.2.0":
version: 1.2.0
resolution: "decamelize@npm:1.2.0"
checksum: 85c39fe8fbf0482d4a1e224ef0119db5c1897f8503bcef8b826adff7a1b11414972f6fef2d7dec2ee0b4be3863cf64ac1439137ae9e6af23a3d8dcbe26a5b4b2
languageName: node
linkType: hard
"decamelize@npm:^5.0.0":
version: 5.0.1
resolution: "decamelize@npm:5.0.1"
checksum: 3da71022bc1e85487810fa0833138effb599fa331ca21e179650e93a765d0c4dabeb1ecdd6ad1474fa0bacd2457953c63ea335afb6e53b35f2b4bf779514e2a3
languageName: node
linkType: hard
"decimal.js@npm:^10.4.2, decimal.js@npm:^10.4.3":
version: 10.4.3
resolution: "decimal.js@npm:10.4.3"
@ -7093,7 +7048,7 @@ __metadata:
languageName: node
linkType: hard
"env-paths@npm:^2.2.0":
"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
checksum: 285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4
@ -7941,6 +7896,19 @@ __metadata:
languageName: node
linkType: hard
"fast-glob@npm:^3.3.2":
version: 3.3.2
resolution: "fast-glob@npm:3.3.2"
dependencies:
"@nodelib/fs.stat": "npm:^2.0.2"
"@nodelib/fs.walk": "npm:^1.2.3"
glob-parent: "npm:^5.1.2"
merge2: "npm:^1.3.0"
micromatch: "npm:^4.0.4"
checksum: 42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845
languageName: node
linkType: hard
"fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0":
version: 2.1.0
resolution: "fast-json-stable-stringify@npm:2.1.0"
@ -7998,12 +7966,12 @@ __metadata:
languageName: node
linkType: hard
"file-entry-cache@npm:^7.0.0":
version: 7.0.1
resolution: "file-entry-cache@npm:7.0.1"
"file-entry-cache@npm:^7.0.2":
version: 7.0.2
resolution: "file-entry-cache@npm:7.0.2"
dependencies:
flat-cache: "npm:^3.1.1"
checksum: fc0e4f830777e07087f97da9a6734820fdffa2945583355433f40d9819dd97b89f16ac87c07118737a6bc3eb9cf4bd896e7b38b07f0768aefcf44da33e797363
flat-cache: "npm:^3.2.0"
checksum: 822664e35c3e295e6a8ca7ec490d8d8077017607f41f94b29922f1f49c6dd07025048e3ed528e2909a1439eba66d60f802c0774aa612cf6ee053ee4ecc16c8c5
languageName: node
linkType: hard
@ -8137,7 +8105,7 @@ __metadata:
languageName: node
linkType: hard
"flat-cache@npm:^3.0.4, flat-cache@npm:^3.1.1":
"flat-cache@npm:^3.0.4, flat-cache@npm:^3.2.0":
version: 3.2.0
resolution: "flat-cache@npm:3.2.0"
dependencies:
@ -8669,13 +8637,6 @@ __metadata:
languageName: node
linkType: hard
"hard-rejection@npm:^2.1.0":
version: 2.1.0
resolution: "hard-rejection@npm:2.1.0"
checksum: febc3343a1ad575aedcc112580835b44a89a89e01f400b4eda6e8110869edfdab0b00cd1bd4c3bfec9475a57e79e0b355aecd5be46454b6a62b9a359af60e564
languageName: node
linkType: hard
"has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2":
version: 1.0.2
resolution: "has-bigints@npm:1.0.2"
@ -8855,15 +8816,6 @@ __metadata:
languageName: node
linkType: hard
"hosted-git-info@npm:^4.0.1":
version: 4.1.0
resolution: "hosted-git-info@npm:4.1.0"
dependencies:
lru-cache: "npm:^6.0.0"
checksum: 150fbcb001600336d17fdbae803264abed013548eea7946c2264c49ebe2ebd8c4441ba71dd23dd8e18c65de79d637f98b22d4760ba5fb2e0b15d62543d0fff07
languageName: node
linkType: hard
"hpack.js@npm:^2.1.6":
version: 2.1.6
resolution: "hpack.js@npm:2.1.6"
@ -9124,6 +9076,13 @@ __metadata:
languageName: node
linkType: hard
"ignore@npm:^5.3.0":
version: 5.3.0
resolution: "ignore@npm:5.3.0"
checksum: dc06bea5c23aae65d0725a957a0638b57e235ae4568dda51ca142053ed2c352de7e3bc93a69b2b32ac31966a1952e9a93c5ef2e2ab7c6b06aef9808f6b55b571
languageName: node
linkType: hard
"immer@npm:^9.0.21":
version: 9.0.21
resolution: "immer@npm:9.0.21"
@ -9145,7 +9104,7 @@ __metadata:
languageName: node
linkType: hard
"import-fresh@npm:^3.2.1":
"import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0":
version: 3.3.0
resolution: "import-fresh@npm:3.3.0"
dependencies:
@ -9155,13 +9114,6 @@ __metadata:
languageName: node
linkType: hard
"import-lazy@npm:^4.0.0":
version: 4.0.0
resolution: "import-lazy@npm:4.0.0"
checksum: a3520313e2c31f25c0b06aa66d167f329832b68a4f957d7c9daf6e0fa41822b6e84948191648b9b9d8ca82f94740cdf15eecf2401a5b42cd1c33fd84f2225cca
languageName: node
linkType: hard
"import-local@npm:^2.0.0":
version: 2.0.0
resolution: "import-local@npm:2.0.0"
@ -9214,13 +9166,6 @@ __metadata:
languageName: node
linkType: hard
"indent-string@npm:^5.0.0":
version: 5.0.0
resolution: "indent-string@npm:5.0.0"
checksum: 8ee77b57d92e71745e133f6f444d6fa3ed503ad0e1bcd7e80c8da08b42375c07117128d670589725ed07b1978065803fa86318c309ba45415b7fe13e7f170220
languageName: node
linkType: hard
"infer-owner@npm:^1.0.4":
version: 1.0.4
resolution: "infer-owner@npm:1.0.4"
@ -9482,7 +9427,7 @@ __metadata:
languageName: node
linkType: hard
"is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.5.0, is-core-module@npm:^2.9.0":
"is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.9.0":
version: 2.13.1
resolution: "is-core-module@npm:2.13.1"
dependencies:
@ -9764,13 +9709,6 @@ __metadata:
languageName: node
linkType: hard
"is-plain-obj@npm:^1.1.0":
version: 1.1.0
resolution: "is-plain-obj@npm:1.1.0"
checksum: daaee1805add26f781b413fdf192fc91d52409583be30ace35c82607d440da63cc4cac0ac55136716688d6c0a2c6ef3edb2254fecbd1fe06056d6bd15975ee8c
languageName: node
linkType: hard
"is-plain-object@npm:^2.0.3, is-plain-object@npm:^2.0.4":
version: 2.0.4
resolution: "is-plain-object@npm:2.0.4"
@ -11264,20 +11202,6 @@ __metadata:
languageName: node
linkType: hard
"map-obj@npm:^1.0.0":
version: 1.0.1
resolution: "map-obj@npm:1.0.1"
checksum: ccca88395e7d38671ed9f5652ecf471ecd546924be2fb900836b9da35e068a96687d96a5f93dcdfa94d9a27d649d2f10a84595590f89a347fb4dda47629dcc52
languageName: node
linkType: hard
"map-obj@npm:^4.1.0":
version: 4.3.0
resolution: "map-obj@npm:4.3.0"
checksum: 1c19e1c88513c8abdab25c316367154c6a0a6a0f77e3e8c391bb7c0e093aefed293f539d026dc013d86219e5e4c25f23b0003ea588be2101ccd757bacc12d43b
languageName: node
linkType: hard
"map-visit@npm:^1.0.0":
version: 1.0.0
resolution: "map-visit@npm:1.0.0"
@ -11381,23 +11305,10 @@ __metadata:
languageName: node
linkType: hard
"meow@npm:^10.1.5":
version: 10.1.5
resolution: "meow@npm:10.1.5"
dependencies:
"@types/minimist": "npm:^1.2.2"
camelcase-keys: "npm:^7.0.0"
decamelize: "npm:^5.0.0"
decamelize-keys: "npm:^1.1.0"
hard-rejection: "npm:^2.1.0"
minimist-options: "npm:4.1.0"
normalize-package-data: "npm:^3.0.2"
read-pkg-up: "npm:^8.0.0"
redent: "npm:^4.0.0"
trim-newlines: "npm:^4.0.2"
type-fest: "npm:^1.2.2"
yargs-parser: "npm:^20.2.9"
checksum: a513849022edd5ddcc41d28c679d31978abe414d9db5bc457e95e537a4327b2910fd2f699cdd883293f9a5da8951a50939bf60fbd62f7fe12b9ddf96a84b1b27
"meow@npm:^12.1.1":
version: 12.1.1
resolution: "meow@npm:12.1.1"
checksum: a125ca99a32e2306e2f4cbe651a0d27f6eb67918d43a075f6e80b35e9bf372ebf0fc3a9fbc201cbbc9516444b6265fb3c9f80c5b7ebd32f548aa93eb7c28e088
languageName: node
linkType: hard
@ -11520,7 +11431,7 @@ __metadata:
languageName: node
linkType: hard
"min-indent@npm:^1.0.0, min-indent@npm:^1.0.1":
"min-indent@npm:^1.0.0":
version: 1.0.1
resolution: "min-indent@npm:1.0.1"
checksum: 7e207bd5c20401b292de291f02913230cb1163abca162044f7db1d951fa245b174dc00869d40dd9a9f32a885ad6a5f3e767ee104cf278f399cb4e92d3f582d5c
@ -11581,17 +11492,6 @@ __metadata:
languageName: node
linkType: hard
"minimist-options@npm:4.1.0":
version: 4.1.0
resolution: "minimist-options@npm:4.1.0"
dependencies:
arrify: "npm:^1.0.1"
is-plain-obj: "npm:^1.1.0"
kind-of: "npm:^6.0.3"
checksum: 7871f9cdd15d1e7374e5b013e2ceda3d327a06a8c7b38ae16d9ef941e07d985e952c589e57213f7aa90a8744c60aed9524c0d85e501f5478382d9181f2763f54
languageName: node
linkType: hard
"minimist@npm:^1.2.0, minimist@npm:^1.2.6":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
@ -11949,18 +11849,6 @@ __metadata:
languageName: node
linkType: hard
"normalize-package-data@npm:^3.0.2":
version: 3.0.3
resolution: "normalize-package-data@npm:3.0.3"
dependencies:
hosted-git-info: "npm:^4.0.1"
is-core-module: "npm:^2.5.0"
semver: "npm:^7.3.4"
validate-npm-package-license: "npm:^3.0.1"
checksum: e5d0f739ba2c465d41f77c9d950e291ea4af78f8816ddb91c5da62257c40b76d8c83278b0d08ffbcd0f187636ebddad20e181e924873916d03e6e5ea2ef026be
languageName: node
linkType: hard
"normalize-path@npm:^2.1.1":
version: 2.1.1
resolution: "normalize-path@npm:2.1.1"
@ -13160,12 +13048,12 @@ __metadata:
languageName: node
linkType: hard
"postcss-safe-parser@npm:^6.0.0":
version: 6.0.0
resolution: "postcss-safe-parser@npm:6.0.0"
"postcss-safe-parser@npm:^7.0.0":
version: 7.0.0
resolution: "postcss-safe-parser@npm:7.0.0"
peerDependencies:
postcss: ^8.3.3
checksum: 5b0997b63de6ab4afb4b718a52dd7902e465c21d1f2e516762bcb59047787459b4dc5713132f6a19c9c8c483043b20b8a380a55fb61152ee66cbffcddf3b57f0
postcss: ^8.4.31
checksum: 4217afd8ce2809e959dc365e4675f499303cc6b91f94db06c8164422822db2d3b3124df701ee2234db4127ad05619b016bfb9c2bccae9bf9cf898a396f1632c9
languageName: node
linkType: hard
@ -13218,7 +13106,7 @@ __metadata:
languageName: node
linkType: hard
"postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.28":
"postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.32":
version: 8.4.32
resolution: "postcss@npm:8.4.32"
dependencies:
@ -13541,13 +13429,6 @@ __metadata:
languageName: node
linkType: hard
"quick-lru@npm:^5.1.1":
version: 5.1.1
resolution: "quick-lru@npm:5.1.1"
checksum: a24cba5da8cec30d70d2484be37622580f64765fb6390a928b17f60cd69e8dbd32a954b3ff9176fa1b86d86ff2ba05252fae55dc4d40d0291c60412b0ad096da
languageName: node
linkType: hard
"raf@npm:^3.1.0":
version: 3.4.1
resolution: "raf@npm:3.4.1"
@ -14027,29 +13908,6 @@ __metadata:
languageName: node
linkType: hard
"read-pkg-up@npm:^8.0.0":
version: 8.0.0
resolution: "read-pkg-up@npm:8.0.0"
dependencies:
find-up: "npm:^5.0.0"
read-pkg: "npm:^6.0.0"
type-fest: "npm:^1.0.1"
checksum: cf3905ccbe5cd602f23192cc7ca65ed17561bab117eadb9aed817441d5bfc6b9a11215c2a3e9505f501d046818f3c4180dbea61fa83c42083e0b4e407d5cc745
languageName: node
linkType: hard
"read-pkg@npm:^6.0.0":
version: 6.0.0
resolution: "read-pkg@npm:6.0.0"
dependencies:
"@types/normalize-package-data": "npm:^2.4.0"
normalize-package-data: "npm:^3.0.2"
parse-json: "npm:^5.2.0"
type-fest: "npm:^1.0.1"
checksum: b51ee5eed75324f4fac34c9a40b5e4b403de4c532242be01959c9bbdb1ff9db1c6c2aefaba569622fec49d1ead866e97ba856ab145f6e11039b11f7bec1318ba
languageName: node
linkType: hard
"readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.6":
version: 2.3.8
resolution: "readable-stream@npm:2.3.8"
@ -14118,16 +13976,6 @@ __metadata:
languageName: node
linkType: hard
"redent@npm:^4.0.0":
version: 4.0.0
resolution: "redent@npm:4.0.0"
dependencies:
indent-string: "npm:^5.0.0"
strip-indent: "npm:^4.0.0"
checksum: a9b640c8f4b2b5b26a1a908706475ff404dd50a97d6f094bc3c59717be922622927cc7d601d4ae2857d897ad243fd979bd76d751a0481cee8be7024e5fb4c662
languageName: node
linkType: hard
"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0":
version: 1.2.0
resolution: "redis-errors@npm:1.2.0"
@ -15270,16 +15118,6 @@ __metadata:
languageName: node
linkType: hard
"spdx-correct@npm:^3.0.0":
version: 3.2.0
resolution: "spdx-correct@npm:3.2.0"
dependencies:
spdx-expression-parse: "npm:^3.0.0"
spdx-license-ids: "npm:^3.0.0"
checksum: 49208f008618b9119208b0dadc9208a3a55053f4fd6a0ae8116861bd22696fc50f4142a35ebfdb389e05ccf2de8ad142573fefc9e26f670522d899f7b2fe7386
languageName: node
linkType: hard
"spdx-exceptions@npm:^2.1.0":
version: 2.3.0
resolution: "spdx-exceptions@npm:2.3.0"
@ -15287,7 +15125,7 @@ __metadata:
languageName: node
linkType: hard
"spdx-expression-parse@npm:^3.0.0, spdx-expression-parse@npm:^3.0.1":
"spdx-expression-parse@npm:^3.0.1":
version: 3.0.1
resolution: "spdx-expression-parse@npm:3.0.1"
dependencies:
@ -15723,15 +15561,6 @@ __metadata:
languageName: node
linkType: hard
"strip-indent@npm:^4.0.0":
version: 4.0.0
resolution: "strip-indent@npm:4.0.0"
dependencies:
min-indent: "npm:^1.0.1"
checksum: 6b1fb4e22056867f5c9e7a6f3f45922d9a2436cac758607d58aeaac0d3b16ec40b1c43317de7900f1b8dd7a4107352fa47fb960f2c23566538c51e8585c8870e
languageName: node
linkType: hard
"strip-json-comments@npm:^3.1.1":
version: 3.1.1
resolution: "strip-json-comments@npm:3.1.1"
@ -15739,13 +15568,6 @@ __metadata:
languageName: node
linkType: hard
"style-search@npm:^0.1.0":
version: 0.1.0
resolution: "style-search@npm:0.1.0"
checksum: 9e5cb735e5dc4fc2f8c61bebdf211d5352f1cf01511a64da12bb726a01e8c6948c50d357eb8fd7893d44b4e3189655bdddcf8ab338f9d508fe89a8942c650b14
languageName: node
linkType: hard
"stylehacks@npm:^6.0.0":
version: 6.0.0
resolution: "stylehacks@npm:6.0.0"
@ -15758,62 +15580,62 @@ __metadata:
languageName: node
linkType: hard
"stylelint-config-recommended-scss@npm:^13.1.0":
version: 13.1.0
resolution: "stylelint-config-recommended-scss@npm:13.1.0"
"stylelint-config-recommended-scss@npm:^14.0.0":
version: 14.0.0
resolution: "stylelint-config-recommended-scss@npm:14.0.0"
dependencies:
postcss-scss: "npm:^4.0.9"
stylelint-config-recommended: "npm:^13.0.0"
stylelint-scss: "npm:^5.3.0"
stylelint-config-recommended: "npm:^14.0.0"
stylelint-scss: "npm:^6.0.0"
peerDependencies:
postcss: ^8.3.3
stylelint: ^15.10.0
stylelint: ^16.0.2
peerDependenciesMeta:
postcss:
optional: true
checksum: e07d0172c7936b4f644138e4129df2f187d297f1f96ce5865ab21ccd1c22caf94220f7caf9d6985e93e515de4c0356f6cb9c924d00df2eee5b3bc237f7e5bb48
checksum: 9ddc92e7a5fa131b41cee1ab1f69251934ca35c0e2803dc613329cdead7b8b27d8457048a63db29f61a1442e7cdef14207f88a3abce00ec53fdefe0d604f7de3
languageName: node
linkType: hard
"stylelint-config-recommended@npm:^13.0.0":
version: 13.0.0
resolution: "stylelint-config-recommended@npm:13.0.0"
"stylelint-config-recommended@npm:^14.0.0":
version: 14.0.0
resolution: "stylelint-config-recommended@npm:14.0.0"
peerDependencies:
stylelint: ^15.10.0
checksum: 80420a1ab616e8637b66223f88c597388990d9991cd6a28b8372049b83329d893412f83029bb253a82b52387e497b62e042bc898064a2f22574b0d8921f01dd2
stylelint: ^16.0.0
checksum: 4ad15c36e8c03291aa7bbe4b672ebfb0f46ab698e7580a0da8d29644046d102d7f31dbf00a2a6eab94b565c390c6fb0d5d528737b83ac3acf6dc2ef085a90b11
languageName: node
linkType: hard
"stylelint-config-standard-scss@npm:^11.0.0":
version: 11.1.0
resolution: "stylelint-config-standard-scss@npm:11.1.0"
"stylelint-config-standard-scss@npm:^12.0.0":
version: 12.0.0
resolution: "stylelint-config-standard-scss@npm:12.0.0"
dependencies:
stylelint-config-recommended-scss: "npm:^13.1.0"
stylelint-config-standard: "npm:^34.0.0"
stylelint-config-recommended-scss: "npm:^14.0.0"
stylelint-config-standard: "npm:^35.0.0"
peerDependencies:
postcss: ^8.3.3
stylelint: ^15.10.0
stylelint: ^16.0.2
peerDependenciesMeta:
postcss:
optional: true
checksum: 22d00e75c1eacce9883fd48c3d67b1107b0e39d7d86e9f73deaa332b11c39a9678c947ae2c34cd5159a452ec9a857694ed58b5a851087480d3c9a66dab629415
checksum: 7f3ccfb4175f9c50b69d30ca35a97887008c5ba493dbe7d5bce0b57b1eafd21b268177b82404368e7780600077cba784f98e1046671724be3b29a00c6a7913a4
languageName: node
linkType: hard
"stylelint-config-standard@npm:^34.0.0":
version: 34.0.0
resolution: "stylelint-config-standard@npm:34.0.0"
"stylelint-config-standard@npm:^35.0.0":
version: 35.0.0
resolution: "stylelint-config-standard@npm:35.0.0"
dependencies:
stylelint-config-recommended: "npm:^13.0.0"
stylelint-config-recommended: "npm:^14.0.0"
peerDependencies:
stylelint: ^15.10.0
checksum: 2494468af2359490b6ebb9723d9653f9e31db3a0772b8d9f0e081018b0079ef84ae6f90dcf94c879a3c374f299e334941e3dcff1afb603c2284d3103085b71fb
stylelint: ^16.0.0
checksum: 791fbc26cc3029ce3c2423a643e903545b5e4cd605251b18f0ce790bac6fbaaf380469845c1ff45f4e320126af9f8a9dc1ca85d0df9274277ae60da91e81895b
languageName: node
linkType: hard
"stylelint-scss@npm:^5.3.0":
version: 5.3.1
resolution: "stylelint-scss@npm:5.3.1"
"stylelint-scss@npm:^6.0.0":
version: 6.0.0
resolution: "stylelint-scss@npm:6.0.0"
dependencies:
known-css-properties: "npm:^0.29.0"
postcss-media-query-parser: "npm:^0.2.3"
@ -15821,58 +15643,56 @@ __metadata:
postcss-selector-parser: "npm:^6.0.13"
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
stylelint: ^14.5.1 || ^15.0.0
checksum: 5dfed5f9ac9812cd2ac6ef0272c720dee0326aaaee2998315a23bdcd71b8f04427f29cad634793eea2b45984182e20f03e90d43501e8e4d55bc956f80e2de477
stylelint: ^16.0.2
checksum: f5e971d19ef6879ae5c18cb8fba8033fe7928f241178e6afd80357cc080d2feddfd6f7fe564aaa696008aa10345df5885d9a4471c926b3e266088e015927782e
languageName: node
linkType: hard
"stylelint@npm:^15.10.1":
version: 15.11.0
resolution: "stylelint@npm:15.11.0"
"stylelint@npm:^16.0.2":
version: 16.0.2
resolution: "stylelint@npm:16.0.2"
dependencies:
"@csstools/css-parser-algorithms": "npm:^2.3.1"
"@csstools/css-tokenizer": "npm:^2.2.0"
"@csstools/media-query-list-parser": "npm:^2.1.4"
"@csstools/css-parser-algorithms": "npm:^2.3.2"
"@csstools/css-tokenizer": "npm:^2.2.1"
"@csstools/media-query-list-parser": "npm:^2.1.5"
"@csstools/selector-specificity": "npm:^3.0.0"
balanced-match: "npm:^2.0.0"
colord: "npm:^2.9.3"
cosmiconfig: "npm:^8.2.0"
cosmiconfig: "npm:^9.0.0"
css-functions-list: "npm:^3.2.1"
css-tree: "npm:^2.3.1"
debug: "npm:^4.3.4"
fast-glob: "npm:^3.3.1"
fast-glob: "npm:^3.3.2"
fastest-levenshtein: "npm:^1.0.16"
file-entry-cache: "npm:^7.0.0"
file-entry-cache: "npm:^7.0.2"
global-modules: "npm:^2.0.0"
globby: "npm:^11.1.0"
globjoin: "npm:^0.1.4"
html-tags: "npm:^3.3.1"
ignore: "npm:^5.2.4"
import-lazy: "npm:^4.0.0"
ignore: "npm:^5.3.0"
imurmurhash: "npm:^0.1.4"
is-plain-object: "npm:^5.0.0"
known-css-properties: "npm:^0.29.0"
mathml-tag-names: "npm:^2.1.3"
meow: "npm:^10.1.5"
meow: "npm:^12.1.1"
micromatch: "npm:^4.0.5"
normalize-path: "npm:^3.0.0"
picocolors: "npm:^1.0.0"
postcss: "npm:^8.4.28"
postcss: "npm:^8.4.32"
postcss-resolve-nested-selector: "npm:^0.1.1"
postcss-safe-parser: "npm:^6.0.0"
postcss-safe-parser: "npm:^7.0.0"
postcss-selector-parser: "npm:^6.0.13"
postcss-value-parser: "npm:^4.2.0"
resolve-from: "npm:^5.0.0"
string-width: "npm:^4.2.3"
strip-ansi: "npm:^6.0.1"
style-search: "npm:^0.1.0"
strip-ansi: "npm:^7.1.0"
supports-hyperlinks: "npm:^3.0.0"
svg-tags: "npm:^1.0.0"
table: "npm:^6.8.1"
write-file-atomic: "npm:^5.0.1"
bin:
stylelint: bin/stylelint.mjs
checksum: 2d88b7293e308b7e418c14ba4130777b1a28b214304957f03b41a6dc8e00005266caf47479f718a6ec5e572cb52e903ca34aabf3febbe3a3ae32fff6b018d9fd
checksum: 5ec755e209beb1877ff40d50f18c1ebb05bf251925da1f98f28fb3911e4031195eb86adaf641ac5cdb01ba973f4c999bc0c6b0270d08c1d5c070adbdd9e734cf
languageName: node
linkType: hard
@ -16326,13 +16146,6 @@ __metadata:
languageName: node
linkType: hard
"trim-newlines@npm:^4.0.2":
version: 4.1.1
resolution: "trim-newlines@npm:4.1.1"
checksum: 70e60e652305efd0dda1f2bce1a5edc9bb5834a2e00d05dfde178715ec48faa8264a2bc1a7efc593b7936d03f6d42c398616329eef44b7bd5070180a02056981
languageName: node
linkType: hard
"ts-api-utils@npm:^1.0.1":
version: 1.0.3
resolution: "ts-api-utils@npm:1.0.3"
@ -16424,13 +16237,6 @@ __metadata:
languageName: node
linkType: hard
"type-fest@npm:^1.0.1, type-fest@npm:^1.2.1, type-fest@npm:^1.2.2":
version: 1.4.0
resolution: "type-fest@npm:1.4.0"
checksum: a3c0f4ee28ff6ddf800d769eafafcdeab32efa38763c1a1b8daeae681920f6e345d7920bf277245235561d8117dab765cb5f829c76b713b4c9de0998a5397141
languageName: node
linkType: hard
"type-fest@npm:^3.0.0":
version: 3.13.1
resolution: "type-fest@npm:3.13.1"
@ -16897,16 +16703,6 @@ __metadata:
languageName: node
linkType: hard
"validate-npm-package-license@npm:^3.0.1":
version: 3.0.4
resolution: "validate-npm-package-license@npm:3.0.4"
dependencies:
spdx-correct: "npm:^3.0.0"
spdx-expression-parse: "npm:^3.0.0"
checksum: 7b91e455a8de9a0beaa9fe961e536b677da7f48c9a493edf4d4d4a87fd80a7a10267d438723364e432c2fcd00b5650b5378275cded362383ef570276e6312f4f
languageName: node
linkType: hard
"value-equal@npm:^1.0.1":
version: 1.0.1
resolution: "value-equal@npm:1.0.1"
@ -17739,8 +17535,8 @@ __metadata:
linkType: hard
"ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.14.2":
version: 8.15.0
resolution: "ws@npm:8.15.0"
version: 8.15.1
resolution: "ws@npm:8.15.1"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
@ -17749,7 +17545,7 @@ __metadata:
optional: true
utf-8-validate:
optional: true
checksum: b778a405b2589ffbf549323e2f404f1f72e372a049d332d2f0b1f33057e9fbb14a05aa474cb156e4584b418cd95edf4297c0ca5263d6519e8009064bf8e0b80d
checksum: 9964360dd5ab35c7376bd7c4295a3c8bd44ea0838c9413742548a6fb3ec371fc6c18552d5b8e76bdc21536db1909765612815bae072674b5ec69971605395a96
languageName: node
linkType: hard
@ -17833,7 +17629,7 @@ __metadata:
languageName: node
linkType: hard
"yargs-parser@npm:^20.2.1, yargs-parser@npm:^20.2.9":
"yargs-parser@npm:^20.2.1":
version: 20.2.9
resolution: "yargs-parser@npm:20.2.9"
checksum: 0685a8e58bbfb57fab6aefe03c6da904a59769bd803a722bb098bd5b0f29d274a1357762c7258fb487512811b8063fb5d2824a3415a0a4540598335b3b086c72