From 4aa06cbdbfbd4a4f9508c988c012951024b561e8 Mon Sep 17 00:00:00 2001
From: Brian Holley <brian.holley@hotmail.com>
Date: Mon, 13 Nov 2023 16:39:54 -0800
Subject: [PATCH 01/14] Fix "Hide these posts from home" list setting not
 refreshing when switching lists (#27763)

---
 app/javascript/mastodon/features/list_timeline/index.jsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/javascript/mastodon/features/list_timeline/index.jsx b/app/javascript/mastodon/features/list_timeline/index.jsx
index aadb6ecd54..55579c2fd1 100644
--- a/app/javascript/mastodon/features/list_timeline/index.jsx
+++ b/app/javascript/mastodon/features/list_timeline/index.jsx
@@ -204,7 +204,7 @@ class ListTimeline extends PureComponent {
           </div>
 
           <div className='setting-toggle'>
-            <Toggle id={`list-${id}-exclusive`} defaultChecked={isExclusive} onChange={this.onExclusiveToggle} />
+            <Toggle id={`list-${id}-exclusive`} checked={isExclusive} onChange={this.onExclusiveToggle} />
             <label htmlFor={`list-${id}-exclusive`} className='setting-toggle__label'>
               <FormattedMessage id='lists.exclusive' defaultMessage='Hide these posts from home' />
             </label>

From 5e2ecc736ddf275e57c91348a7b0a485f9b2462d Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 14 Nov 2023 05:29:33 -0500
Subject: [PATCH 02/14] Remove double `subject` in
 api/v1/accounts/relationships spec (#27839)

---
 .../api/v1/accounts/relationships_spec.rb     | 24 ++++++++++++-------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/spec/requests/api/v1/accounts/relationships_spec.rb b/spec/requests/api/v1/accounts/relationships_spec.rb
index bb78e3b3e4..5011352c6b 100644
--- a/spec/requests/api/v1/accounts/relationships_spec.rb
+++ b/spec/requests/api/v1/accounts/relationships_spec.rb
@@ -102,17 +102,25 @@ describe 'GET /api/v1/accounts/relationships' do
       end
     end
 
-    it 'returns JSON with correct data on cached requests too' do
-      subject
-      subject
+    it 'returns JSON with correct data on previously cached requests' do
+      # Initial request including multiple accounts in params
+      get '/api/v1/accounts/relationships', headers: headers, params: { id: [simon.id, lewis.id] }
+      expect(body_as_json.size).to eq(2)
+
+      # Subsequent request with different id, should override cache from first request
+      get '/api/v1/accounts/relationships', headers: headers, params: { id: [simon.id] }
 
       expect(response).to have_http_status(200)
 
-      json = body_as_json
-
-      expect(json).to be_a Enumerable
-      expect(json.first[:following]).to be true
-      expect(json.first[:showing_reblogs]).to be true
+      expect(body_as_json)
+        .to be_an(Enumerable)
+        .and have_attributes(
+          size: 1,
+          first: hash_including(
+            following: true,
+            showing_reblogs: true
+          )
+        )
     end
 
     it 'returns JSON with correct data after change too' do

From 373aa95dddd7119725c4f11d3af402066e2288cf Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 14 Nov 2023 11:30:39 +0100
Subject: [PATCH 03/14] Update formatjs monorepo (#27849)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 154 +++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 107 insertions(+), 47 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index e8fe28fb46..1cb5a42d50 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1800,6 +1800,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@formatjs/ecma402-abstract@npm:1.18.0":
+  version: 1.18.0
+  resolution: "@formatjs/ecma402-abstract@npm:1.18.0"
+  dependencies:
+    "@formatjs/intl-localematcher": "npm:0.5.2"
+    tslib: "npm:^2.4.0"
+  checksum: bbdad0aee8e48baad6bfe6b2c27caf3befe35e658b922ee2f84417a819f0bdc7e849a8c0c782db8b53f5666bf19669d2b10a1104257c08796d198c87766bfc92
+  languageName: node
+  linkType: hard
+
 "@formatjs/fast-memoize@npm:2.2.0":
   version: 2.2.0
   resolution: "@formatjs/fast-memoize@npm:2.2.0"
@@ -1820,6 +1830,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@formatjs/icu-messageformat-parser@npm:2.7.3":
+  version: 2.7.3
+  resolution: "@formatjs/icu-messageformat-parser@npm:2.7.3"
+  dependencies:
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
+    "@formatjs/icu-skeleton-parser": "npm:1.7.0"
+    tslib: "npm:^2.4.0"
+  checksum: 2a51038813e5cff7e2df767e1227373d228e907adb7268fc3744b3d82c4fa69d4aa9f6020a62de2c468cf724600e9372ac07ae43a4480ed066fe34e224e80e4a
+  languageName: node
+  linkType: hard
+
 "@formatjs/icu-skeleton-parser@npm:1.6.4":
   version: 1.6.4
   resolution: "@formatjs/icu-skeleton-parser@npm:1.6.4"
@@ -1830,25 +1851,35 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@formatjs/intl-displaynames@npm:6.6.3":
-  version: 6.6.3
-  resolution: "@formatjs/intl-displaynames@npm:6.6.3"
+"@formatjs/icu-skeleton-parser@npm:1.7.0":
+  version: 1.7.0
+  resolution: "@formatjs/icu-skeleton-parser@npm:1.7.0"
   dependencies:
-    "@formatjs/ecma402-abstract": "npm:1.17.4"
-    "@formatjs/intl-localematcher": "npm:0.5.1"
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
     tslib: "npm:^2.4.0"
-  checksum: b0520cb744a51290fbcde80860f39ed9c9df9b81beae98986e1fc089ef635f7699c750631fa42a559f3678d1dd02b14904614e70360477d18e68d3eba6592390
+  checksum: 2e4db815247ddb10f7990bbb501c85b854ee951ee45143673eb91b4392b11d0a8312327adb8b624c6a2fdafab12083904630d6d22475503d025f1612da4dcaee
   languageName: node
   linkType: hard
 
-"@formatjs/intl-listformat@npm:7.5.2":
-  version: 7.5.2
-  resolution: "@formatjs/intl-listformat@npm:7.5.2"
+"@formatjs/intl-displaynames@npm:6.6.4":
+  version: 6.6.4
+  resolution: "@formatjs/intl-displaynames@npm:6.6.4"
   dependencies:
-    "@formatjs/ecma402-abstract": "npm:1.17.4"
-    "@formatjs/intl-localematcher": "npm:0.5.1"
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
+    "@formatjs/intl-localematcher": "npm:0.5.2"
     tslib: "npm:^2.4.0"
-  checksum: 54fa03da4ea45504681d6d87d72d1cac574809ce43f965fa4b845e83be3072d92324c58cec57ad386827087fb1d6ecae438d29576f30176bf52eb212e454bce2
+  checksum: 009e443dd0d10776b8573d0181407d4c0d6c7a2ff537a5ea1e36413d1b08db9c21dfef272eabab8efabd01a58b64f663a30e4d584fd761df3fd68a5d23fe444b
+  languageName: node
+  linkType: hard
+
+"@formatjs/intl-listformat@npm:7.5.3":
+  version: 7.5.3
+  resolution: "@formatjs/intl-listformat@npm:7.5.3"
+  dependencies:
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
+    "@formatjs/intl-localematcher": "npm:0.5.2"
+    tslib: "npm:^2.4.0"
+  checksum: de741ce84b16fed57016afbfe446ebd57cd23a046859a9353f5d455f8bc9114493bf83b9e18429268c7ce8f77bc54516a9b8190baf09fbb25c9b06cfc80101d4
   languageName: node
   linkType: hard
 
@@ -1861,34 +1892,43 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@formatjs/intl-pluralrules@npm:^5.2.2":
-  version: 5.2.9
-  resolution: "@formatjs/intl-pluralrules@npm:5.2.9"
+"@formatjs/intl-localematcher@npm:0.5.2":
+  version: 0.5.2
+  resolution: "@formatjs/intl-localematcher@npm:0.5.2"
   dependencies:
-    "@formatjs/ecma402-abstract": "npm:1.17.4"
-    "@formatjs/intl-localematcher": "npm:0.5.1"
     tslib: "npm:^2.4.0"
-  checksum: a6ca5c498ce542facacf8ce8640d4ba068f9119b758547a23614b50611eb385a46abd386ff88fa423211355ec463cf102c2c908b74f6e23a5bc9e2a23873dc29
+  checksum: 4b3ae75470e3e53ffa39b2d46e65a2a4c9c4becbc0aac989b0694370e10c6687643660a045512d676509bc29b257fe5726fbb028de12f889be02c2d20b6527e6
   languageName: node
   linkType: hard
 
-"@formatjs/intl@npm:2.9.8":
-  version: 2.9.8
-  resolution: "@formatjs/intl@npm:2.9.8"
+"@formatjs/intl-pluralrules@npm:^5.2.2":
+  version: 5.2.10
+  resolution: "@formatjs/intl-pluralrules@npm:5.2.10"
   dependencies:
-    "@formatjs/ecma402-abstract": "npm:1.17.4"
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
+    "@formatjs/intl-localematcher": "npm:0.5.2"
+    tslib: "npm:^2.4.0"
+  checksum: 1050416613e80bff2c58546c80c8d52ed97847d13c90535a53d058e44969369b50e1cfdb464e9e9ef4802c934c84ea0e656c3f4e3b4d5ac7496b722c759da4cf
+  languageName: node
+  linkType: hard
+
+"@formatjs/intl@npm:2.9.9":
+  version: 2.9.9
+  resolution: "@formatjs/intl@npm:2.9.9"
+  dependencies:
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
     "@formatjs/fast-memoize": "npm:2.2.0"
-    "@formatjs/icu-messageformat-parser": "npm:2.7.2"
-    "@formatjs/intl-displaynames": "npm:6.6.3"
-    "@formatjs/intl-listformat": "npm:7.5.2"
-    intl-messageformat: "npm:10.5.7"
+    "@formatjs/icu-messageformat-parser": "npm:2.7.3"
+    "@formatjs/intl-displaynames": "npm:6.6.4"
+    "@formatjs/intl-listformat": "npm:7.5.3"
+    intl-messageformat: "npm:10.5.8"
     tslib: "npm:^2.4.0"
   peerDependencies:
     typescript: 5
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 6341f4bfb56a0e14373395b1232e1eeb8e64588a8c3d4614cd2b06f71d4e65dbd4a79e3a1c07e1b6c20c48e399ac2385977b01a559e1d2bd1a1d226e0eae3058
+  checksum: b26904da605ab309535dfbbfbd403a3bb33d51d3c969c548b88fa04755be3aff60b1bddd1c453514a84048c7432271cef507ac66de32dcfa66b3f842a1ddb977
   languageName: node
   linkType: hard
 
@@ -1912,6 +1952,26 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@formatjs/ts-transformer@npm:3.13.9":
+  version: 3.13.9
+  resolution: "@formatjs/ts-transformer@npm:3.13.9"
+  dependencies:
+    "@formatjs/icu-messageformat-parser": "npm:2.7.3"
+    "@types/json-stable-stringify": "npm:^1.0.32"
+    "@types/node": "npm:14 || 16 || 17"
+    chalk: "npm:^4.0.0"
+    json-stable-stringify: "npm:^1.0.1"
+    tslib: "npm:^2.4.0"
+    typescript: "npm:5"
+  peerDependencies:
+    ts-jest: ">=27"
+  peerDependenciesMeta:
+    ts-jest:
+      optional: true
+  checksum: 4e313b967e45aae79246174c3181d31cc7cd297380d3a880a98fc0be16d76b783868712151e840ea16d22e2fbec0388b1005f688b6d4cb74ee4411b43f6d33f4
+  languageName: node
+  linkType: hard
+
 "@gamestdio/websocket@npm:^0.3.2":
   version: 0.3.2
   resolution: "@gamestdio/websocket@npm:0.3.2"
@@ -4675,21 +4735,21 @@ __metadata:
   linkType: hard
 
 "babel-plugin-formatjs@npm:^10.5.1":
-  version: 10.5.9
-  resolution: "babel-plugin-formatjs@npm:10.5.9"
+  version: 10.5.10
+  resolution: "babel-plugin-formatjs@npm:10.5.10"
   dependencies:
     "@babel/core": "npm:^7.10.4"
     "@babel/helper-plugin-utils": "npm:^7.10.4"
     "@babel/plugin-syntax-jsx": "npm:7"
     "@babel/traverse": "npm:7"
     "@babel/types": "npm:^7.12.11"
-    "@formatjs/icu-messageformat-parser": "npm:2.7.2"
-    "@formatjs/ts-transformer": "npm:3.13.8"
+    "@formatjs/icu-messageformat-parser": "npm:2.7.3"
+    "@formatjs/ts-transformer": "npm:3.13.9"
     "@types/babel__core": "npm:^7.1.7"
     "@types/babel__helper-plugin-utils": "npm:^7.10.0"
     "@types/babel__traverse": "npm:^7.1.7"
     tslib: "npm:^2.4.0"
-  checksum: 5e4127cf7b4b9b3306a9d0ab5b029831712d22db5e2117225ce706b55d222d09a7eba1f3720fdad7a99f61843b5cba107296fc11ae00a6f0941217d9322aa02e
+  checksum: bff65cd2a88a0ae00eabab1d022ffc44c4385b7e529cac42375bb1828c678c7a71a78f644512e5d1dd8cd532d418c16acdbabcef2bf6670e24404f4f164a74ce
   languageName: node
   linkType: hard
 
@@ -9243,15 +9303,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"intl-messageformat@npm:10.5.7, intl-messageformat@npm:^10.3.5":
-  version: 10.5.7
-  resolution: "intl-messageformat@npm:10.5.7"
+"intl-messageformat@npm:10.5.8, intl-messageformat@npm:^10.3.5":
+  version: 10.5.8
+  resolution: "intl-messageformat@npm:10.5.8"
   dependencies:
-    "@formatjs/ecma402-abstract": "npm:1.17.4"
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
     "@formatjs/fast-memoize": "npm:2.2.0"
-    "@formatjs/icu-messageformat-parser": "npm:2.7.2"
+    "@formatjs/icu-messageformat-parser": "npm:2.7.3"
     tslib: "npm:^2.4.0"
-  checksum: 7f341b3eb5b3d402167c99ca7fb98720c7ad553bed8a490b2210bd90ea9009a09f9030939307fecb111fce1454f31b4298b4f0a346999af627c86f8164a5c547
+  checksum: 1d2854aae8471ec48165ca265760d6c5b1814eca831c88db698eb29b5ed20bee21ca8533090c9d28d9c6f1d844dda210b0bc58a2e036446158fae0845e5eed4f
   languageName: node
   linkType: hard
 
@@ -13643,18 +13703,18 @@ __metadata:
   linkType: hard
 
 "react-intl@npm:^6.4.2":
-  version: 6.5.4
-  resolution: "react-intl@npm:6.5.4"
+  version: 6.5.5
+  resolution: "react-intl@npm:6.5.5"
   dependencies:
-    "@formatjs/ecma402-abstract": "npm:1.17.4"
-    "@formatjs/icu-messageformat-parser": "npm:2.7.2"
-    "@formatjs/intl": "npm:2.9.8"
-    "@formatjs/intl-displaynames": "npm:6.6.3"
-    "@formatjs/intl-listformat": "npm:7.5.2"
+    "@formatjs/ecma402-abstract": "npm:1.18.0"
+    "@formatjs/icu-messageformat-parser": "npm:2.7.3"
+    "@formatjs/intl": "npm:2.9.9"
+    "@formatjs/intl-displaynames": "npm:6.6.4"
+    "@formatjs/intl-listformat": "npm:7.5.3"
     "@types/hoist-non-react-statics": "npm:^3.3.1"
     "@types/react": "npm:16 || 17 || 18"
     hoist-non-react-statics: "npm:^3.3.2"
-    intl-messageformat: "npm:10.5.7"
+    intl-messageformat: "npm:10.5.8"
     tslib: "npm:^2.4.0"
   peerDependencies:
     react: ^16.6.0 || 17 || 18
@@ -13662,7 +13722,7 @@ __metadata:
   peerDependenciesMeta:
     typescript:
       optional: true
-  checksum: 1117a7f866b103abf88a4087f5fe8b854d9c069c69444c592f8431e7d28c9b90423f7b50e550be0f2f173b7563e943bcc9238e80f6747181f81861275f6e2ce7
+  checksum: 9ff6200f195557804b735d618ee593aed7848e84213ac4eb9c57708f55c0d93232e0dd338c990348ba3b1d73dca071502a2051d4a2790838d962c3ccde87fa6c
   languageName: node
   linkType: hard
 

From 1f8173ac5aa2dbdab66e6c426b137f4fcc162462 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 14 Nov 2023 05:31:59 -0500
Subject: [PATCH 04/14] Extract private methods in
 api/v1/instances/domain_blocks (#27844)

---
 .../api/v1/instances/domain_blocks_controller.rb   | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/app/controllers/api/v1/instances/domain_blocks_controller.rb b/app/controllers/api/v1/instances/domain_blocks_controller.rb
index 566764dbf0..8fb90305ad 100644
--- a/app/controllers/api/v1/instances/domain_blocks_controller.rb
+++ b/app/controllers/api/v1/instances/domain_blocks_controller.rb
@@ -13,7 +13,7 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr
       cache_if_unauthenticated!
     end
 
-    render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: (Setting.show_domain_blocks_rationale == 'all' || (Setting.show_domain_blocks_rationale == 'users' && user_signed_in?))
+    render json: @domain_blocks, each_serializer: REST::DomainBlockSerializer, with_comment: show_rationale_in_response?
   end
 
   private
@@ -25,4 +25,16 @@ class Api::V1::Instances::DomainBlocksController < Api::V1::Instances::BaseContr
   def set_domain_blocks
     @domain_blocks = DomainBlock.with_user_facing_limitations.by_severity
   end
+
+  def show_rationale_in_response?
+    always_show_rationale? || show_rationale_for_user?
+  end
+
+  def always_show_rationale?
+    Setting.show_domain_blocks_rationale == 'all'
+  end
+
+  def show_rationale_for_user?
+    Setting.show_domain_blocks_rationale == 'users' && user_signed_in?
+  end
 end

From c1e071f6343a0b5c6383e6a336fd1aaf8dc97100 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 14 Nov 2023 11:53:47 +0100
Subject: [PATCH 05/14] New Crowdin Translations (automated) (#27848)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/af.json      |  4 ++++
 app/javascript/mastodon/locales/be.json      |  6 +++---
 app/javascript/mastodon/locales/ca.json      |  1 +
 app/javascript/mastodon/locales/fy.json      |  1 +
 app/javascript/mastodon/locales/lt.json      |  9 ++++++++-
 app/javascript/mastodon/locales/sl.json      |  1 +
 app/javascript/mastodon/locales/sr-Latn.json |  1 +
 app/javascript/mastodon/locales/sr.json      |  1 +
 config/locales/activerecord.af.yml           |  4 ++++
 config/locales/af.yml                        | 17 +++++++++++++++++
 config/locales/be.yml                        |  1 +
 config/locales/ca.yml                        |  1 +
 config/locales/cy.yml                        |  1 +
 config/locales/da.yml                        |  5 +++++
 config/locales/de.yml                        |  1 +
 config/locales/devise.be.yml                 |  2 +-
 config/locales/doorkeeper.af.yml             |  1 +
 config/locales/es-AR.yml                     |  1 +
 config/locales/es-MX.yml                     |  1 +
 config/locales/es.yml                        |  1 +
 config/locales/eu.yml                        |  1 +
 config/locales/fa.yml                        |  1 +
 config/locales/fo.yml                        |  1 +
 config/locales/fr-QC.yml                     |  1 +
 config/locales/fr.yml                        |  1 +
 config/locales/fy.yml                        |  1 +
 config/locales/gl.yml                        |  1 +
 config/locales/he.yml                        |  5 +++--
 config/locales/hu.yml                        |  1 +
 config/locales/is.yml                        |  1 +
 config/locales/it.yml                        |  1 +
 config/locales/ko.yml                        |  1 +
 config/locales/lt.yml                        |  1 +
 config/locales/nl.yml                        |  1 +
 config/locales/nn.yml                        |  1 +
 config/locales/no.yml                        |  1 +
 config/locales/pl.yml                        |  1 +
 config/locales/pt-BR.yml                     |  1 +
 config/locales/pt-PT.yml                     |  1 +
 config/locales/simple_form.be.yml            |  2 +-
 config/locales/sk.yml                        |  8 ++++++++
 config/locales/sl.yml                        |  1 +
 config/locales/sq.yml                        |  1 +
 config/locales/sr-Latn.yml                   |  1 +
 config/locales/sr.yml                        |  1 +
 config/locales/sv.yml                        |  1 +
 config/locales/th.yml                        |  1 +
 config/locales/tr.yml                        |  1 +
 config/locales/uk.yml                        |  1 +
 config/locales/zh-CN.yml                     |  1 +
 config/locales/zh-HK.yml                     |  1 +
 config/locales/zh-TW.yml                     |  1 +
 52 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json
index 7e842b5dd4..6f7f355fc4 100644
--- a/app/javascript/mastodon/locales/af.json
+++ b/app/javascript/mastodon/locales/af.json
@@ -14,6 +14,7 @@
   "account.badges.group": "Groep",
   "account.block": "Blokkeer @{name}",
   "account.block_domain": "Blokkeer domein {domain}",
+  "account.block_short": "Blokkeer",
   "account.blocked": "Geblokkeer",
   "account.browse_more_on_origin_server": "Verken die oorspronklike profiel",
   "account.cancel_follow_request": "Herroep volgversoek",
@@ -45,6 +46,7 @@
   "account.posts_with_replies": "Plasings en antwoorde",
   "account.report": "Rapporteer @{name}",
   "account.requested": "Wag op goedkeuring. Klik om volgversoek te kanselleer",
+  "account.requested_follow": "{name} het versoek om jou te volg",
   "account.share": "Deel @{name} se profiel",
   "account.show_reblogs": "Wys aangestuurde plasings van @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} Plaas} other {{counter} Plasings}}",
@@ -82,6 +84,7 @@
   "column.community": "Plaaslike tydlyn",
   "column.directory": "Blaai deur profiele",
   "column.domain_blocks": "Geblokkeerde domeine",
+  "column.favourites": "Gunstelinge",
   "column.follow_requests": "Volgversoeke",
   "column.home": "Tuis",
   "column.lists": "Lyste",
@@ -271,6 +274,7 @@
   "privacy.unlisted.short": "Ongelys",
   "privacy_policy.last_updated": "Laaste bywerking op {date}",
   "privacy_policy.title": "Privaatheidsbeleid",
+  "regeneration_indicator.sublabel": "Jou tuis-voer word voorberei!",
   "reply_indicator.cancel": "Kanselleer",
   "report.placeholder": "Type or paste additional comments",
   "report.submit": "Submit report",
diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json
index 7c2d652b61..e8a52ee29c 100644
--- a/app/javascript/mastodon/locales/be.json
+++ b/app/javascript/mastodon/locales/be.json
@@ -201,7 +201,7 @@
   "disabled_account_banner.text": "Ваш уліковы запіс {disabledAccount} часова адключаны.",
   "dismissable_banner.community_timeline": "Гэта самыя апошнія допісы ад людзей, уліковыя запісы якіх размяшчаюцца на {domain}.",
   "dismissable_banner.dismiss": "Адхіліць",
-  "dismissable_banner.explore_links": "Гэтыя навіны абмяркоўваюцца прама зараз на гэтым і іншых серверах дэцэнтралізаванай сеткі.",
+  "dismissable_banner.explore_links": "Гэтыя навіны абмяркоўваюцца цяпер на гэтым і іншых серверах дэцэнтралізаванай сеткі.",
   "dismissable_banner.explore_statuses": "Допісы з гэтага і іншых сервераў дэцэнтралізаванай сеткі, якія набіраюць папулярнасць прама зараз.",
   "dismissable_banner.explore_tags": "Гэтыя хэштэгі зараз набіраюць папулярнасць сярод людзей на гэтым і іншых серверах дэцэнтралізаванай сеткі",
   "dismissable_banner.public_timeline": "Гэта апошнія публічныя допісы людзей з усей сеткі, за якімі сочаць карыстальнікі {domain}.",
@@ -482,7 +482,7 @@
   "onboarding.share.lead": "Дайце людзям ведаць, як яны могуць знайсці вас на Mastodon!",
   "onboarding.share.message": "Я {username} на #Mastodon! Сачыце за мной на {url}",
   "onboarding.share.next_steps": "Магчымыя наступныя крокі:",
-  "onboarding.share.title": "Падзяліцеся сваім профілем",
+  "onboarding.share.title": "Абагульце свой профіль",
   "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
   "onboarding.start.skip": "Want to skip right ahead?",
   "onboarding.start.title": "Вы зрабілі гэта!",
@@ -493,7 +493,7 @@
   "onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.",
   "onboarding.steps.setup_profile.title": "Customize your profile",
   "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
-  "onboarding.steps.share_profile.title": "Share your profile",
+  "onboarding.steps.share_profile.title": "Абагульць ваш профіль у Mastodon",
   "onboarding.tips.2fa": "<strong>Ці вы ведаеце?</strong> Вы можаце абараніць свой уліковы запіс, усталяваўшы двухфактарную аўтэнтыфікацыю ў наладах уліковага запісу. Яна працуе з любой праграмай TOTP на ваш выбар, нумар тэлефона не патрэбны!",
   "onboarding.tips.accounts_from_other_servers": "<strong>Ці вы ведаеце?</strong> Паколькі Mastodon дэцэнтралізаваны, некаторыя профілі, якія вам трапляюцца, будуць размяшчацца на іншых серверах, адрозных ад вашага. І ўсё ж вы можаце бесперашкодна ўзаемадзейнічаць з імі! Іх сервер пазначаны ў другой палове імя карыстальніка!",
   "onboarding.tips.migration": "<strong>Ці вы ведаеце?</strong> Калі вы адчуваеце, што {domain} не з'яўляецца для вас лепшым выбарам у будучыні, вы можаце перайсці на іншы сервер Mastodon, не губляючы сваіх падпісчыкаў. Вы нават можаце стварыць свой уласны сервер!",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 433b9b47b7..99cae584b0 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -222,6 +222,7 @@
   "emoji_button.search_results": "Resultats de la cerca",
   "emoji_button.symbols": "Símbols",
   "emoji_button.travel": "Viatges i llocs",
+  "empty_column.account_hides_collections": "Aquest usuari ha elegit no mostrar aquesta informació",
   "empty_column.account_suspended": "Compte suspès",
   "empty_column.account_timeline": "No hi ha tuts aquí!",
   "empty_column.account_unavailable": "Perfil no disponible",
diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json
index 12365c879d..9d3b416068 100644
--- a/app/javascript/mastodon/locales/fy.json
+++ b/app/javascript/mastodon/locales/fy.json
@@ -222,6 +222,7 @@
   "emoji_button.search_results": "Sykresultaten",
   "emoji_button.symbols": "Symboalen",
   "emoji_button.travel": "Reizgje en lokaasjes",
+  "empty_column.account_hides_collections": "Dizze brûker hat derfoar keazen dizze ynformaasje net beskikber te meitsjen",
   "empty_column.account_suspended": "Account beskoattele",
   "empty_column.account_timeline": "Hjir binne gjin berjochten!",
   "empty_column.account_unavailable": "Profyl net beskikber",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index 75f4a239e7..5cdc575dee 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -41,6 +41,8 @@
   "account.languages": "Keisti prenumeruojamas kalbas",
   "account.locked_info": "Šios paskyros privatumo būsena nustatyta kaip užrakinta. Savininkas (-ė) rankiniu būdu peržiūri, kas gali sekti.",
   "account.media": "Medija",
+  "account.mention": "Paminėti @{name}",
+  "account.moved_to": "{name} nurodė, kad dabar jų nauja paskyra yra:",
   "account.mute": "Užtildyti @{name}",
   "account.muted": "Užtildytas",
   "account.posts": "Toots",
@@ -53,10 +55,15 @@
   "account.unfollow": "Nebesekti",
   "account.unmute_short": "Atitildyti",
   "account_note.placeholder": "Click to add a note",
-  "alert.unexpected.title": "Oi!",
+  "alert.unexpected.message": "Įvyko netikėta klaida.",
+  "alert.unexpected.title": "Ups!",
   "announcement.announcement": "Skelbimas",
+  "attachments_list.unprocessed": "(neapdorotas)",
   "audio.hide": "Slėpti garsą",
   "autosuggest_hashtag.per_week": "{count} per savaitę",
+  "boost_modal.combo": "Gali spausti {combo}, kad praleisti kitą kartą",
+  "bundle_column_error.copy_stacktrace": "Kopijuoti klaidos ataskaitą",
+  "bundle_column_error.error.body": "Užklausos puslapio nepavyko atvaizduoti. Tai gali būti dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos.",
   "bundle_column_error.error.title": "O, ne!",
   "column.domain_blocks": "Hidden domains",
   "column.lists": "Sąrašai",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 63507be791..f16a91d65a 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -222,6 +222,7 @@
   "emoji_button.search_results": "Rezultati iskanja",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Potovanja in kraji",
+  "empty_column.account_hides_collections": "Ta uporabnik se je odločil, da te informacije ne bo dal na voljo",
   "empty_column.account_suspended": "Račun je suspendiran",
   "empty_column.account_timeline": "Tukaj ni objav!",
   "empty_column.account_unavailable": "Profil ni na voljo",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index f64952f7b5..aa948b1f0e 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -222,6 +222,7 @@
   "emoji_button.search_results": "Rezultati pretrage",
   "emoji_button.symbols": "Simboli",
   "emoji_button.travel": "Putovanja i mesta",
+  "empty_column.account_hides_collections": "Ovaj korisnik je odlučio da ove informacije ne učini dostupnim",
   "empty_column.account_suspended": "Nalog je suspendovan",
   "empty_column.account_timeline": "Nema objava ovde!",
   "empty_column.account_unavailable": "Profil je nedostupan",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index ec2c76e8f9..9e67169272 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -222,6 +222,7 @@
   "emoji_button.search_results": "Резултати претраге",
   "emoji_button.symbols": "Симболи",
   "emoji_button.travel": "Путовања и места",
+  "empty_column.account_hides_collections": "Овај корисник је одлучио да ове информације не учини доступним",
   "empty_column.account_suspended": "Налог је суспендован",
   "empty_column.account_timeline": "Нема објава овде!",
   "empty_column.account_unavailable": "Профил је недоступан",
diff --git a/config/locales/activerecord.af.yml b/config/locales/activerecord.af.yml
index c0810999d8..91980644ed 100644
--- a/config/locales/activerecord.af.yml
+++ b/config/locales/activerecord.af.yml
@@ -53,3 +53,7 @@ af:
             position:
               elevated: kan nie hoër as jou huidige rol wees nie
               own_role: kan nie verander word met jou huidige rol nie
+        webhook:
+          attributes:
+            events:
+              invalid_permissions: geleenthede waartoe jy nie toegang het nie mag nie ingesluit word nie
diff --git a/config/locales/af.yml b/config/locales/af.yml
index 1dbf99afe9..74d3495914 100644
--- a/config/locales/af.yml
+++ b/config/locales/af.yml
@@ -5,7 +5,23 @@ af:
     contact_unavailable: NVT
     hosted_on: Mastodon gehuisves op %{domain}
     title: Aangaande
+  accounts:
+    follow: Volg
+    followers:
+      one: Volgeling
+      other: Volgelinge
+    following: Volg
+    nothing_here: Daar is niks hier nie!
+    posts:
+      one: Plasing
+      other: Plasings
+    posts_tab_heading: Plasings
   admin:
+    account_actions:
+      action: Voer aksie uit
+      title: Voer modereer aksie uit op %{acct}
+    account_moderation_notes:
+      create: Los nota
     accounts:
       location:
         local: Plaaslik
@@ -102,6 +118,7 @@ af:
     types:
       bookmarks: Boekmerke
   invites:
+    invalid: Hierdie uitnodiging is nie geldig nie
     title: Nooi ander
   login_activities:
     description_html: Indien jy onbekende aktiwiteite gewaar, oorweeg dit om jou wagwoord te verander en tweefaktorverifikasie te aktiveer.
diff --git a/config/locales/be.yml b/config/locales/be.yml
index 275ef7a82c..96a2720126 100644
--- a/config/locales/be.yml
+++ b/config/locales/be.yml
@@ -1418,6 +1418,7 @@ be:
       '86400': 1 дзень
     expires_in_prompt: Ніколі
     generate: Стварыць запрашальную спасылку
+    invalid: Гэта запрашэнне несапраўднае
     invited_by: 'Вас запрасіў(-ла):'
     max_uses:
       few: "%{count} выкарыстанні"
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 2cdf87d8fd..03b3ff3c23 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -1358,6 +1358,7 @@ ca:
       '86400': 1 dia
     expires_in_prompt: Mai
     generate: Genera
+    invalid: Aquesta invitació no és vàlida
     invited_by: 'Has estat invitat per:'
     max_uses:
       one: 1 ús
diff --git a/config/locales/cy.yml b/config/locales/cy.yml
index 846ce41def..c9d5b88284 100644
--- a/config/locales/cy.yml
+++ b/config/locales/cy.yml
@@ -1468,6 +1468,7 @@ cy:
       '86400': 1 diwrnod
     expires_in_prompt: Byth
     generate: Cynhyrchu dolen wahoddiad
+    invalid: Nid yw'r gwahoddiad hwn yn ddilys
     invited_by: 'Cawsoch eich gwahodd gan:'
     max_uses:
       few: "%{count} defnydd"
diff --git a/config/locales/da.yml b/config/locales/da.yml
index c5d6391853..13010e1adf 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -1110,6 +1110,7 @@ da:
       functional: Din konto er fuldt funktionel.
       pending: Din ansøgning afventer gennemgang af vores medarbejdere. Dette kan tage noget tid. Du modtager en e-mail, hvis din ansøgning godkendes.
       redirecting_to: Din konto er inaktiv, da den pt. er omdirigerer til %{acct}.
+      self_destruct: Da %{domain} er under nedlukning, vil kontoadgangen være begrænset.
       view_strikes: Se tidligere anmeldelser af din konto
     too_fast: Formularen indsendt for hurtigt, forsøg igen.
     use_security_key: Brug sikkerhedsnøgle
@@ -1367,6 +1368,7 @@ da:
       '86400': 1 dag
     expires_in_prompt: Aldrig
     generate: Generér invitationslink
+    invalid: Denne invitation er ikke gyldig
     invited_by: 'Du blev inviteret af:'
     max_uses:
       one: 1 benyttelse
@@ -1579,6 +1581,9 @@ da:
     over_daily_limit: Den daglige grænse på %{limit} planlagte indlæg er nået
     over_total_limit: Grænsen på %{limit} planlagte indlæg er nået
     too_soon: Den planlagte dato skal være i fremtiden
+  self_destruct:
+    lead_html: Desværre lukker <strong>%{domain}</strong> permanent. Har man en konto dér, vil fortsat brug heraf ikke være mulig. Man kan dog stadig anmode om en sikkerhedskopi af sine data.
+    title: Denne server er under nedlukning
   sessions:
     activity: Seneste aktivitet
     browser: Browser
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 2ab90611fb..81fa4b57f3 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -1368,6 +1368,7 @@ de:
       '86400': 1 Tag
     expires_in_prompt: Nie
     generate: Einladungslink erstellen
+    invalid: Diese Einladung ist ungültig
     invited_by: 'Du wurdest eingeladen von:'
     max_uses:
       one: Eine Verwendung
diff --git a/config/locales/devise.be.yml b/config/locales/devise.be.yml
index 3bf35daeda..e5e8aea91e 100644
--- a/config/locales/devise.be.yml
+++ b/config/locales/devise.be.yml
@@ -18,7 +18,7 @@ be:
       unconfirmed: Вы павінны пацвердзіць свой адрас электроннай пошты, перш чым працягнуць
     mailer:
       confirmation_instructions:
-        action: Пацвердзіце адрас электроннай пошты
+        action: Пацвердзіць адрас электроннай пошты
         action_with_app: Пацвердзіць і вярнуцца да %{app}
         explanation: Вы стварылі ўліковы запіс на %{host} з гэтым адрасам электроннай пошты. Вам спатрэбіцца ўсяго адзін клік, каб пацвердзіць яго. Калі гэта былі не вы, то проста праігнаруйце гэты ліст.
         explanation_when_pending: Вы падалі заяўку на запрашэнне на %{host} з гэтым адрасам электроннай пошты. Як толькі вы пацвердзіце свой адрас электроннай пошты, мы разгледзім вашу заяўку. Вы можаце ўвайсці, каб змяніць свае дадзеныя або выдаліць свой уліковы запіс, але вы не можаце атрымаць доступ да большасці функцый, пакуль ваш уліковы запіс не будзе зацверджаны. Калі ваша заяўка будзе адхілена, вашы даныя будуць выдалены, таму ад вас не спатрэбіцца ніякіх дадатковых дзеянняў. Калі гэта былі не вы, ігнаруйце гэты ліст
diff --git a/config/locales/doorkeeper.af.yml b/config/locales/doorkeeper.af.yml
index 504c7f507e..9e05f403f6 100644
--- a/config/locales/doorkeeper.af.yml
+++ b/config/locales/doorkeeper.af.yml
@@ -149,6 +149,7 @@ af:
       write:blocks: blokkeer rekeninge en domeine
       write:bookmarks: laat ’n boekmerk by plasings
       write:conversations: doof en wis gesprekke uit
+      write:favourites: gunsteling plasings
       write:filters: skep filters
       write:follows: volg mense
       write:lists: skep lyste
diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml
index 127f1262a3..9175a1fc1a 100644
--- a/config/locales/es-AR.yml
+++ b/config/locales/es-AR.yml
@@ -1368,6 +1368,7 @@ es-AR:
       '86400': 1 día
     expires_in_prompt: Nunca
     generate: Generar enlace de invitación
+    invalid: Esta invitación no es válida
     invited_by: 'Fuiste invitado por:'
     max_uses:
       one: 1 uso
diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml
index ad2fb184ea..75d329b0ae 100644
--- a/config/locales/es-MX.yml
+++ b/config/locales/es-MX.yml
@@ -1368,6 +1368,7 @@ es-MX:
       '86400': 1 día
     expires_in_prompt: Nunca
     generate: Generar
+    invalid: Esta invitación no es válida
     invited_by: 'Fuiste invitado por:'
     max_uses:
       one: 1 uso
diff --git a/config/locales/es.yml b/config/locales/es.yml
index d071e19a12..34d1c85dc0 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -1368,6 +1368,7 @@ es:
       '86400': 1 día
     expires_in_prompt: Nunca
     generate: Generar
+    invalid: Esta invitación no es válida
     invited_by: 'Fuiste invitado por:'
     max_uses:
       one: 1 uso
diff --git a/config/locales/eu.yml b/config/locales/eu.yml
index 3b7bba6ce3..dd7575c57a 100644
--- a/config/locales/eu.yml
+++ b/config/locales/eu.yml
@@ -1363,6 +1363,7 @@ eu:
       '86400': Egun 1
     expires_in_prompt: Inoiz ez
     generate: Sortu
+    invalid: Gonbidapen hau ez da baliozkoa
     invited_by: 'Honek gonbidatu zaitu:'
     max_uses:
       one: Erabilera 1
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index 8569d2e371..04fb52e757 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -1157,6 +1157,7 @@ fa:
       '86400': ۱ روز
     expires_in_prompt: هیچ وقت
     generate: ساختن
+    invalid: این دعوت‌نامه معتبر نیست
     invited_by: 'دعوت‌کنندهٔ شما:'
     max_uses:
       one: ۱ بار
diff --git a/config/locales/fo.yml b/config/locales/fo.yml
index 8e98cc8649..ffa54f588e 100644
--- a/config/locales/fo.yml
+++ b/config/locales/fo.yml
@@ -1368,6 +1368,7 @@ fo:
       '86400': 1 dag
     expires_in_prompt: Ongantíð
     generate: Ger innbjóðingarleinki
+    invalid: Henda innbjóðing er ikki gildug
     invited_by: 'Tú var bjóðað/ur av:'
     max_uses:
       one: 1 brúk
diff --git a/config/locales/fr-QC.yml b/config/locales/fr-QC.yml
index 41b71f569d..f7425ea320 100644
--- a/config/locales/fr-QC.yml
+++ b/config/locales/fr-QC.yml
@@ -1368,6 +1368,7 @@ fr-QC:
       '86400': 1 jour
     expires_in_prompt: Jamais
     generate: Générer un lien d'invitation
+    invalid: Cette invitation n’est pas valide
     invited_by: 'Vous avez été invité·e par :'
     max_uses:
       one: 1 utilisation
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 79d8b92c2a..289afb2268 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -1368,6 +1368,7 @@ fr:
       '86400': 1 jour
     expires_in_prompt: Jamais
     generate: Générer un lien d'invitation
+    invalid: Cette invitation n’est pas valide
     invited_by: 'Vous avez été invité·e par :'
     max_uses:
       one: 1 utilisation
diff --git a/config/locales/fy.yml b/config/locales/fy.yml
index 011899d15d..de609a14dd 100644
--- a/config/locales/fy.yml
+++ b/config/locales/fy.yml
@@ -1368,6 +1368,7 @@ fy:
       '86400': 1 dei
     expires_in_prompt: Nea
     generate: Utnûgingskeppeling generearje
+    invalid: Dizze útnûging is net jildich
     invited_by: 'Jo binne útnûge troch:'
     max_uses:
       one: 1 kear
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index f86b4fc20b..0075c65920 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -1368,6 +1368,7 @@ gl:
       '86400': 1 día
     expires_in_prompt: Nunca
     generate: Xerar
+    invalid: Este convite non é válido
     invited_by: 'Convidoute:'
     max_uses:
       one: 1 uso
diff --git a/config/locales/he.yml b/config/locales/he.yml
index ea2ab8f29a..11e5db4539 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -113,8 +113,8 @@ he:
       previous_strikes_description_html:
         many: לחשבון הזה יש <strong>%{count}</strong> פסילות.
         one: לחשבון הזה פסילה <strong>אחת</strong>.
-        other: לחשבון הזה <strong>%{count}</strong> פסילות.
-        two: לחשבון הזה <strong>%{count}</strong> פסילות.
+        other: לחשבון הזה יש <strong>%{count}</strong> פסילות.
+        two: לחשבון הזה יש <strong>שתי</strong> פסילות.
       promote: להעלות בדרגה
       protocol: פרטיכל
       public: פומבי
@@ -1418,6 +1418,7 @@ he:
       '86400': יום אחד
     expires_in_prompt: לעולם לא
     generate: יצירת קישור להזמנה
+    invalid: הזמנה זו אינה תקפה
     invited_by: הוזמנת ע"י
     max_uses:
       many: "%{count} שימושים"
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index c01f38a9a9..48f9d5b9d1 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -1368,6 +1368,7 @@ hu:
       '86400': 1 nap
     expires_in_prompt: Soha
     generate: Generálás
+    invalid: Ez a meghívó nem érvényes
     invited_by: 'Téged meghívott:'
     max_uses:
       one: 1 használat
diff --git a/config/locales/is.yml b/config/locales/is.yml
index e0eb955699..390ce0ac03 100644
--- a/config/locales/is.yml
+++ b/config/locales/is.yml
@@ -1372,6 +1372,7 @@ is:
       '86400': 1 dagur
     expires_in_prompt: Aldrei
     generate: Útbúa boðstengil
+    invalid: Þetta boð er ekki gilt
     invited_by: 'Þér var boðið af:'
     max_uses:
       one: 1 afnot
diff --git a/config/locales/it.yml b/config/locales/it.yml
index e62ea620c9..f35e9e42b1 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -1370,6 +1370,7 @@ it:
       '86400': 1 giorno
     expires_in_prompt: Mai
     generate: Genera
+    invalid: Questo invito non è valido
     invited_by: 'Sei stato invitato da:'
     max_uses:
       one: un uso
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index 72eafc8fb8..fb193c75f6 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -1345,6 +1345,7 @@ ko:
       '86400': 1 일
     expires_in_prompt: 영원히
     generate: 초대 링크 생성하기
+    invalid: 이 초대는 올바르지 않습니다
     invited_by: '당신을 초대한 사람:'
     max_uses:
       other: "%{count}회"
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 2f4d813c9e..529eb5a44c 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -383,6 +383,7 @@ lt:
       '86400': 1 dienos
     expires_in_prompt: Niekada
     generate: Generuoti
+    invalid: Šis kvietimas negalioja.
     invited_by: 'Jus pakvietė:'
     max_uses:
       few: "%{count} panaudojimai"
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 2a8a59d764..94a1f29f7f 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -1368,6 +1368,7 @@ nl:
       '86400': 1 dag
     expires_in_prompt: Nooit
     generate: Uitnodigingslink genereren
+    invalid: Deze uitnodiging is niet geldig
     invited_by: 'Jij bent uitgenodigd door:'
     max_uses:
       one: 1 keer
diff --git a/config/locales/nn.yml b/config/locales/nn.yml
index acd6b206e7..4925d44632 100644
--- a/config/locales/nn.yml
+++ b/config/locales/nn.yml
@@ -1368,6 +1368,7 @@ nn:
       '86400': 1 dag
     expires_in_prompt: Aldri
     generate: Lag innbydingslenkje
+    invalid: Denne invitasjonen er ikkje gyldig
     invited_by: 'Du vart innboden av:'
     max_uses:
       one: 1 bruk
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 75085fa5ab..a1058bf9f2 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -1368,6 +1368,7 @@
       '86400': 1 dag
     expires_in_prompt: Aldri
     generate: Generer
+    invalid: Denne invitasjonen er ikke gyldig
     invited_by: 'Du ble invitert av:'
     max_uses:
       one: 1 bruk
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index e02cad0395..6085997248 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -1418,6 +1418,7 @@ pl:
       '86400': dobie
     expires_in_prompt: Nigdy
     generate: Wygeneruj
+    invalid: Niepoprawne zaproszenie
     invited_by: 'Zostałeś(-aś) zaproszony(-a) przez:'
     max_uses:
       few: "%{count} użycia"
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index d99c265fe2..3eb2950bd6 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -1368,6 +1368,7 @@ pt-BR:
       '86400': 1 dia
     expires_in_prompt: Nunca
     generate: Gerar convite
+    invalid: Este convite não é válido
     invited_by: 'Você recebeu convite de:'
     max_uses:
       one: 1 uso
diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml
index a40ac02f4c..ce7479aa86 100644
--- a/config/locales/pt-PT.yml
+++ b/config/locales/pt-PT.yml
@@ -1368,6 +1368,7 @@ pt-PT:
       '86400': 1 dia
     expires_in_prompt: Nunca
     generate: Gerar hiperligação de convite
+    invalid: Este convite não é válido
     invited_by: 'Foi convidado por:'
     max_uses:
       one: 1 uso
diff --git a/config/locales/simple_form.be.yml b/config/locales/simple_form.be.yml
index 1ed6c80848..7ad87cdd9f 100644
--- a/config/locales/simple_form.be.yml
+++ b/config/locales/simple_form.be.yml
@@ -53,7 +53,7 @@ be:
         password: Не менш за 8 сімвалаў
         phrase: Параўнанне адбудзецца нягледзячы на рэгістр тэксту і папярэджанні аб змесціве допісу
         scopes: Якімі API праграм будзе дазволена карыстацца. Калі вы абярэце найвышэйшы ўзровень, не трэба абіраць асобныя.
-        setting_aggregate_reblogs: Не паказваць новыя пашырэнні для допісаў, якія нядаўна пашырылі(уплывае выключна на будучыя пашырэнні)
+        setting_aggregate_reblogs: Не паказваць новыя пашырэнні для допісаў, якія пашырылі нядаўна (закранае толькі нядаўнія пашырэнні)
         setting_always_send_emails: Звычайна лісты з апавяшчэннямі не будуць дасылацца, калі вы актыўна карыстаецеся Mastodon
         setting_default_sensitive: Далікатныя медыя прадвызначана схаваныя. Іх можна адкрыць адзіным клікам
         setting_display_media_default: Хаваць медыя пазначаныя як далікатныя
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index 954ef745da..63779e5bd5 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -592,6 +592,8 @@ sk:
         title: Ohľadom
       appearance:
         title: Vzhľad
+      content_retention:
+        title: Ponechanie obsahu
       discovery:
         follow_recommendations: Odporúčania pre nasledovanie
         profile_directory: Katalóg profilov
@@ -616,6 +618,7 @@ sk:
       delete: Vymaž nahratý súbor
       destroyed_msg: Nahratie bolo zo stránky úspešne vymazané!
     software_updates:
+      critical_update: Kritické — prosím aktualizuj rýchlo
       documentation_link: Zisti viac
       title: Dostupné aktualizácie
       types:
@@ -646,6 +649,10 @@ sk:
       appeal_approved: Namietnuté
       appeal_rejected: Námietka zamietnutá
     system_checks:
+      elasticsearch_preset:
+        action: Pozri dokumentáciu
+      elasticsearch_preset_single_node:
+        action: Pozri dokumentáciu
       rules_check:
         action: Spravuj serverové pravidlá
         message_html: Neurčil/a si žiadne serverové pravidlá.
@@ -925,6 +932,7 @@ sk:
       '86400': 1 deň
     expires_in_prompt: Nikdy
     generate: Vygeneruj
+    invalid: Táto pozvánka je neplatná
     invited_by: 'Bol/a si pozvaný/á užívateľom:'
     max_uses:
       few: "%{count} využití"
diff --git a/config/locales/sl.yml b/config/locales/sl.yml
index 77ddfd6e2b..b31fe118a1 100644
--- a/config/locales/sl.yml
+++ b/config/locales/sl.yml
@@ -1418,6 +1418,7 @@ sl:
       '86400': 1 dan
     expires_in_prompt: Nikoli
     generate: Ustvari
+    invalid: To povabilo ni veljavno
     invited_by: 'Povabil/a vas je:'
     max_uses:
       few: "%{count} uporabe"
diff --git a/config/locales/sq.yml b/config/locales/sq.yml
index e4fb811ce3..bd01a80890 100644
--- a/config/locales/sq.yml
+++ b/config/locales/sq.yml
@@ -1362,6 +1362,7 @@ sq:
       '86400': 1 ditë
     expires_in_prompt: Kurrë
     generate: Prodho lidhje ftese
+    invalid: Kjo ftesë s’është e vlefshme
     invited_by: 'Qetë ftuar nga:'
     max_uses:
       one: 1 përdorim
diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml
index cba4354f06..6ccec7fd9a 100644
--- a/config/locales/sr-Latn.yml
+++ b/config/locales/sr-Latn.yml
@@ -1393,6 +1393,7 @@ sr-Latn:
       '86400': 1 dan
     expires_in_prompt: Nikad
     generate: Generiši
+    invalid: Ova pozivnica nije važeća
     invited_by: 'Pozvao Vas je:'
     max_uses:
       few: "%{count} korišćenja"
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 902f13ad61..407908517b 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -1393,6 +1393,7 @@ sr:
       '86400': 1 дан
     expires_in_prompt: Никад
     generate: Генериши
+    invalid: Ова позивница није важећа
     invited_by: 'Позвао Вас је:'
     max_uses:
       few: "%{count} коришћења"
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index 736d839f91..8126455f4c 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -1368,6 +1368,7 @@ sv:
       '86400': 1 dag
     expires_in_prompt: Aldrig
     generate: Skapa
+    invalid: Ogiltig inbjudan
     invited_by: 'Du blev inbjuden av:'
     max_uses:
       one: 1 användning
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 33d8a898ed..6618998962 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -1343,6 +1343,7 @@ th:
       '86400': 1 วัน
     expires_in_prompt: ไม่เลย
     generate: สร้างลิงก์เชิญ
+    invalid: คำเชิญนี้ไม่ถูกต้อง
     invited_by: 'คุณได้รับเชิญโดย:'
     max_uses:
       other: "%{count} การใช้งาน"
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index c9adfd9132..9d4d95a838 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -1368,6 +1368,7 @@ tr:
       '86400': 1 gün
     expires_in_prompt: Asla
     generate: Davet bağlantısı oluştur
+    invalid: Bu davet geçerli değil
     invited_by: 'Davet edildiniz:'
     max_uses:
       one: 1 kullanım
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index eb947a7d0b..2261c647ba 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -1418,6 +1418,7 @@ uk:
       '86400': 1 день
     expires_in_prompt: Ніколи
     generate: Згенерувати
+    invalid: Це запрошення не дійсне
     invited_by: 'Вас запросив:'
     max_uses:
       few: "%{count} використання"
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index 7902cea4dd..b98193065b 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -1343,6 +1343,7 @@ zh-CN:
       '86400': 1 天后
     expires_in_prompt: 永不过期
     generate: 生成邀请链接
+    invalid: 此邀请无效
     invited_by: 你的邀请人是:
     max_uses:
       other: "%{count} 次"
diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml
index 58aeac8414..f13cedad6e 100644
--- a/config/locales/zh-HK.yml
+++ b/config/locales/zh-HK.yml
@@ -1343,6 +1343,7 @@ zh-HK:
       '86400': 1 天後
     expires_in_prompt: 永不過期
     generate: 生成邀請連結
+    invalid: 此邀請無效
     invited_by: 你的邀請人是:
     max_uses:
       other: "%{count} 次"
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index 7bcc133964..3063b7afd1 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -1345,6 +1345,7 @@ zh-TW:
       '86400': 1 天後
     expires_in_prompt: 永不過期
     generate: 建立邀請連結
+    invalid: 此邀請是無效的
     invited_by: 您的邀請人是:
     max_uses:
       other: "%{count} 則"

From 4eb4e8b22c564222d8bc69cca754cbfb9a524611 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 14 Nov 2023 12:07:13 +0100
Subject: [PATCH 06/14] Update Yarn to v4.0.2 (#27851)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 2230b20ea7..7c73d17c12 100644
--- a/package.json
+++ b/package.json
@@ -242,5 +242,5 @@
     "*.{js,jsx,ts,tsx}": "eslint --fix",
     "*.{css,scss}": "stylelint --fix"
   },
-  "packageManager": "yarn@4.0.1"
+  "packageManager": "yarn@4.0.2"
 }

From d562fb84596f7cf2380e7c1cc465e1d4d478d759 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 14 Nov 2023 09:34:30 -0500
Subject: [PATCH 07/14] Specs for minimal CSP policy in `Api::` controllers
 (#27845)

---
 app/controllers/api/base_controller.rb        | 21 +--------
 .../concerns/api/content_security_policy.rb   | 27 ++++++++++++
 spec/requests/api/v1/csp_spec.rb              | 43 +++++++++++++++++++
 3 files changed, 71 insertions(+), 20 deletions(-)
 create mode 100644 app/controllers/concerns/api/content_security_policy.rb
 create mode 100644 spec/requests/api/v1/csp_spec.rb

diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index c764b45101..135c575658 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -7,6 +7,7 @@ class Api::BaseController < ApplicationController
   include RateLimitHeaders
   include AccessTokenTrackingConcern
   include ApiCachingConcern
+  include Api::ContentSecurityPolicy
 
   skip_before_action :require_functional!, unless: :limited_federation_mode?
 
@@ -17,26 +18,6 @@ class Api::BaseController < ApplicationController
 
   protect_from_forgery with: :null_session
 
-  content_security_policy do |p|
-    # Set every directive that does not have a fallback
-    p.default_src :none
-    p.frame_ancestors :none
-    p.form_action :none
-
-    # Disable every directive with a fallback to cut on response size
-    p.base_uri false
-    p.font_src false
-    p.img_src false
-    p.style_src false
-    p.media_src false
-    p.frame_src false
-    p.manifest_src false
-    p.connect_src false
-    p.script_src false
-    p.child_src false
-    p.worker_src false
-  end
-
   rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
     render json: { error: e.to_s }, status: 422
   end
diff --git a/app/controllers/concerns/api/content_security_policy.rb b/app/controllers/concerns/api/content_security_policy.rb
new file mode 100644
index 0000000000..8116dca57b
--- /dev/null
+++ b/app/controllers/concerns/api/content_security_policy.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Api::ContentSecurityPolicy
+  extend ActiveSupport::Concern
+
+  included do
+    content_security_policy do |policy|
+      # Set every directive that does not have a fallback
+      policy.default_src :none
+      policy.frame_ancestors :none
+      policy.form_action :none
+
+      # Disable every directive with a fallback to cut on response size
+      policy.base_uri false
+      policy.font_src false
+      policy.img_src false
+      policy.style_src false
+      policy.media_src false
+      policy.frame_src false
+      policy.manifest_src false
+      policy.connect_src false
+      policy.script_src false
+      policy.child_src false
+      policy.worker_src false
+    end
+  end
+end
diff --git a/spec/requests/api/v1/csp_spec.rb b/spec/requests/api/v1/csp_spec.rb
new file mode 100644
index 0000000000..2db52ac725
--- /dev/null
+++ b/spec/requests/api/v1/csp_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'API namespace minimal Content-Security-Policy' do
+  before { stub_tests_controller }
+
+  after { Rails.application.reload_routes! }
+
+  it 'returns the correct CSP headers' do
+    get '/api/v1/tests'
+
+    expect(response).to have_http_status(200)
+    expect(response.headers['Content-Security-Policy']).to eq(minimal_csp_headers)
+  end
+
+  private
+
+  def stub_tests_controller
+    stub_const('Api::V1::TestsController', api_tests_controller)
+
+    Rails.application.routes.draw do
+      get '/api/v1/tests', to: 'api/v1/tests#index'
+    end
+  end
+
+  def api_tests_controller
+    Class.new(Api::BaseController) do
+      def index
+        head 200
+      end
+
+      private
+
+      def user_signed_in? = false
+      def current_user = nil
+    end
+  end
+
+  def minimal_csp_headers
+    "default-src 'none'; frame-ancestors 'none'; form-action 'none'"
+  end
+end

From b2c5b20ef27edd948eca8d6bd2014b7a5efaec11 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 14 Nov 2023 09:52:59 -0500
Subject: [PATCH 08/14] Fix `RSpec/AnyInstance` cop (#27810)

---
 .rubocop_todo.yml                             | 17 ----------
 .../activitypub/inboxes_controller_spec.rb    |  2 +-
 .../admin/accounts_controller_spec.rb         |  3 +-
 .../admin/resets_controller_spec.rb           | 18 ++++++++---
 .../auth/sessions_controller_spec.rb          |  6 ++--
 .../confirmations_controller_spec.rb          | 31 +++++++++----------
 .../recovery_codes_controller_spec.rb         |  6 ++--
 spec/lib/request_spec.rb                      |  5 ++-
 spec/lib/status_filter_spec.rb                |  6 ++--
 spec/models/account_spec.rb                   | 14 +++++++--
 spec/models/setting_spec.rb                   |  6 ++--
 .../process_collection_service_spec.rb        |  9 ++++--
 .../activitypub/delivery_worker_spec.rb       |  3 +-
 .../web/push_notification_worker_spec.rb      |  4 +--
 14 files changed, 70 insertions(+), 60 deletions(-)

diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index f9d14fd551..1672046048 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -41,23 +41,6 @@ Metrics/CyclomaticComplexity:
 Metrics/PerceivedComplexity:
   Max: 27
 
-RSpec/AnyInstance:
-  Exclude:
-    - 'spec/controllers/activitypub/inboxes_controller_spec.rb'
-    - 'spec/controllers/admin/accounts_controller_spec.rb'
-    - 'spec/controllers/admin/resets_controller_spec.rb'
-    - 'spec/controllers/auth/sessions_controller_spec.rb'
-    - 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
-    - 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
-    - 'spec/lib/request_spec.rb'
-    - 'spec/lib/status_filter_spec.rb'
-    - 'spec/models/account_spec.rb'
-    - 'spec/models/setting_spec.rb'
-    - 'spec/services/activitypub/process_collection_service_spec.rb'
-    - 'spec/validators/follow_limit_validator_spec.rb'
-    - 'spec/workers/activitypub/delivery_worker_spec.rb'
-    - 'spec/workers/web/push_notification_worker_spec.rb'
-
 # Configuration parameters: CountAsOne.
 RSpec/ExampleLength:
   Max: 22
diff --git a/spec/controllers/activitypub/inboxes_controller_spec.rb b/spec/controllers/activitypub/inboxes_controller_spec.rb
index 030a303266..feca543cb7 100644
--- a/spec/controllers/activitypub/inboxes_controller_spec.rb
+++ b/spec/controllers/activitypub/inboxes_controller_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe ActivityPub::InboxesController do
 
       before do
         allow(ActivityPub::FollowersSynchronizationWorker).to receive(:perform_async).and_return(nil)
-        allow_any_instance_of(Account).to receive(:local_followers_hash).and_return('somehash')
+        allow(remote_account).to receive(:local_followers_hash).and_return('somehash')
 
         request.headers['Collection-Synchronization'] = synchronization_header
         post :create, body: '{}'
diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb
index 3b8fa2f71c..307e819501 100644
--- a/spec/controllers/admin/accounts_controller_spec.rb
+++ b/spec/controllers/admin/accounts_controller_spec.rb
@@ -227,7 +227,8 @@ RSpec.describe Admin::AccountsController do
     let(:account) { Fabricate(:account, domain: 'example.com') }
 
     before do
-      allow_any_instance_of(ResolveAccountService).to receive(:call)
+      service = instance_double(ResolveAccountService, call: nil)
+      allow(ResolveAccountService).to receive(:new).and_return(service)
     end
 
     context 'when user is admin' do
diff --git a/spec/controllers/admin/resets_controller_spec.rb b/spec/controllers/admin/resets_controller_spec.rb
index 16adb8a128..14826973c1 100644
--- a/spec/controllers/admin/resets_controller_spec.rb
+++ b/spec/controllers/admin/resets_controller_spec.rb
@@ -13,12 +13,20 @@ describe Admin::ResetsController do
 
   describe 'POST #create' do
     it 'redirects to admin accounts page' do
-      expect_any_instance_of(User).to receive(:send_reset_password_instructions) do |value|
-        expect(value.account_id).to eq account.id
-      end
-
-      post :create, params: { account_id: account.id }
+      expect do
+        post :create, params: { account_id: account.id }
+      end.to change(Devise.mailer.deliveries, :size).by(2)
 
+      expect(Devise.mailer.deliveries).to have_attributes(
+        first: have_attributes(
+          to: include(account.user.email),
+          subject: I18n.t('devise.mailer.password_change.subject')
+        ),
+        last: have_attributes(
+          to: include(account.user.email),
+          subject: I18n.t('devise.mailer.reset_password_instructions.subject')
+        )
+      )
       expect(response).to redirect_to(admin_account_path(account.id))
     end
   end
diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb
index 049190e2ee..f341d75b79 100644
--- a/spec/controllers/auth/sessions_controller_spec.rb
+++ b/spec/controllers/auth/sessions_controller_spec.rb
@@ -126,7 +126,7 @@ RSpec.describe Auth::SessionsController do
         let!(:previous_login) { Fabricate(:login_activity, user: user, ip: previous_ip) }
 
         before do
-          allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(current_ip)
+          allow(controller.request).to receive(:remote_ip).and_return(current_ip)
           user.update(current_sign_in_at: 1.month.ago)
           post :create, params: { user: { email: user.email, password: user.password } }
         end
@@ -279,7 +279,9 @@ RSpec.describe Auth::SessionsController do
 
         context 'when the server has an decryption error' do
           before do
-            allow_any_instance_of(User).to receive(:validate_and_consume_otp!).and_raise(OpenSSL::Cipher::CipherError)
+            allow(user).to receive(:validate_and_consume_otp!).with(user.current_otp).and_raise(OpenSSL::Cipher::CipherError)
+            allow(User).to receive(:find_by).with(id: user.id).and_return(user)
+
             post :create, params: { user: { otp_attempt: user.current_otp } }, session: { attempt_user_id: user.id, attempt_user_updated_at: user.updated_at.to_s }
           end
 
diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb
index 37381fe1f7..a5a35e91d0 100644
--- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb
+++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb
@@ -61,6 +61,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
           it 'renders page with success' do
             prepare_user_otp_generation
             prepare_user_otp_consumption
+            allow(controller).to receive(:current_user).and_return(user)
 
             expect do
               post :create,
@@ -75,30 +76,28 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do
           end
 
           def prepare_user_otp_generation
-            expect_any_instance_of(User).to receive(:generate_otp_backup_codes!) do |value|
-              expect(value).to eq user
-              otp_backup_codes
-            end
+            allow(user)
+              .to receive(:generate_otp_backup_codes!)
+              .and_return(otp_backup_codes)
           end
 
           def prepare_user_otp_consumption
-            expect_any_instance_of(User).to receive(:validate_and_consume_otp!) do |value, code, options|
-              expect(value).to eq user
-              expect(code).to eq '123456'
-              expect(options).to eq({ otp_secret: 'thisisasecretforthespecofnewview' })
-              true
-            end
+            options = { otp_secret: 'thisisasecretforthespecofnewview' }
+            allow(user)
+              .to receive(:validate_and_consume_otp!)
+              .with('123456', options)
+              .and_return(true)
           end
         end
 
         describe 'when creation fails' do
           subject do
-            expect_any_instance_of(User).to receive(:validate_and_consume_otp!) do |value, code, options|
-              expect(value).to eq user
-              expect(code).to eq '123456'
-              expect(options).to eq({ otp_secret: 'thisisasecretforthespecofnewview' })
-              false
-            end
+            options = { otp_secret: 'thisisasecretforthespecofnewview' }
+            allow(user)
+              .to receive(:validate_and_consume_otp!)
+              .with('123456', options)
+              .and_return(false)
+            allow(controller).to receive(:current_user).and_return(user)
 
             expect do
               post :create,
diff --git a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb
index 630cec428e..28a40e138c 100644
--- a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb
+++ b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb
@@ -9,10 +9,8 @@ describe Settings::TwoFactorAuthentication::RecoveryCodesController do
     it 'updates the codes and shows them on a view when signed in' do
       user = Fabricate(:user)
       otp_backup_codes = user.generate_otp_backup_codes!
-      expect_any_instance_of(User).to receive(:generate_otp_backup_codes!) do |value|
-        expect(value).to eq user
-        otp_backup_codes
-      end
+      allow(user).to receive(:generate_otp_backup_codes!).and_return(otp_backup_codes)
+      allow(controller).to receive(:current_user).and_return(user)
 
       sign_in user, scope: :user
       post :create, session: { challenge_passed_at: Time.now.utc }
diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb
index f0861376b9..c7620cf9b6 100644
--- a/spec/lib/request_spec.rb
+++ b/spec/lib/request_spec.rb
@@ -64,8 +64,11 @@ describe Request do
       end
 
       it 'closes underlying connection' do
-        expect_any_instance_of(HTTP::Client).to receive(:close)
+        allow(subject.send(:http_client)).to receive(:close)
+
         expect { |block| subject.perform(&block) }.to yield_control
+
+        expect(subject.send(:http_client)).to have_received(:close)
       end
 
       it 'returns response which implements body_with_limit' do
diff --git a/spec/lib/status_filter_spec.rb b/spec/lib/status_filter_spec.rb
index c994ad419f..cf6f3c7959 100644
--- a/spec/lib/status_filter_spec.rb
+++ b/spec/lib/status_filter_spec.rb
@@ -23,7 +23,8 @@ describe StatusFilter do
 
       context 'when status policy does not allow show' do
         it 'filters the status' do
-          allow_any_instance_of(StatusPolicy).to receive(:show?).and_return(false)
+          policy = instance_double(StatusPolicy, show?: false)
+          allow(StatusPolicy).to receive(:new).and_return(policy)
 
           expect(filter).to be_filtered
         end
@@ -74,7 +75,8 @@ describe StatusFilter do
 
       context 'when status policy does not allow show' do
         it 'filters the status' do
-          allow_any_instance_of(StatusPolicy).to receive(:show?).and_return(false)
+          policy = instance_double(StatusPolicy, show?: false)
+          allow(StatusPolicy).to receive(:new).and_return(policy)
 
           expect(filter).to be_filtered
         end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index b5d942412e..f77ecb055a 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -209,9 +209,13 @@ RSpec.describe Account do
         expect(account.refresh!).to be_nil
       end
 
-      it 'calls not ResolveAccountService#call' do
-        expect_any_instance_of(ResolveAccountService).to_not receive(:call).with(acct)
+      it 'does not call ResolveAccountService#call' do
+        service = instance_double(ResolveAccountService, call: nil)
+        allow(ResolveAccountService).to receive(:new).and_return(service)
+
         account.refresh!
+
+        expect(service).to_not have_received(:call).with(acct)
       end
     end
 
@@ -219,8 +223,12 @@ RSpec.describe Account do
       let(:domain) { 'example.com' }
 
       it 'calls ResolveAccountService#call' do
-        expect_any_instance_of(ResolveAccountService).to receive(:call).with(acct).once
+        service = instance_double(ResolveAccountService, call: nil)
+        allow(ResolveAccountService).to receive(:new).and_return(service)
+
         account.refresh!
+
+        expect(service).to have_received(:call).with(acct).once
       end
     end
   end
diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb
index b08136a1c1..e98062810e 100644
--- a/spec/models/setting_spec.rb
+++ b/spec/models/setting_spec.rb
@@ -52,7 +52,8 @@ RSpec.describe Setting do
         before do
           allow(RailsSettings::Settings).to receive(:object).with(key).and_return(object)
           allow(described_class).to receive(:default_settings).and_return(default_settings)
-          allow_any_instance_of(Settings::ScopedSettings).to receive(:thing_scoped).and_return(records)
+          settings_double = instance_double(Settings::ScopedSettings, thing_scoped: records)
+          allow(Settings::ScopedSettings).to receive(:new).and_return(settings_double)
           Rails.cache.delete(cache_key)
         end
 
@@ -128,7 +129,8 @@ RSpec.describe Setting do
 
   describe '.all_as_records' do
     before do
-      allow_any_instance_of(Settings::ScopedSettings).to receive(:thing_scoped).and_return(records)
+      settings_double = instance_double(Settings::ScopedSettings, thing_scoped: records)
+      allow(Settings::ScopedSettings).to receive(:new).and_return(settings_double)
       allow(described_class).to receive(:default_settings).and_return(default_settings)
     end
 
diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb
index ede9f5c049..df526daf34 100644
--- a/spec/services/activitypub/process_collection_service_spec.rb
+++ b/spec/services/activitypub/process_collection_service_spec.rb
@@ -76,7 +76,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       let(:forwarder) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/other_account') }
 
       it 'does not process payload if no signature exists' do
-        allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
+        signature_double = instance_double(ActivityPub::LinkedDataSignature, verify_actor!: nil)
+        allow(ActivityPub::LinkedDataSignature).to receive(:new).and_return(signature_double)
         allow(ActivityPub::Activity).to receive(:factory)
 
         subject.call(json, forwarder)
@@ -87,7 +88,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       it 'processes payload with actor if valid signature exists' do
         payload['signature'] = { 'type' => 'RsaSignature2017' }
 
-        allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(actor)
+        signature_double = instance_double(ActivityPub::LinkedDataSignature, verify_actor!: actor)
+        allow(ActivityPub::LinkedDataSignature).to receive(:new).and_return(signature_double)
         allow(ActivityPub::Activity).to receive(:factory).with(instance_of(Hash), actor, instance_of(Hash))
 
         subject.call(json, forwarder)
@@ -98,7 +100,8 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       it 'does not process payload if invalid signature exists' do
         payload['signature'] = { 'type' => 'RsaSignature2017' }
 
-        allow_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
+        signature_double = instance_double(ActivityPub::LinkedDataSignature, verify_actor!: nil)
+        allow(ActivityPub::LinkedDataSignature).to receive(:new).and_return(signature_double)
         allow(ActivityPub::Activity).to receive(:factory)
 
         subject.call(json, forwarder)
diff --git a/spec/workers/activitypub/delivery_worker_spec.rb b/spec/workers/activitypub/delivery_worker_spec.rb
index d39393d507..efce610ae4 100644
--- a/spec/workers/activitypub/delivery_worker_spec.rb
+++ b/spec/workers/activitypub/delivery_worker_spec.rb
@@ -11,7 +11,8 @@ describe ActivityPub::DeliveryWorker do
   let(:payload) { 'test' }
 
   before do
-    allow_any_instance_of(Account).to receive(:remote_followers_hash).with('https://example.com/api').and_return('somehash')
+    allow(sender).to receive(:remote_followers_hash).with('https://example.com/api').and_return('somehash')
+    allow(Account).to receive(:find).with(sender.id).and_return(sender)
   end
 
   describe 'perform' do
diff --git a/spec/workers/web/push_notification_worker_spec.rb b/spec/workers/web/push_notification_worker_spec.rb
index 822ef5257f..637206a409 100644
--- a/spec/workers/web/push_notification_worker_spec.rb
+++ b/spec/workers/web/push_notification_worker_spec.rb
@@ -23,8 +23,8 @@ describe Web::PushNotificationWorker do
 
   describe 'perform' do
     before do
-      allow_any_instance_of(subscription.class).to receive(:contact_email).and_return(contact_email)
-      allow_any_instance_of(subscription.class).to receive(:vapid_key).and_return(vapid_key)
+      allow(subscription).to receive_messages(contact_email: contact_email, vapid_key: vapid_key)
+      allow(Web::PushSubscription).to receive(:find).with(subscription.id).and_return(subscription)
       allow(Webpush::Encryption).to receive(:encrypt).and_return(payload)
       allow(JWT).to receive(:encode).and_return('jwt.encoded.payload')
 

From 7e1a77ea51e6dc4aecbf678f8928aa96698fa072 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 14 Nov 2023 09:53:31 -0500
Subject: [PATCH 09/14] Add base class for `api/v1/timelines/*` controllers
 (#27840)

---
 .../api/v1/timelines/base_controller.rb       | 33 +++++++++++++++++++
 .../api/v1/timelines/home_controller.rb       | 25 +++-----------
 .../api/v1/timelines/list_controller.rb       | 24 +++-----------
 .../api/v1/timelines/public_controller.rb     | 25 +++-----------
 .../api/v1/timelines/tag_controller.rb        | 25 +++-----------
 5 files changed, 52 insertions(+), 80 deletions(-)
 create mode 100644 app/controllers/api/v1/timelines/base_controller.rb

diff --git a/app/controllers/api/v1/timelines/base_controller.rb b/app/controllers/api/v1/timelines/base_controller.rb
new file mode 100644
index 0000000000..173e173cc9
--- /dev/null
+++ b/app/controllers/api/v1/timelines/base_controller.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class Api::V1::Timelines::BaseController < Api::BaseController
+  after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
+
+  private
+
+  def insert_pagination_headers
+    set_pagination_headers(next_path, prev_path)
+  end
+
+  def pagination_max_id
+    @statuses.last.id
+  end
+
+  def pagination_since_id
+    @statuses.first.id
+  end
+
+  def next_path_params
+    permitted_params.merge(max_id: pagination_max_id)
+  end
+
+  def prev_path_params
+    permitted_params.merge(min_id: pagination_since_id)
+  end
+
+  def permitted_params
+    params
+      .slice(*self.class::PERMITTED_PARAMS)
+      .permit(*self.class::PERMITTED_PARAMS)
+  end
+end
diff --git a/app/controllers/api/v1/timelines/home_controller.rb b/app/controllers/api/v1/timelines/home_controller.rb
index 83b8cb4c66..36fdbea647 100644
--- a/app/controllers/api/v1/timelines/home_controller.rb
+++ b/app/controllers/api/v1/timelines/home_controller.rb
@@ -1,9 +1,10 @@
 # frozen_string_literal: true
 
-class Api::V1::Timelines::HomeController < Api::BaseController
+class Api::V1::Timelines::HomeController < Api::V1::Timelines::BaseController
   before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
   before_action :require_user!, only: [:show]
-  after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
+
+  PERMITTED_PARAMS = %i(local limit).freeze
 
   def show
     with_read_replica do
@@ -40,27 +41,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController
     HomeFeed.new(current_account)
   end
 
-  def insert_pagination_headers
-    set_pagination_headers(next_path, prev_path)
-  end
-
-  def pagination_params(core_params)
-    params.slice(:local, :limit).permit(:local, :limit).merge(core_params)
-  end
-
   def next_path
-    api_v1_timelines_home_url pagination_params(max_id: pagination_max_id)
+    api_v1_timelines_home_url next_path_params
   end
 
   def prev_path
-    api_v1_timelines_home_url pagination_params(min_id: pagination_since_id)
-  end
-
-  def pagination_max_id
-    @statuses.last.id
-  end
-
-  def pagination_since_id
-    @statuses.first.id
+    api_v1_timelines_home_url prev_path_params
   end
 end
diff --git a/app/controllers/api/v1/timelines/list_controller.rb b/app/controllers/api/v1/timelines/list_controller.rb
index a15eae468d..14b884ecd9 100644
--- a/app/controllers/api/v1/timelines/list_controller.rb
+++ b/app/controllers/api/v1/timelines/list_controller.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
-class Api::V1::Timelines::ListController < Api::BaseController
+class Api::V1::Timelines::ListController < Api::V1::Timelines::BaseController
   before_action -> { doorkeeper_authorize! :read, :'read:lists' }
   before_action :require_user!
   before_action :set_list
   before_action :set_statuses
 
-  after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
+  PERMITTED_PARAMS = %i(limit).freeze
 
   def show
     render json: @statuses,
@@ -41,27 +41,11 @@ class Api::V1::Timelines::ListController < Api::BaseController
     ListFeed.new(@list)
   end
 
-  def insert_pagination_headers
-    set_pagination_headers(next_path, prev_path)
-  end
-
-  def pagination_params(core_params)
-    params.slice(:limit).permit(:limit).merge(core_params)
-  end
-
   def next_path
-    api_v1_timelines_list_url params[:id], pagination_params(max_id: pagination_max_id)
+    api_v1_timelines_list_url params[:id], next_path_params
   end
 
   def prev_path
-    api_v1_timelines_list_url params[:id], pagination_params(min_id: pagination_since_id)
-  end
-
-  def pagination_max_id
-    @statuses.last.id
-  end
-
-  def pagination_since_id
-    @statuses.first.id
+    api_v1_timelines_list_url params[:id], prev_path_params
   end
 end
diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb
index 5bbd92b9ee..35af8dc4b5 100644
--- a/app/controllers/api/v1/timelines/public_controller.rb
+++ b/app/controllers/api/v1/timelines/public_controller.rb
@@ -1,8 +1,9 @@
 # frozen_string_literal: true
 
-class Api::V1::Timelines::PublicController < Api::BaseController
+class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
   before_action :require_user!, only: [:show], if: :require_auth?
-  after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
+
+  PERMITTED_PARAMS = %i(local remote limit only_media).freeze
 
   def show
     cache_if_unauthenticated!
@@ -42,27 +43,11 @@ class Api::V1::Timelines::PublicController < Api::BaseController
     )
   end
 
-  def insert_pagination_headers
-    set_pagination_headers(next_path, prev_path)
-  end
-
-  def pagination_params(core_params)
-    params.slice(:local, :remote, :limit, :only_media).permit(:local, :remote, :limit, :only_media).merge(core_params)
-  end
-
   def next_path
-    api_v1_timelines_public_url pagination_params(max_id: pagination_max_id)
+    api_v1_timelines_public_url next_path_params
   end
 
   def prev_path
-    api_v1_timelines_public_url pagination_params(min_id: pagination_since_id)
-  end
-
-  def pagination_max_id
-    @statuses.last.id
-  end
-
-  def pagination_since_id
-    @statuses.first.id
+    api_v1_timelines_public_url prev_path_params
   end
 end
diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb
index a79d65c124..4ba439dbb2 100644
--- a/app/controllers/api/v1/timelines/tag_controller.rb
+++ b/app/controllers/api/v1/timelines/tag_controller.rb
@@ -1,9 +1,10 @@
 # frozen_string_literal: true
 
-class Api::V1::Timelines::TagController < Api::BaseController
+class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
   before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth?
   before_action :load_tag
-  after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
+
+  PERMITTED_PARAMS = %i(local limit only_media).freeze
 
   def show
     cache_if_unauthenticated!
@@ -51,27 +52,11 @@ class Api::V1::Timelines::TagController < Api::BaseController
     )
   end
 
-  def insert_pagination_headers
-    set_pagination_headers(next_path, prev_path)
-  end
-
-  def pagination_params(core_params)
-    params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params)
-  end
-
   def next_path
-    api_v1_timelines_tag_url params[:id], pagination_params(max_id: pagination_max_id)
+    api_v1_timelines_tag_url params[:id], next_path_params
   end
 
   def prev_path
-    api_v1_timelines_tag_url params[:id], pagination_params(min_id: pagination_since_id)
-  end
-
-  def pagination_max_id
-    @statuses.last.id
-  end
-
-  def pagination_since_id
-    @statuses.first.id
+    api_v1_timelines_tag_url params[:id], prev_path_params
   end
 end

From 2b038b4f8951845bee470e18ffb11fab29aacb51 Mon Sep 17 00:00:00 2001
From: ppnplus <54897463+ppnplus@users.noreply.github.com>
Date: Tue, 14 Nov 2023 22:33:59 +0700
Subject: [PATCH 10/14] Added Thai diacritics and tone marks in
 HASHTAG_INVALID_CHARS_RE (#26576)

---
 app/models/tag.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/models/tag.rb b/app/models/tag.rb
index 8fab98fb5c..46e55d74f9 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -37,7 +37,7 @@ class Tag < ApplicationRecord
 
   HASHTAG_RE = %r{(?<![=/)\w])#(#{HASHTAG_NAME_PAT})}i
   HASHTAG_NAME_RE = /\A(#{HASHTAG_NAME_PAT})\z/i
-  HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]#{HASHTAG_SEPARATORS}]/
+  HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]\u0E47-\u0E4E#{HASHTAG_SEPARATORS}]/
 
   validates :name, presence: true, format: { with: HASHTAG_NAME_RE }
   validates :display_name, format: { with: HASHTAG_NAME_RE }

From 35b9749b95fc8f61631a8b38f9dad37ec5f36a7a Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 14 Nov 2023 17:53:15 +0100
Subject: [PATCH 11/14] Update dependency webpack-bundle-analyzer to v4.10.0
 (#27852)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 58 +++++++++++++------------------------------------------
 1 file changed, 13 insertions(+), 45 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index 1cb5a42d50..addbe638fd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6430,6 +6430,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"debounce@npm:^1.2.1":
+  version: 1.2.1
+  resolution: "debounce@npm:1.2.1"
+  checksum: 6c9320aa0973fc42050814621a7a8a78146c1975799b5b3cc1becf1f77ba9a5aa583987884230da0842a03f385def452fad5d60db97c3d1c8b824e38a8edf500
+  languageName: node
+  linkType: hard
+
 "debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3":
   version: 2.6.9
   resolution: "debug@npm:2.6.9"
@@ -8903,7 +8910,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"html-escaper@npm:^2.0.0":
+"html-escaper@npm:^2.0.0, html-escaper@npm:^2.0.2":
   version: 2.0.2
   resolution: "html-escaper@npm:2.0.2"
   checksum: 208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0
@@ -11032,20 +11039,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lodash.escape@npm:^4.0.1":
-  version: 4.0.1
-  resolution: "lodash.escape@npm:4.0.1"
-  checksum: 90ade409cec05b6869090476952fdfb84d4d87b1ff4a0e03ebd590f980d9a1248d93ba14579f10d80c6429e4d6af13ba137c28db64cae6dadb71442e54a3ad2b
-  languageName: node
-  linkType: hard
-
-"lodash.flatten@npm:^4.4.0":
-  version: 4.4.0
-  resolution: "lodash.flatten@npm:4.4.0"
-  checksum: 97e8f0d6b61fe4723c02ad0c6e67e51784c4a2c48f56ef283483e556ad01594cf9cec9c773e177bbbdbdb5d19e99b09d2487cb6b6e5dc405c2693e93b125bd3a
-  languageName: node
-  linkType: hard
-
 "lodash.get@npm:^4.0":
   version: 4.4.2
   resolution: "lodash.get@npm:4.4.2"
@@ -11060,13 +11053,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lodash.invokemap@npm:^4.6.0":
-  version: 4.6.0
-  resolution: "lodash.invokemap@npm:4.6.0"
-  checksum: 2bcc5f4b8782a316d55ff139215eb797f576f0f6d3db2755ebba7b35fd6061f8cbe81702a72a30bc6d70073a5dcc461f7570eaddcc9184c2e42ec3023645c6a1
-  languageName: node
-  linkType: hard
-
 "lodash.isarguments@npm:^3.1.0":
   version: 3.1.0
   resolution: "lodash.isarguments@npm:3.1.0"
@@ -11109,13 +11095,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lodash.pullall@npm:^4.2.0":
-  version: 4.2.0
-  resolution: "lodash.pullall@npm:4.2.0"
-  checksum: b129e8d879258c7db04a7dc1c23dd9e37c52f63a04e105faa8d2ab55e97b5a170d5e15cffbb732a36e7f48c4345c07b6fbddfe50e1f5ec301492b6f64a92040c
-  languageName: node
-  linkType: hard
-
 "lodash.sortby@npm:^4.7.0":
   version: 4.7.0
   resolution: "lodash.sortby@npm:4.7.0"
@@ -11137,13 +11116,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lodash.uniqby@npm:^4.7.0":
-  version: 4.7.0
-  resolution: "lodash.uniqby@npm:4.7.0"
-  checksum: c505c0de20ca759599a2ba38710e8fb95ff2d2028e24d86c901ef2c74be8056518571b9b754bfb75053b2818d30dd02243e4a4621a6940c206bbb3f7626db656
-  languageName: node
-  linkType: hard
-
 "lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21":
   version: 4.17.21
   resolution: "lodash@npm:4.17.21"
@@ -17035,29 +17007,25 @@ __metadata:
   linkType: hard
 
 "webpack-bundle-analyzer@npm:^4.8.0":
-  version: 4.9.1
-  resolution: "webpack-bundle-analyzer@npm:4.9.1"
+  version: 4.10.0
+  resolution: "webpack-bundle-analyzer@npm:4.10.0"
   dependencies:
     "@discoveryjs/json-ext": "npm:0.5.7"
     acorn: "npm:^8.0.4"
     acorn-walk: "npm:^8.0.0"
     commander: "npm:^7.2.0"
+    debounce: "npm:^1.2.1"
     escape-string-regexp: "npm:^4.0.0"
     gzip-size: "npm:^6.0.0"
+    html-escaper: "npm:^2.0.2"
     is-plain-object: "npm:^5.0.0"
-    lodash.debounce: "npm:^4.0.8"
-    lodash.escape: "npm:^4.0.1"
-    lodash.flatten: "npm:^4.4.0"
-    lodash.invokemap: "npm:^4.6.0"
-    lodash.pullall: "npm:^4.2.0"
-    lodash.uniqby: "npm:^4.7.0"
     opener: "npm:^1.5.2"
     picocolors: "npm:^1.0.0"
     sirv: "npm:^2.0.3"
     ws: "npm:^7.3.1"
   bin:
     webpack-bundle-analyzer: lib/bin/analyzer.js
-  checksum: dd047c306471e6c389d6d4156ff22402e587140310a065a6191ee380f8251063f73a8ec6ac6d977c1cd634dbb717e2522b5d0b6cc9b0e847d4f15737fd9c65c9
+  checksum: f812a8d3c0198ce518baf742bff656526f3eae69fb7a64c7f0c9cff202f6fb3380cabf3baaae965b8d6ffbbb6fb802eacb373fca03a596a38b01b84cfb2e8329
   languageName: node
   linkType: hard
 

From 36d7d1781f99c66c85cbbde75015b622124b1c3e Mon Sep 17 00:00:00 2001
From: Nick Schonning <nschonni@gmail.com>
Date: Tue, 14 Nov 2023 11:53:38 -0500
Subject: [PATCH 12/14] Add CodeCov for Ruby coverage reports (#23868)

---
 .github/workflows/test-ruby.yml | 8 +++++++-
 Gemfile                         | 1 +
 Gemfile.lock                    | 2 ++
 spec/spec_helper.rb             | 8 ++++++++
 4 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index 07fd25fb1b..101de66ac7 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -94,7 +94,7 @@ jobs:
       DB_HOST: localhost
       DB_USER: postgres
       DB_PASS: postgres
-      DISABLE_SIMPLECOV: true
+      DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
       RAILS_ENV: test
       ALLOW_NOPAM: true
       PAM_ENABLED: true
@@ -137,6 +137,12 @@ jobs:
 
       - run: bin/rspec
 
+      - name: Upload coverage reports to Codecov
+        if: matrix.ruby-version == '.ruby-version'
+        uses: codecov/codecov-action@v3
+        with:
+          files: coverage/lcov/mastodon.lcov
+
   test-e2e:
     name: End to End testing
     runs-on: ubuntu-latest
diff --git a/Gemfile b/Gemfile
index 039e136754..add7b36066 100644
--- a/Gemfile
+++ b/Gemfile
@@ -139,6 +139,7 @@ group :test do
 
   # Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false
   gem 'simplecov', '~> 0.22', require: false
+  gem 'simplecov-lcov', '~> 0.8', require: false
 
   # Stub web requests for specs
   gem 'webmock', '~> 3.18'
diff --git a/Gemfile.lock b/Gemfile.lock
index 84ad19b805..20c958e2e0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -725,6 +725,7 @@ GEM
       simplecov-html (~> 0.11)
       simplecov_json_formatter (~> 0.1)
     simplecov-html (0.12.3)
+    simplecov-lcov (0.8.0)
     simplecov_json_formatter (0.1.4)
     smart_properties (1.17.0)
     sprockets (3.7.2)
@@ -936,6 +937,7 @@ DEPENDENCIES
   simple-navigation (~> 4.4)
   simple_form (~> 5.2)
   simplecov (~> 0.22)
+  simplecov-lcov (~> 0.8)
   sprockets (~> 3.7.2)
   sprockets-rails (~> 3.4)
   stackprof
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 7c97d85953..f5dcefc789 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -2,13 +2,21 @@
 
 if ENV['DISABLE_SIMPLECOV'] != 'true'
   require 'simplecov'
+  require 'simplecov-lcov'
+  SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
+  SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
   SimpleCov.start 'rails' do
+    enable_coverage :branch
+    enable_coverage_for_eval
+
     add_filter 'lib/linter'
     add_group 'Policies', 'app/policies'
     add_group 'Presenters', 'app/presenters'
     add_group 'Serializers', 'app/serializers'
     add_group 'Services', 'app/services'
     add_group 'Validators', 'app/validators'
+
+    add_group 'Libraries', 'lib'
   end
 end
 

From 15b2d7eec59c745b418debf63907d8bd08c4a730 Mon Sep 17 00:00:00 2001
From: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
Date: Tue, 14 Nov 2023 18:43:20 +0100
Subject: [PATCH 13/14] Split streaming server from web server (#24702)

---
 Procfile.dev           |   2 +-
 package.json           |  27 ++--------
 streaming/index.js     |   6 ++-
 streaming/package.json |  39 +++++++++++++++
 yarn.lock              | 109 ++++++++++++++++++++++-------------------
 5 files changed, 108 insertions(+), 75 deletions(-)
 create mode 100644 streaming/package.json

diff --git a/Procfile.dev b/Procfile.dev
index fbb2c2de23..f81333b04c 100644
--- a/Procfile.dev
+++ b/Procfile.dev
@@ -1,4 +1,4 @@
 web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
 sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
-stream: env PORT=4000 yarn run start
+stream: env PORT=4000 yarn workspace @mastodon/streaming start
 webpack: bin/webpack-dev-server
diff --git a/package.json b/package.json
index 7c73d17c12..bcd91e3fcc 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,13 @@
 {
   "name": "@mastodon/mastodon",
   "license": "AGPL-3.0-or-later",
+  "packageManager": "yarn@4.0.2",
   "engines": {
     "node": ">=18"
   },
   "workspaces": [
-    "."
+    ".",
+    "streaming"
   ],
   "scripts": {
     "build:development": "cross-env RAILS_ENV=development NODE_ENV=development ./bin/webpack",
@@ -71,10 +73,8 @@
     "css-loader": "^5.2.7",
     "cssnano": "^6.0.1",
     "detect-passive-events": "^2.0.3",
-    "dotenv": "^16.0.3",
     "emoji-mart": "npm:emoji-mart-lazyload@latest",
     "escape-html": "^1.0.3",
-    "express": "^4.18.2",
     "file-loader": "^6.2.0",
     "font-awesome": "^4.7.0",
     "fuzzysort": "^2.0.4",
@@ -85,21 +85,15 @@
     "immutable": "^4.3.0",
     "imports-loader": "^1.2.0",
     "intl-messageformat": "^10.3.5",
-    "ioredis": "^5.3.2",
     "js-yaml": "^4.1.0",
-    "jsdom": "^22.1.0",
     "lodash": "^4.17.21",
     "mark-loader": "^0.1.6",
     "marky": "^1.2.5",
     "mini-css-extract-plugin": "^1.6.2",
     "mkdirp": "^3.0.1",
-    "npmlog": "^7.0.1",
     "path-complete-extname": "^1.0.0",
-    "pg": "^8.5.0",
-    "pg-connection-string": "^2.6.0",
     "postcss": "^8.4.24",
     "postcss-loader": "^4.3.0",
-    "prom-client": "^15.0.0",
     "prop-types": "^15.8.1",
     "punycode": "^2.3.0",
     "react": "^18.2.0",
@@ -138,7 +132,6 @@
     "tesseract.js": "^2.1.5",
     "tiny-queue": "^0.2.1",
     "twitter-text": "3.1.0",
-    "uuid": "^9.0.0",
     "webpack": "^4.47.0",
     "webpack-assets-manifest": "^4.0.6",
     "webpack-bundle-analyzer": "^4.8.0",
@@ -150,8 +143,7 @@
     "workbox-routing": "^7.0.0",
     "workbox-strategies": "^7.0.0",
     "workbox-webpack-plugin": "^7.0.0",
-    "workbox-window": "^7.0.0",
-    "ws": "^8.12.1"
+    "workbox-window": "^7.0.0"
   },
   "devDependencies": {
     "@formatjs/cli": "^6.1.1",
@@ -160,16 +152,13 @@
     "@types/babel__core": "^7.20.1",
     "@types/emoji-mart": "^3.0.9",
     "@types/escape-html": "^1.0.2",
-    "@types/express": "^4.17.17",
     "@types/hoist-non-react-statics": "^3.3.1",
     "@types/http-link-header": "^1.0.3",
     "@types/intl": "^1.2.0",
     "@types/jest": "^29.5.2",
     "@types/js-yaml": "^4.0.5",
     "@types/lodash": "^4.14.195",
-    "@types/npmlog": "^4.1.4",
     "@types/object-assign": "^4.0.30",
-    "@types/pg": "^8.6.6",
     "@types/prop-types": "^15.7.5",
     "@types/punycode": "^2.1.0",
     "@types/react": "^18.2.7",
@@ -188,7 +177,6 @@
     "@types/react-toggle": "^4.0.3",
     "@types/redux-immutable": "^4.0.3",
     "@types/requestidlecallback": "^0.3.5",
-    "@types/uuid": "^9.0.0",
     "@types/webpack": "^4.41.33",
     "@types/yargs": "^17.0.24",
     "@typescript-eslint/eslint-plugin": "^6.0.0",
@@ -232,15 +220,10 @@
       "optional": true
     }
   },
-  "optionalDependencies": {
-    "bufferutil": "^4.0.7",
-    "utf-8-validate": "^6.0.3"
-  },
   "lint-staged": {
     "*": "prettier --ignore-unknown --write",
     "Capfile|Gemfile|*.{rb,ruby,ru,rake}": "bundle exec rubocop --force-exclusion -a",
     "*.{js,jsx,ts,tsx}": "eslint --fix",
     "*.{css,scss}": "stylelint --fix"
-  },
-  "packageManager": "yarn@4.0.2"
+  }
 }
diff --git a/streaming/index.js b/streaming/index.js
index b3765531c6..e3b63b53a6 100644
--- a/streaming/index.js
+++ b/streaming/index.js
@@ -2,6 +2,7 @@
 
 const fs = require('fs');
 const http = require('http');
+const path = require('path');
 const url = require('url');
 
 const dotenv = require('dotenv');
@@ -17,8 +18,11 @@ const WebSocket = require('ws');
 
 const environment = process.env.NODE_ENV || 'development';
 
+// Correctly detect and load .env or .env.production file based on environment:
+const dotenvFile = environment === 'production' ? '.env.production' : '.env';
+
 dotenv.config({
-  path: environment === 'production' ? '.env.production' : '.env',
+  path: path.resolve(__dirname, path.join('..', dotenvFile))
 });
 
 log.level = process.env.LOG_LEVEL || 'verbose';
diff --git a/streaming/package.json b/streaming/package.json
new file mode 100644
index 0000000000..d3f2144329
--- /dev/null
+++ b/streaming/package.json
@@ -0,0 +1,39 @@
+{
+  "name": "@mastodon/streaming",
+  "license": "AGPL-3.0-or-later",
+  "packageManager": "yarn@4.0.1",
+  "engines": {
+    "node": ">=18"
+  },
+  "description": "Mastodon's Streaming Server",
+  "private": true,
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/mastodon/mastodon.git"
+  },
+  "scripts": {
+    "start": "node ./index.js"
+  },
+  "dependencies": {
+    "dotenv": "^16.0.3",
+    "express": "^4.18.2",
+    "ioredis": "^5.3.2",
+    "jsdom": "^22.1.0",
+    "npmlog": "^7.0.1",
+    "pg": "^8.5.0",
+    "pg-connection-string": "^2.6.0",
+    "prom-client": "^15.0.0",
+    "uuid": "^9.0.0",
+    "ws": "^8.12.1"
+  },
+  "devDependencies": {
+    "@types/express": "^4.17.17",
+    "@types/npmlog": "^4.1.4",
+    "@types/pg": "^8.6.6",
+    "@types/uuid": "^9.0.0"
+  },
+  "optionalDependencies": {
+    "bufferutil": "^4.0.7",
+    "utf-8-validate": "^6.0.3"
+  }
+}
diff --git a/yarn.lock b/yarn.lock
index addbe638fd..c88dd49d03 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2368,16 +2368,13 @@ __metadata:
     "@types/babel__core": "npm:^7.20.1"
     "@types/emoji-mart": "npm:^3.0.9"
     "@types/escape-html": "npm:^1.0.2"
-    "@types/express": "npm:^4.17.17"
     "@types/hoist-non-react-statics": "npm:^3.3.1"
     "@types/http-link-header": "npm:^1.0.3"
     "@types/intl": "npm:^1.2.0"
     "@types/jest": "npm:^29.5.2"
     "@types/js-yaml": "npm:^4.0.5"
     "@types/lodash": "npm:^4.14.195"
-    "@types/npmlog": "npm:^4.1.4"
     "@types/object-assign": "npm:^4.0.30"
-    "@types/pg": "npm:^8.6.6"
     "@types/prop-types": "npm:^15.7.5"
     "@types/punycode": "npm:^2.1.0"
     "@types/react": "npm:^18.2.7"
@@ -2396,7 +2393,6 @@ __metadata:
     "@types/react-toggle": "npm:^4.0.3"
     "@types/redux-immutable": "npm:^4.0.3"
     "@types/requestidlecallback": "npm:^0.3.5"
-    "@types/uuid": "npm:^9.0.0"
     "@types/webpack": "npm:^4.41.33"
     "@types/yargs": "npm:^17.0.24"
     "@typescript-eslint/eslint-plugin": "npm:^6.0.0"
@@ -2412,7 +2408,6 @@ __metadata:
     babel-plugin-preval: "npm:^5.1.0"
     babel-plugin-transform-react-remove-prop-types: "npm:^0.4.24"
     blurhash: "npm:^2.0.5"
-    bufferutil: "npm:^4.0.7"
     circular-dependency-plugin: "npm:^5.2.2"
     classnames: "npm:^2.3.2"
     cocoon-js-vanilla: "npm:^1.3.0"
@@ -2423,7 +2418,6 @@ __metadata:
     css-loader: "npm:^5.2.7"
     cssnano: "npm:^6.0.1"
     detect-passive-events: "npm:^2.0.3"
-    dotenv: "npm:^16.0.3"
     emoji-mart: "npm:emoji-mart-lazyload@latest"
     escape-html: "npm:^1.0.3"
     eslint: "npm:^8.41.0"
@@ -2437,7 +2431,6 @@ __metadata:
     eslint-plugin-promise: "npm:~6.1.1"
     eslint-plugin-react: "npm:~7.33.0"
     eslint-plugin-react-hooks: "npm:^4.6.0"
-    express: "npm:^4.18.2"
     file-loader: "npm:^6.2.0"
     font-awesome: "npm:^4.7.0"
     fuzzysort: "npm:^2.0.4"
@@ -2449,25 +2442,19 @@ __metadata:
     immutable: "npm:^4.3.0"
     imports-loader: "npm:^1.2.0"
     intl-messageformat: "npm:^10.3.5"
-    ioredis: "npm:^5.3.2"
     jest: "npm:^29.5.0"
     jest-environment-jsdom: "npm:^29.5.0"
     js-yaml: "npm:^4.1.0"
-    jsdom: "npm:^22.1.0"
     lint-staged: "npm:^15.0.0"
     lodash: "npm:^4.17.21"
     mark-loader: "npm:^0.1.6"
     marky: "npm:^1.2.5"
     mini-css-extract-plugin: "npm:^1.6.2"
     mkdirp: "npm:^3.0.1"
-    npmlog: "npm:^7.0.1"
     path-complete-extname: "npm:^1.0.0"
-    pg: "npm:^8.5.0"
-    pg-connection-string: "npm:^2.6.0"
     postcss: "npm:^8.4.24"
     postcss-loader: "npm:^4.3.0"
     prettier: "npm:^3.0.0"
-    prom-client: "npm:^15.0.0"
     prop-types: "npm:^15.8.1"
     punycode: "npm:^2.3.0"
     react: "npm:^18.2.0"
@@ -2510,8 +2497,6 @@ __metadata:
     tiny-queue: "npm:^0.2.1"
     twitter-text: "npm:3.1.0"
     typescript: "npm:^5.0.4"
-    utf-8-validate: "npm:^6.0.3"
-    uuid: "npm:^9.0.0"
     webpack: "npm:^4.47.0"
     webpack-assets-manifest: "npm:^4.0.6"
     webpack-bundle-analyzer: "npm:^4.8.0"
@@ -2525,13 +2510,7 @@ __metadata:
     workbox-strategies: "npm:^7.0.0"
     workbox-webpack-plugin: "npm:^7.0.0"
     workbox-window: "npm:^7.0.0"
-    ws: "npm:^8.12.1"
     yargs: "npm:^17.7.2"
-  dependenciesMeta:
-    bufferutil:
-      optional: true
-    utf-8-validate:
-      optional: true
   peerDependenciesMeta:
     react:
       optional: true
@@ -2542,6 +2521,34 @@ __metadata:
   languageName: unknown
   linkType: soft
 
+"@mastodon/streaming@workspace:streaming":
+  version: 0.0.0-use.local
+  resolution: "@mastodon/streaming@workspace:streaming"
+  dependencies:
+    "@types/express": "npm:^4.17.17"
+    "@types/npmlog": "npm:^4.1.4"
+    "@types/pg": "npm:^8.6.6"
+    "@types/uuid": "npm:^9.0.0"
+    bufferutil: "npm:^4.0.7"
+    dotenv: "npm:^16.0.3"
+    express: "npm:^4.18.2"
+    ioredis: "npm:^5.3.2"
+    jsdom: "npm:^22.1.0"
+    npmlog: "npm:^7.0.1"
+    pg: "npm:^8.5.0"
+    pg-connection-string: "npm:^2.6.0"
+    prom-client: "npm:^15.0.0"
+    utf-8-validate: "npm:^6.0.3"
+    uuid: "npm:^9.0.0"
+    ws: "npm:^8.12.1"
+  dependenciesMeta:
+    bufferutil:
+      optional: true
+    utf-8-validate:
+      optional: true
+  languageName: unknown
+  linkType: soft
+
 "@material-symbols/svg-600@npm:^0.14.0":
   version: 0.14.0
   resolution: "@material-symbols/svg-600@npm:0.14.0"
@@ -3056,21 +3063,21 @@ __metadata:
   linkType: hard
 
 "@types/body-parser@npm:*":
-  version: 1.19.4
-  resolution: "@types/body-parser@npm:1.19.4"
+  version: 1.19.5
+  resolution: "@types/body-parser@npm:1.19.5"
   dependencies:
     "@types/connect": "npm:*"
     "@types/node": "npm:*"
-  checksum: bec2b8a97861a960ee415f7ab3c2aeb7f4d779fd364d27ddee46057897ea571735f1f854f5ee41682964315d4e3699f62427998b9c21851d773398ef535f0612
+  checksum: aebeb200f25e8818d8cf39cd0209026750d77c9b85381cdd8deeb50913e4d18a1ebe4b74ca9b0b4d21952511eeaba5e9fbbf739b52731a2061e206ec60d568df
   languageName: node
   linkType: hard
 
 "@types/connect@npm:*":
-  version: 3.4.37
-  resolution: "@types/connect@npm:3.4.37"
+  version: 3.4.38
+  resolution: "@types/connect@npm:3.4.38"
   dependencies:
     "@types/node": "npm:*"
-  checksum: 79fd5c32a8bb5c9548369e6da3221b6a820f3a8c5396d50f6f642712b9f4c1c881ef86bdf48994a4a279e81998563410b8843c5a10dde5521d5ef6a8ae944c3b
+  checksum: 2e1cdba2c410f25649e77856505cd60223250fa12dff7a503e492208dbfdd25f62859918f28aba95315251fd1f5e1ffbfca1e25e73037189ab85dd3f8d0a148c
   languageName: node
   linkType: hard
 
@@ -3115,14 +3122,14 @@ __metadata:
   linkType: hard
 
 "@types/express-serve-static-core@npm:^4.17.33":
-  version: 4.17.39
-  resolution: "@types/express-serve-static-core@npm:4.17.39"
+  version: 4.17.41
+  resolution: "@types/express-serve-static-core@npm:4.17.41"
   dependencies:
     "@types/node": "npm:*"
     "@types/qs": "npm:*"
     "@types/range-parser": "npm:*"
     "@types/send": "npm:*"
-  checksum: b23b005fddd2ba3f7142ec9713f06b5582c7712cdf99c3419d3972364903b348a103c3264d9a761d6497140e3b89bd416454684c4bdeff206b4c59b86e96428a
+  checksum: dc166cbf4475c00a81fbcab120bf7477c527184be11ae149df7f26d9c1082114c68f8d387a2926fe80291b06477c8bbd9231ff4f5775de328e887695aefce269
   languageName: node
   linkType: hard
 
@@ -3175,9 +3182,9 @@ __metadata:
   linkType: hard
 
 "@types/http-errors@npm:*":
-  version: 2.0.3
-  resolution: "@types/http-errors@npm:2.0.3"
-  checksum: 717ce3e8f49a1facb7130fed934108fa8a51ab02089a1049c782e353e0e08e79bdfaac054c2a94db14ea400302e523276387363aa820eaf0031af8ba5d2941dc
+  version: 2.0.4
+  resolution: "@types/http-errors@npm:2.0.4"
+  checksum: 494670a57ad4062fee6c575047ad5782506dd35a6b9ed3894cea65830a94367bd84ba302eb3dde331871f6d70ca287bfedb1b2cf658e6132cd2cbd427ab56836
   languageName: node
   linkType: hard
 
@@ -3279,16 +3286,16 @@ __metadata:
   linkType: hard
 
 "@types/mime@npm:*":
-  version: 3.0.3
-  resolution: "@types/mime@npm:3.0.3"
-  checksum: cef99f8cdc42af9de698027c2a20ba5df12bc9a89dcf5513e70103ebb55e00c5f5c585d02411f4b42fde0e78488342f1b1d3e3546a59a3da42e95fdc616e01eb
+  version: 3.0.4
+  resolution: "@types/mime@npm:3.0.4"
+  checksum: db478bc0f99e40f7b3e01d356a9bdf7817060808a294978111340317bcd80ca35382855578c5b60fbc84ae449674bd9bb38427b18417e1f8f19e4f72f8b242cd
   languageName: node
   linkType: hard
 
 "@types/mime@npm:^1":
-  version: 1.3.4
-  resolution: "@types/mime@npm:1.3.4"
-  checksum: a0a16d26c0e70a1b133e26e7c46b70b3136b7e894396bdb7de1c642f4ac87fdbbba26bf56cf73f001312289d89de4f1c06ab745d9445850df45a5a802564c4d6
+  version: 1.3.5
+  resolution: "@types/mime@npm:1.3.5"
+  checksum: c2ee31cd9b993804df33a694d5aa3fa536511a49f2e06eeab0b484fef59b4483777dbb9e42a4198a0809ffbf698081fdbca1e5c2218b82b91603dfab10a10fbc
   languageName: node
   linkType: hard
 
@@ -3392,16 +3399,16 @@ __metadata:
   linkType: hard
 
 "@types/qs@npm:*":
-  version: 6.9.9
-  resolution: "@types/qs@npm:6.9.9"
-  checksum: aede2a4181a49ae8548a1354bac3f8235cb0c5aab066b10875a3e68e88a199e220f4284e7e2bb75a3c18e5d4ff6abe1a6ce0389ef31b63952cc45e0f4d885ba0
+  version: 6.9.10
+  resolution: "@types/qs@npm:6.9.10"
+  checksum: 6be12e5f062d1b41eb037d59bf9cb65bc9410cedd5e6da832dfd7c8e2b3f4c91e81c9b90b51811140770e5052c6c4e8361181bd9437ddcd4515dc128b7c00353
   languageName: node
   linkType: hard
 
 "@types/range-parser@npm:*":
-  version: 1.2.6
-  resolution: "@types/range-parser@npm:1.2.6"
-  checksum: 46e7fffc54cdacc8fb0cd576f8f9a6436453f0176205d6ec55434a460c7677e78e688673426d5db5e480501b2943ba08a16ececa3a354c222093551c7217fb8f
+  version: 1.2.7
+  resolution: "@types/range-parser@npm:1.2.7"
+  checksum: 361bb3e964ec5133fa40644a0b942279ed5df1949f21321d77de79f48b728d39253e5ce0408c9c17e4e0fd95ca7899da36841686393b9f7a1e209916e9381a3c
   languageName: node
   linkType: hard
 
@@ -3587,23 +3594,23 @@ __metadata:
   linkType: hard
 
 "@types/send@npm:*":
-  version: 0.17.3
-  resolution: "@types/send@npm:0.17.3"
+  version: 0.17.4
+  resolution: "@types/send@npm:0.17.4"
   dependencies:
     "@types/mime": "npm:^1"
     "@types/node": "npm:*"
-  checksum: 773a0cb55ea03eefbe9a0e6d42114e0f84968db30954a131aae9ba7e9ab984a4776915447ebdeab4412d7f11750126614b0b75e99413f75810045bdb3196554a
+  checksum: 7f17fa696cb83be0a104b04b424fdedc7eaba1c9a34b06027239aba513b398a0e2b7279778af521f516a397ced417c96960e5f50fcfce40c4bc4509fb1a5883c
   languageName: node
   linkType: hard
 
 "@types/serve-static@npm:*":
-  version: 1.15.4
-  resolution: "@types/serve-static@npm:1.15.4"
+  version: 1.15.5
+  resolution: "@types/serve-static@npm:1.15.5"
   dependencies:
     "@types/http-errors": "npm:*"
     "@types/mime": "npm:*"
     "@types/node": "npm:*"
-  checksum: 061b38993bf8f2b5033f57147c8ec90e1d1a0d6f734958ceb531ba7cc31192fd272c999cdbc57ede8672787e3aa171ec142dc65a467c04078e43823e7476eb49
+  checksum: 811d1a2f7e74a872195e7a013bcd87a2fb1edf07eaedcb9dcfd20c1eb4bc56ad4ea0d52141c13192c91ccda7c8aeb8a530d8a7e60b9c27f5990d7e62e0fecb03
   languageName: node
   linkType: hard
 

From 998f0684994b9be5ccad986b83308039ff395ef6 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 14 Nov 2023 18:52:34 +0100
Subject: [PATCH 14/14] Update Yarn to v4.0.2 (#27857)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 streaming/package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/streaming/package.json b/streaming/package.json
index d3f2144329..a474e62263 100644
--- a/streaming/package.json
+++ b/streaming/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@mastodon/streaming",
   "license": "AGPL-3.0-or-later",
-  "packageManager": "yarn@4.0.1",
+  "packageManager": "yarn@4.0.2",
   "engines": {
     "node": ">=18"
   },