diff --git a/.eslintrc.js b/.eslintrc.js
index e8e841ecd0..8bc6e08951 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -323,7 +323,7 @@ module.exports = {
         'plugin:import/recommended',
         'plugin:import/typescript',
         'plugin:promise/recommended',
-        'plugin:jsdoc/recommended',
+        'plugin:jsdoc/recommended-typescript',
         'plugin:prettier/recommended',
       ],
 
diff --git a/.rubocop.yml b/.rubocop.yml
index 4eef9d63e6..bd561df1d2 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -53,6 +53,28 @@ Lint/UselessAccessModifier:
   ContextCreatingMethods:
     - class_methods
 
+## Disable most Metrics/*Length cops
+# Reason: those are often triggered and force significant refactors when this happend
+#         but the team feel they are not really improving the code quality.
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength
+Metrics/BlockLength:
+  Enabled: false
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
+Metrics/ClassLength:
+  Enabled: false
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength
+Metrics/MethodLength:
+  Enabled: false
+
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
+Metrics/ModuleLength:
+  Enabled: false
+
+## End Disable Metrics/*Length cops
+
 # Reason: Currently disabled in .rubocop_todo.yml
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize
 Metrics/AbcSize:
@@ -60,88 +82,12 @@ Metrics/AbcSize:
     - 'lib/mastodon/cli/*.rb'
     - db/*migrate/**/*
 
-# Reason: Some functions cannot be broken up, but others may be refactor candidates
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength
-Metrics/BlockLength:
-  CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
-  Exclude:
-    - 'config/routes.rb'
-    - 'lib/mastodon/cli/*.rb'
-    - 'lib/tasks/*.rake'
-    - 'app/models/concerns/account_associations.rb'
-    - 'app/models/concerns/account_interactions.rb'
-    - 'app/models/concerns/ldap_authenticable.rb'
-    - 'app/models/concerns/omniauthable.rb'
-    - 'app/models/concerns/pam_authenticable.rb'
-    - 'app/models/concerns/remotable.rb'
-    - 'app/services/suspend_account_service.rb'
-    - 'app/services/unsuspend_account_service.rb'
-    - 'app/views/accounts/show.rss.ruby'
-    - 'app/views/tags/show.rss.ruby'
-    - 'config/environments/development.rb'
-    - 'config/environments/production.rb'
-    - 'config/initializers/devise.rb'
-    - 'config/initializers/doorkeeper.rb'
-    - 'config/initializers/omniauth.rb'
-    - 'config/initializers/simple_form.rb'
-    - 'config/navigation.rb'
-    - 'config/routes.rb'
-    - 'config/routes/*.rb'
-    - 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
-    - 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
-    - 'lib/paperclip/gif_transcoder.rb'
-
 # Reason:
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting
 Metrics/BlockNesting:
   Exclude:
     - 'lib/mastodon/cli/*.rb'
 
-# Reason: Some Excluded files would be candidates for refactoring but not currently addressed
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
-Metrics/ClassLength:
-  CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
-  Exclude:
-    - 'lib/mastodon/cli/*.rb'
-    - 'app/controllers/admin/accounts_controller.rb'
-    - 'app/controllers/api/base_controller.rb'
-    - 'app/controllers/api/v1/admin/accounts_controller.rb'
-    - 'app/controllers/application_controller.rb'
-    - 'app/controllers/auth/registrations_controller.rb'
-    - 'app/controllers/auth/sessions_controller.rb'
-    - 'app/lib/activitypub/activity.rb'
-    - 'app/lib/activitypub/activity/create.rb'
-    - 'app/lib/activitypub/tag_manager.rb'
-    - 'app/lib/feed_manager.rb'
-    - 'app/lib/link_details_extractor.rb'
-    - 'app/lib/request.rb'
-    - 'app/lib/text_formatter.rb'
-    - 'app/lib/user_settings_decorator.rb'
-    - 'app/mailers/user_mailer.rb'
-    - 'app/models/account.rb'
-    - 'app/models/admin/account_action.rb'
-    - 'app/models/form/account_batch.rb'
-    - 'app/models/media_attachment.rb'
-    - 'app/models/status.rb'
-    - 'app/models/tag.rb'
-    - 'app/models/user.rb'
-    - 'app/serializers/activitypub/actor_serializer.rb'
-    - 'app/serializers/activitypub/note_serializer.rb'
-    - 'app/serializers/rest/status_serializer.rb'
-    - 'app/services/account_search_service.rb'
-    - 'app/services/activitypub/process_account_service.rb'
-    - 'app/services/activitypub/process_status_update_service.rb'
-    - 'app/services/backup_service.rb'
-    - 'app/services/bulk_import_service.rb'
-    - 'app/services/delete_account_service.rb'
-    - 'app/services/fan_out_on_write_service.rb'
-    - 'app/services/fetch_link_card_service.rb'
-    - 'app/services/import_service.rb'
-    - 'app/services/notify_service.rb'
-    - 'app/services/post_status_service.rb'
-    - 'app/services/update_status_service.rb'
-    - 'lib/paperclip/color_extractor.rb'
-
 # Reason: Currently disabled in .rubocop_todo.yml
 # https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity
 Metrics/CyclomaticComplexity:
@@ -149,17 +95,10 @@ Metrics/CyclomaticComplexity:
     - lib/mastodon/cli/*.rb
     - db/*migrate/**/*
 
-# Reason: Currently disabled in .rubocop_todo.yml
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength
-Metrics/MethodLength:
-  CountAsOne: [array, heredoc]
-  Exclude:
-    - 'lib/mastodon/cli/*.rb'
-
 # Reason:
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
-Metrics/ModuleLength:
-  CountAsOne: [array, heredoc]
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsparameterlists
+Metrics/ParameterLists:
+  CountKeywordArgs: false
 
 # Reason: Prevailing style is argument file paths
 # https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index bd8d885f29..4f9e0748f8 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -156,12 +156,6 @@ Metrics/AbcSize:
   Exclude:
     - 'app/serializers/initial_state_serializer.rb'
 
-# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
-# AllowedMethods: refine
-Metrics/BlockLength:
-  Exclude:
-    - 'app/models/concerns/status_safe_reblog_insert.rb'
-
 # Configuration parameters: CountBlocks, Max.
 Metrics/BlockNesting:
   Exclude:
@@ -171,28 +165,6 @@ Metrics/BlockNesting:
 Metrics/CyclomaticComplexity:
   Max: 25
 
-# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
-Metrics/MethodLength:
-  Max: 58
-
-# Configuration parameters: CountComments, Max, CountAsOne.
-Metrics/ModuleLength:
-  Exclude:
-    - 'app/controllers/concerns/signature_verification.rb'
-    - 'app/helpers/application_helper.rb'
-    - 'app/helpers/jsonld_helper.rb'
-    - 'app/models/concerns/account_interactions.rb'
-    - 'app/models/concerns/has_user_settings.rb'
-    - 'lib/sanitize_ext/sanitize_config.rb'
-
-# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
-Metrics/ParameterLists:
-  Exclude:
-    - 'app/models/concerns/account_interactions.rb'
-    - 'app/services/activitypub/fetch_remote_account_service.rb'
-    - 'app/services/activitypub/fetch_remote_actor_service.rb'
-    - 'app/services/activitypub/fetch_remote_status_service.rb'
-
 # Configuration parameters: AllowedMethods, AllowedPatterns.
 Metrics/PerceivedComplexity:
   Max: 28
@@ -895,7 +867,6 @@ Rails/WhereExists:
     - 'app/validators/vote_validator.rb'
     - 'app/workers/move_worker.rb'
     - 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
-    - 'lib/mastodon/cli/email_domain_blocks.rb'
     - 'lib/tasks/tests.rake'
     - 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
     - 'spec/controllers/api/v1/tags_controller_spec.rb'
@@ -957,7 +928,6 @@ Style/FormatStringToken:
   Exclude:
     - 'app/models/privacy_policy.rb'
     - 'config/initializers/devise.rb'
-    - 'lib/mastodon/cli/maintenance.rb'
     - 'lib/paperclip/color_extractor.rb'
 
 # This cop supports unsafe autocorrection (--autocorrect-all).
diff --git a/Gemfile b/Gemfile
index 88ac7c5ae4..51308c24db 100644
--- a/Gemfile
+++ b/Gemfile
@@ -59,7 +59,7 @@ gem 'idn-ruby', require: 'idn'
 gem 'kaminari', '~> 1.2'
 gem 'link_header', '~> 0.0'
 gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
-gem 'nokogiri', '~> 1.14'
+gem 'nokogiri', '~> 1.15'
 gem 'nsa', '~> 0.2'
 gem 'oj', '~> 3.14'
 gem 'ox', '~> 2.14'
diff --git a/Gemfile.lock b/Gemfile.lock
index 0dc69f30fb..7eadbc571d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -439,8 +439,8 @@ GEM
       net-protocol
     net-ssh (7.1.0)
     nio4r (2.5.9)
-    nokogiri (1.14.3)
-      mini_portile2 (~> 2.8.0)
+    nokogiri (1.15.2)
+      mini_portile2 (~> 2.8.2)
       racc (~> 1.4)
     nsa (0.2.8)
       activesupport (>= 4.2, < 7)
@@ -642,7 +642,7 @@ GEM
       activerecord (>= 4.0.0)
       railties (>= 4.0.0)
     semantic_range (3.0.0)
-    sidekiq (6.5.8)
+    sidekiq (6.5.9)
       connection_pool (>= 2.2.5, < 3)
       rack (~> 2.0)
       redis (>= 4.5.0, < 5)
@@ -829,7 +829,7 @@ DEPENDENCIES
   mime-types (~> 3.4.1)
   net-http (~> 0.3.2)
   net-ldap (~> 0.18)
-  nokogiri (~> 1.14)
+  nokogiri (~> 1.15)
   nsa (~> 0.2)
   oj (~> 3.14)
   omniauth (~> 1.9)
diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb
index 00e1e5178b..840a18d3e6 100644
--- a/app/helpers/languages_helper.rb
+++ b/app/helpers/languages_helper.rb
@@ -1,7 +1,5 @@
 # frozen_string_literal: true
 
-# rubocop:disable Metrics/ModuleLength
-
 module LanguagesHelper
   ISO_639_1 = {
     aa: ['Afar', 'Afaraf'].freeze,
diff --git a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
index 36aa4c4ea0..da67602b59 100644
--- a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
+++ b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
@@ -24,7 +24,10 @@ const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
   },
 
   onClose(id) {
-    dispatch(closeModal('ACTIONS'));
+    dispatch(closeModal({
+      modalType: 'ACTIONS',
+      ignoreFocus: false,
+    }));
     dispatch(closeDropdownMenu(id));
   },
 });
diff --git a/app/javascript/flavours/glitch/features/compose/containers/dropdown_container.js b/app/javascript/flavours/glitch/features/compose/containers/dropdown_container.js
index 1f39edbffc..aa156b8e56 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/dropdown_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/dropdown_container.js
@@ -8,7 +8,7 @@ import Dropdown from '../components/dropdown';
 const mapDispatchToProps = dispatch => ({
   isUserTouching,
   onModalOpen: props => dispatch(openModal({ modalType: 'ACTIONS', modalProps: props })),
-  onModalClose: () => dispatch(closeModal()),
+  onModalClose: () => dispatch(closeModal({ modalType: undefined, ignoreFocus: false })),
 });
 
 export default connect(null, mapDispatchToProps)(Dropdown);
diff --git a/app/javascript/flavours/glitch/features/interaction_modal/index.jsx b/app/javascript/flavours/glitch/features/interaction_modal/index.jsx
index db28876476..c9bbecf1af 100644
--- a/app/javascript/flavours/glitch/features/interaction_modal/index.jsx
+++ b/app/javascript/flavours/glitch/features/interaction_modal/index.jsx
@@ -13,7 +13,7 @@ import { registrationsOpen } from 'flavours/glitch/initial_state';
 
 const mapStateToProps = (state, { accountId }) => ({
   displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']),
-  signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up'),
+  signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
 });
 
 const mapDispatchToProps = (dispatch) => ({
diff --git a/app/javascript/flavours/glitch/features/local_settings/index.jsx b/app/javascript/flavours/glitch/features/local_settings/index.jsx
index f78b8a38e9..d4f04c2108 100644
--- a/app/javascript/flavours/glitch/features/local_settings/index.jsx
+++ b/app/javascript/flavours/glitch/features/local_settings/index.jsx
@@ -21,7 +21,10 @@ const mapDispatchToProps = dispatch => ({
     dispatch(changeLocalSetting(setting, value));
   },
   onClose () {
-    dispatch(closeModal());
+    dispatch(closeModal({
+      modalType: undefined,
+      ignoreFocus: false,
+    }));
   },
 });
 
diff --git a/app/javascript/flavours/glitch/features/status/index.jsx b/app/javascript/flavours/glitch/features/status/index.jsx
index b1be455ece..d2e04c2f7e 100644
--- a/app/javascript/flavours/glitch/features/status/index.jsx
+++ b/app/javascript/flavours/glitch/features/status/index.jsx
@@ -163,8 +163,9 @@ const makeMapStateToProps = () => {
 };
 
 const truncate = (str, num) => {
-  if (str.length > num) {
-    return str.slice(0, num) + '…';
+  const arr = Array.from(str);
+  if (arr.length > num) {
+    return arr.slice(0, num).join('') + '…';
   } else {
     return str;
   }
diff --git a/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx b/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx
index 31d43af7b1..45e4e89f15 100644
--- a/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx
+++ b/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx
@@ -16,7 +16,7 @@ const SignInBanner = () => {
 
   let signupButton;
 
-  const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up'));
+  const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up');
 
   if (registrationsOpen) {
     signupButton = (
diff --git a/app/javascript/flavours/glitch/utils/numbers.ts b/app/javascript/flavours/glitch/utils/numbers.ts
index 2bd88406e0..7dd8edff54 100644
--- a/app/javascript/flavours/glitch/utils/numbers.ts
+++ b/app/javascript/flavours/glitch/utils/numbers.ts
@@ -14,14 +14,15 @@ export type DecimalUnits = ValueOf<typeof DECIMAL_UNITS>;
 const TEN_THOUSAND = DECIMAL_UNITS.THOUSAND * 10;
 const TEN_MILLIONS = DECIMAL_UNITS.MILLION * 10;
 
+export type ShortNumber = [number, DecimalUnits, 0 | 1]; // Array of: shorten number, unit of shorten number and maximum fraction digits
+
 /**
- * @param {number} sourceNumber Number to convert to short number
- * @returns {ShortNumber} Calculated short number
+ * @param sourceNumber Number to convert to short number
+ * @returns Calculated short number
  * @example
  * shortNumber(5936);
  * // => [5.936, 1000, 1]
  */
-export type ShortNumber = [number, DecimalUnits, 0 | 1]; // Array of: shorten number, unit of shorten number and maximum fraction digits
 export function toShortNumber(sourceNumber: number): ShortNumber {
   if (sourceNumber < DECIMAL_UNITS.THOUSAND) {
     return [sourceNumber, DECIMAL_UNITS.ONE, 0];
@@ -45,9 +46,9 @@ export function toShortNumber(sourceNumber: number): ShortNumber {
 }
 
 /**
- * @param {number} sourceNumber Original number that is shortened
- * @param {number} division The scale in which short number is displayed
- * @returns {number} Number that can be used for plurals when short form used
+ * @param sourceNumber Original number that is shortened
+ * @param division The scale in which short number is displayed
+ * @returns Number that can be used for plurals when short form used
  * @example
  * pluralReady(1793, DECIMAL_UNITS.THOUSAND)
  * // => 1790
diff --git a/app/javascript/mastodon/features/interaction_modal/index.jsx b/app/javascript/mastodon/features/interaction_modal/index.jsx
index be03b8f5fe..0f17a0896a 100644
--- a/app/javascript/mastodon/features/interaction_modal/index.jsx
+++ b/app/javascript/mastodon/features/interaction_modal/index.jsx
@@ -13,7 +13,7 @@ import { registrationsOpen } from 'mastodon/initial_state';
 
 const mapStateToProps = (state, { accountId }) => ({
   displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']),
-  signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up'),
+  signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
 });
 
 const mapDispatchToProps = (dispatch) => ({
diff --git a/app/javascript/mastodon/features/status/index.jsx b/app/javascript/mastodon/features/status/index.jsx
index 9219e867e3..d5420bb71d 100644
--- a/app/javascript/mastodon/features/status/index.jsx
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -169,8 +169,9 @@ const makeMapStateToProps = () => {
 };
 
 const truncate = (str, num) => {
-  if (str.length > num) {
-    return str.slice(0, num) + '…';
+  const arr = Array.from(str);
+  if (arr.length > num) {
+    return arr.slice(0, num).join('') + '…';
   } else {
     return str;
   }
diff --git a/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx b/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
index dad36134cf..abae34f7fd 100644
--- a/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
+++ b/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
@@ -17,7 +17,7 @@ const SignInBanner = () => {
 
   let signupButton;
 
-  const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up'));
+  const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up');
 
   if (registrationsOpen) {
     signupButton = (
diff --git a/app/javascript/mastodon/utils/numbers.ts b/app/javascript/mastodon/utils/numbers.ts
index a4a028c30e..4c8465f577 100644
--- a/app/javascript/mastodon/utils/numbers.ts
+++ b/app/javascript/mastodon/utils/numbers.ts
@@ -14,14 +14,15 @@ export type DecimalUnits = ValueOf<typeof DECIMAL_UNITS>;
 const TEN_THOUSAND = DECIMAL_UNITS.THOUSAND * 10;
 const TEN_MILLIONS = DECIMAL_UNITS.MILLION * 10;
 
+export type ShortNumber = [number, DecimalUnits, 0 | 1]; // Array of: shorten number, unit of shorten number and maximum fraction digits
+
 /**
- * @param {number} sourceNumber Number to convert to short number
- * @returns {ShortNumber} Calculated short number
+ * @param sourceNumber Number to convert to short number
+ * @returns Calculated short number
  * @example
  * shortNumber(5936);
  * // => [5.936, 1000, 1]
  */
-export type ShortNumber = [number, DecimalUnits, 0 | 1]; // Array of: shorten number, unit of shorten number and maximum fraction digits
 export function toShortNumber(sourceNumber: number): ShortNumber {
   if (sourceNumber < DECIMAL_UNITS.THOUSAND) {
     return [sourceNumber, DECIMAL_UNITS.ONE, 0];
@@ -45,9 +46,9 @@ export function toShortNumber(sourceNumber: number): ShortNumber {
 }
 
 /**
- * @param {number} sourceNumber Original number that is shortened
- * @param {number} division The scale in which short number is displayed
- * @returns {number} Number that can be used for plurals when short form used
+ * @param sourceNumber Original number that is shortened
+ * @param division The scale in which short number is displayed
+ * @returns Number that can be used for plurals when short form used
  * @example
  * pluralReady(1793, DECIMAL_UNITS.THOUSAND)
  * // => 1790
diff --git a/app/lib/settings/extend.rb b/app/lib/settings/extend.rb
deleted file mode 100644
index 5fb2c8aae0..0000000000
--- a/app/lib/settings/extend.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module Settings
-  module Extend
-    def settings
-      @settings ||= ScopedSettings.new(self)
-    end
-  end
-end
diff --git a/app/models/account.rb b/app/models/account.rb
index 2549279c58..7f26f88bea 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -123,7 +123,7 @@ class Account < ApplicationRecord
   scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc, accounts.id desc')) }
   scope :by_recent_sign_in, -> { order(Arel.sql('(case when users.current_sign_in_at is null then 1 else 0 end) asc, users.current_sign_in_at desc, accounts.id desc')) }
   scope :popular, -> { order('account_stats.followers_count desc') }
-  scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomain(domain).select(:domain)) }
+  scope :by_domain_and_subdomains, ->(domain) { where(domain: Instance.by_domain_and_subdomains(domain).select(:domain)) }
   scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }
   scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) }
 
diff --git a/app/models/instance.rb b/app/models/instance.rb
index a80e91e94c..5e6a93f646 100644
--- a/app/models/instance.rb
+++ b/app/models/instance.rb
@@ -22,7 +22,7 @@ class Instance < ApplicationRecord
   end
 
   scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
-  scope :by_domain_and_subdomain, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") }
+  scope :by_domain_and_subdomains, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") }
 
   def self.refresh
     Scenic.database.refresh_materialized_view(table_name, concurrently: true, cascade: false)
diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb
index a2000e5967..a3406e5a57 100644
--- a/app/services/fetch_resource_service.rb
+++ b/app/services/fetch_resource_service.rb
@@ -19,7 +19,7 @@ class FetchResourceService < BaseService
 
   private
 
-  def process(url, terminal = false)
+  def process(url, terminal: false)
     @url = url
 
     perform_request { |response| process_response(response, terminal) }
diff --git a/babel.config.js b/babel.config.js
index 0a2e69a476..986d605495 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -11,7 +11,7 @@ module.exports = (api) => {
     modules: false,
     debug: false,
     include: [
-      'proposal-numeric-separator',
+      'transform-numeric-separator',
     ],
   };
 
@@ -24,8 +24,8 @@ module.exports = (api) => {
     plugins: [
       ['react-intl', { messagesDir: './build/messages' }],
       'preval',
-      '@babel/plugin-proposal-optional-chaining',
-      '@babel/plugin-proposal-nullish-coalescing-operator',
+      '@babel/plugin-transform-optional-chaining',
+      '@babel/plugin-transform-nullish-coalescing-operator',
     ],
     overrides: [
       {
diff --git a/lib/mastodon/cli/accounts.rb b/lib/mastodon/cli/accounts.rb
index 417f126ccd..33520df25d 100644
--- a/lib/mastodon/cli/accounts.rb
+++ b/lib/mastodon/cli/accounts.rb
@@ -113,12 +113,7 @@ module Mastodon::CLI
         say('OK', :green)
         say("New password: #{password}")
       else
-        user.errors.each do |error|
-          say('Failure/Error: ', :red)
-          say(error.attribute)
-          say("    #{error.type}", :red)
-        end
-
+        report_errors(user.errors)
         exit(1)
       end
     end
@@ -189,12 +184,7 @@ module Mastodon::CLI
         say('OK', :green)
         say("New password: #{password}") if options[:reset_password]
       else
-        user.errors.each do |error|
-          say('Failure/Error: ', :red)
-          say(error.attribute)
-          say("    #{error.type}", :red)
-        end
-
+        report_errors(user.errors)
         exit(1)
       end
     end
@@ -217,7 +207,6 @@ module Mastodon::CLI
         exit(1)
       end
 
-      dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
       account = nil
 
       if username.present?
@@ -234,9 +223,9 @@ module Mastodon::CLI
         end
       end
 
-      say("Deleting user with #{account.statuses_count} statuses, this might take a while...#{dry_run}")
-      DeleteAccountService.new.call(account, reserve_email: false) unless options[:dry_run]
-      say("OK#{dry_run}", :green)
+      say("Deleting user with #{account.statuses_count} statuses, this might take a while...#{dry_run_mode_suffix}")
+      DeleteAccountService.new.call(account, reserve_email: false) unless dry_run?
+      say("OK#{dry_run_mode_suffix}", :green)
     end
 
     option :force, type: :boolean, aliases: [:f], description: 'Override public key check'
@@ -291,7 +280,7 @@ module Mastodon::CLI
       Account.remote.select(:uri, 'count(*)').group(:uri).having('count(*) > 1').pluck(:uri).each do |uri|
         say("Duplicates found for #{uri}")
         begin
-          ActivityPub::FetchRemoteAccountService.new.call(uri) unless options[:dry_run]
+          ActivityPub::FetchRemoteAccountService.new.call(uri) unless dry_run?
         rescue => e
           say("Error processing #{uri}: #{e}", :red)
         end
@@ -332,7 +321,6 @@ module Mastodon::CLI
     LONG_DESC
     def cull(*domains)
       skip_threshold = 7.days.ago
-      dry_run        = options[:dry_run] ? ' (DRY RUN)' : ''
       skip_domains   = Concurrent::Set.new
 
       query = Account.remote.where(protocol: :activitypub)
@@ -350,7 +338,7 @@ module Mastodon::CLI
         end
 
         if [404, 410].include?(code)
-          DeleteAccountService.new.call(account, reserve_username: false) unless options[:dry_run]
+          DeleteAccountService.new.call(account, reserve_username: false) unless dry_run?
           1
         else
           # Touch account even during dry run to avoid getting the account into the window again
@@ -358,7 +346,7 @@ module Mastodon::CLI
         end
       end
 
-      say("Visited #{processed} accounts, removed #{culled}#{dry_run}", :green)
+      say("Visited #{processed} accounts, removed #{culled}#{dry_run_mode_suffix}", :green)
 
       unless skip_domains.empty?
         say('The following domains were not available during the check:', :yellow)
@@ -381,21 +369,19 @@ module Mastodon::CLI
       specified with space-separated USERNAMES.
     LONG_DESC
     def refresh(*usernames)
-      dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
-
       if options[:domain] || options[:all]
         scope  = Account.remote
         scope  = scope.where(domain: options[:domain]) if options[:domain]
 
         processed, = parallelize_with_progress(scope) do |account|
-          next if options[:dry_run]
+          next if dry_run?
 
           account.reset_avatar!
           account.reset_header!
           account.save
         end
 
-        say("Refreshed #{processed} accounts#{dry_run}", :green, true)
+        say("Refreshed #{processed} accounts#{dry_run_mode_suffix}", :green, true)
       elsif !usernames.empty?
         usernames.each do |user|
           user, domain = user.split('@')
@@ -406,7 +392,7 @@ module Mastodon::CLI
             exit(1)
           end
 
-          next if options[:dry_run]
+          next if dry_run?
 
           begin
             account.reset_avatar!
@@ -417,7 +403,7 @@ module Mastodon::CLI
           end
         end
 
-        say("OK#{dry_run}", :green)
+        say("OK#{dry_run_mode_suffix}", :green)
       else
         say('No account(s) given', :red)
         exit(1)
@@ -568,8 +554,6 @@ module Mastodon::CLI
       - not muted/blocked by us
     LONG_DESC
     def prune
-      dry_run = options[:dry_run] ? ' (dry run)' : ''
-
       query = Account.remote.where.not(actor_type: %i(Application Service))
       query = query.where('NOT EXISTS (SELECT 1 FROM mentions WHERE account_id = accounts.id)')
       query = query.where('NOT EXISTS (SELECT 1 FROM favourites WHERE account_id = accounts.id)')
@@ -585,11 +569,11 @@ module Mastodon::CLI
         next if account.suspended?
         next if account.silenced?
 
-        account.destroy unless options[:dry_run]
+        account.destroy unless dry_run?
         1
       end
 
-      say("OK, pruned #{deleted} accounts#{dry_run}", :green)
+      say("OK, pruned #{deleted} accounts#{dry_run_mode_suffix}", :green)
     end
 
     option :force, type: :boolean
@@ -667,6 +651,14 @@ module Mastodon::CLI
 
     private
 
+    def report_errors(errors)
+      errors.each do |error|
+        say('Failure/Error: ', :red)
+        say(error.attribute)
+        say("    #{error.type}", :red)
+      end
+    end
+
     def rotate_keys_for_account(account, delay = 0)
       if account.nil?
         say('No such account', :red)
diff --git a/lib/mastodon/cli/domains.rb b/lib/mastodon/cli/domains.rb
index d885c95638..d17b253681 100644
--- a/lib/mastodon/cli/domains.rb
+++ b/lib/mastodon/cli/domains.rb
@@ -34,7 +34,6 @@ module Mastodon::CLI
       When the --purge-domain-blocks option is given, also purge matching domain blocks.
     LONG_DESC
     def purge(*domains)
-      dry_run            = options[:dry_run] ? ' (DRY RUN)' : ''
       domains            = domains.map { |domain| TagManager.instance.normalize_domain(domain) }
       account_scope      = Account.none
       domain_block_scope = DomainBlock.none
@@ -79,23 +78,23 @@ module Mastodon::CLI
 
       # Actually perform the deletions
       processed, = parallelize_with_progress(account_scope) do |account|
-        DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) unless options[:dry_run]
+        DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) unless dry_run?
       end
 
-      say("Removed #{processed} accounts#{dry_run}", :green)
+      say("Removed #{processed} accounts#{dry_run_mode_suffix}", :green)
 
       if options[:purge_domain_blocks]
         domain_block_count = domain_block_scope.count
-        domain_block_scope.in_batches.destroy_all unless options[:dry_run]
-        say("Removed #{domain_block_count} domain blocks#{dry_run}", :green)
+        domain_block_scope.in_batches.destroy_all unless dry_run?
+        say("Removed #{domain_block_count} domain blocks#{dry_run_mode_suffix}", :green)
       end
 
       custom_emojis_count = emoji_scope.count
-      emoji_scope.in_batches.destroy_all unless options[:dry_run]
+      emoji_scope.in_batches.destroy_all unless dry_run?
 
-      Instance.refresh unless options[:dry_run]
+      Instance.refresh unless dry_run?
 
-      say("Removed #{custom_emojis_count} custom emojis#{dry_run}", :green)
+      say("Removed #{custom_emojis_count} custom emojis#{dry_run_mode_suffix}", :green)
     end
 
     option :concurrency, type: :numeric, default: 50, aliases: [:c]
diff --git a/lib/mastodon/cli/email_domain_blocks.rb b/lib/mastodon/cli/email_domain_blocks.rb
index abbbdfe31c..88a84ecb42 100644
--- a/lib/mastodon/cli/email_domain_blocks.rb
+++ b/lib/mastodon/cli/email_domain_blocks.rb
@@ -39,7 +39,7 @@ module Mastodon::CLI
       processed = 0
 
       domains.each do |domain|
-        if EmailDomainBlock.where(domain: domain).exists?
+        if EmailDomainBlock.exists?(domain: domain)
           say("#{domain} is already blocked.", :yellow)
           skipped += 1
           next
@@ -60,7 +60,7 @@ module Mastodon::CLI
         (email_domain_block.other_domains || []).uniq.each do |hostname|
           another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: email_domain_block)
 
-          if EmailDomainBlock.where(domain: hostname).exists?
+          if EmailDomainBlock.exists?(domain: hostname)
             say("#{hostname} is already blocked.", :yellow)
             skipped += 1
             next
diff --git a/lib/mastodon/cli/feeds.rb b/lib/mastodon/cli/feeds.rb
index 342b550ca3..34617e7538 100644
--- a/lib/mastodon/cli/feeds.rb
+++ b/lib/mastodon/cli/feeds.rb
@@ -18,14 +18,12 @@ module Mastodon::CLI
       Otherwise, a single user specified by USERNAME.
     LONG_DESC
     def build(username = nil)
-      dry_run = options[:dry_run] ? '(DRY RUN)' : ''
-
       if options[:all] || username.nil?
         processed, = parallelize_with_progress(Account.joins(:user).merge(User.active)) do |account|
-          PrecomputeFeedService.new.call(account) unless options[:dry_run]
+          PrecomputeFeedService.new.call(account) unless dry_run?
         end
 
-        say("Regenerated feeds for #{processed} accounts #{dry_run}", :green, true)
+        say("Regenerated feeds for #{processed} accounts #{dry_run_mode_suffix}", :green, true)
       elsif username.present?
         account = Account.find_local(username)
 
@@ -34,9 +32,9 @@ module Mastodon::CLI
           exit(1)
         end
 
-        PrecomputeFeedService.new.call(account) unless options[:dry_run]
+        PrecomputeFeedService.new.call(account) unless dry_run?
 
-        say("OK #{dry_run}", :green, true)
+        say("OK #{dry_run_mode_suffix}", :green, true)
       else
         say('No account(s) given', :red)
         exit(1)
diff --git a/lib/mastodon/cli/helper.rb b/lib/mastodon/cli/helper.rb
index b277e16ebd..78931b9a22 100644
--- a/lib/mastodon/cli/helper.rb
+++ b/lib/mastodon/cli/helper.rb
@@ -15,6 +15,10 @@ module Mastodon::CLI
       options[:dry_run]
     end
 
+    def dry_run_mode_suffix
+      dry_run? ? ' (DRY RUN)' : ''
+    end
+
     def create_progress_bar(total = nil)
       ProgressBar.create(total: total, format: '%c/%u |%b%i| %e')
     end
diff --git a/lib/mastodon/cli/main.rb b/lib/mastodon/cli/main.rb
index e61a6f9c46..1594eadce8 100644
--- a/lib/mastodon/cli/main.rb
+++ b/lib/mastodon/cli/main.rb
@@ -94,7 +94,7 @@ module Mastodon::CLI
 
       exit(1) unless prompt.ask('Type in the domain of the server to confirm:', required: true) == Rails.configuration.x.local_domain
 
-      unless options[:dry_run]
+      unless dry_run?
         prompt.warn('This operation WILL NOT be reversible. It can also take a long time.')
         prompt.warn('While the data won\'t be erased locally, the server will be in a BROKEN STATE afterwards.')
         prompt.warn('A running Sidekiq process is required. Do not shut it down until queues clear.')
@@ -104,12 +104,11 @@ module Mastodon::CLI
 
       inboxes   = Account.inboxes
       processed = 0
-      dry_run   = options[:dry_run] ? ' (DRY RUN)' : ''
 
-      Setting.registrations_mode = 'none' unless options[:dry_run]
+      Setting.registrations_mode = 'none' unless dry_run?
 
       if inboxes.empty?
-        Account.local.without_suspended.in_batches.update_all(suspended_at: Time.now.utc, suspension_origin: :local) unless options[:dry_run]
+        Account.local.without_suspended.in_batches.update_all(suspended_at: Time.now.utc, suspension_origin: :local) unless dry_run?
         prompt.ok('It seems like your server has not federated with anything')
         prompt.ok('You can shut it down and delete it any time')
         return
@@ -126,7 +125,7 @@ module Mastodon::CLI
 
         json = Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(account))
 
-        unless options[:dry_run]
+        unless dry_run?
           ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
             [json, account.id, inbox_url]
           end
@@ -140,7 +139,7 @@ module Mastodon::CLI
       Account.local.without_suspended.find_each { |account| delete_account.call(account) }
       Account.local.suspended.joins(:deletion_request).find_each { |account| delete_account.call(account) }
 
-      prompt.ok("Queued #{inboxes.size * processed} items into Sidekiq for #{processed} accounts#{dry_run}")
+      prompt.ok("Queued #{inboxes.size * processed} items into Sidekiq for #{processed} accounts#{dry_run_mode_suffix}")
       prompt.ok('Wait until Sidekiq processes all items, then you can shut everything down and delete the data')
     rescue TTY::Reader::InputInterrupt
       exit(1)
diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb
index b107480359..e9badfb8d1 100644
--- a/lib/mastodon/cli/maintenance.rb
+++ b/lib/mastodon/cli/maintenance.rb
@@ -1,6 +1,5 @@
 # frozen_string_literal: true
 
-require 'tty-prompt'
 require_relative 'base'
 
 module Mastodon::CLI
@@ -134,25 +133,23 @@ module Mastodon::CLI
       Mastodon has to be stopped to run this task, which will take a long time and may be destructive.
     LONG_DESC
     def fix_duplicates
-      @prompt = TTY::Prompt.new
-
       if ActiveRecord::Migrator.current_version < MIN_SUPPORTED_VERSION
-        @prompt.error 'Your version of the database schema is too old and is not supported by this script.'
-        @prompt.error 'Please update to at least Mastodon 3.0.0 before running this script.'
+        say 'Your version of the database schema is too old and is not supported by this script.', :red
+        say 'Please update to at least Mastodon 3.0.0 before running this script.', :red
         exit(1)
       elsif ActiveRecord::Migrator.current_version > MAX_SUPPORTED_VERSION
-        @prompt.warn 'Your version of the database schema is more recent than this script, this may cause unexpected errors.'
-        exit(1) unless @prompt.yes?('Continue anyway? (Yes/No)')
+        say 'Your version of the database schema is more recent than this script, this may cause unexpected errors.', :yellow
+        exit(1) unless yes?('Continue anyway? (Yes/No)')
       end
 
       if Sidekiq::ProcessSet.new.any?
-        @prompt.error 'It seems Sidekiq is running. All Mastodon processes need to be stopped when using this script.'
+        say 'It seems Sidekiq is running. All Mastodon processes need to be stopped when using this script.', :red
         exit(1)
       end
 
-      @prompt.warn 'This task will take a long time to run and is potentially destructive.'
-      @prompt.warn 'Please make sure to stop Mastodon and have a backup.'
-      exit(1) unless @prompt.yes?('Continue? (Yes/No)')
+      say 'This task will take a long time to run and is potentially destructive.', :yellow
+      say 'Please make sure to stop Mastodon and have a backup.', :yellow
+      exit(1) unless yes?('Continue? (Yes/No)')
 
       deduplicate_users!
       deduplicate_account_domain_blocks!
@@ -176,7 +173,7 @@ module Mastodon::CLI
       Scenic.database.refresh_materialized_view('instances', concurrently: true, cascade: false) if ActiveRecord::Migrator.current_version >= 2020_12_06_004238
       Rails.cache.clear
 
-      @prompt.say 'Finished!'
+      say 'Finished!'
     end
 
     private
@@ -184,7 +181,7 @@ module Mastodon::CLI
     def deduplicate_accounts!
       remove_index_if_exists!(:accounts, 'index_accounts_on_username_and_domain_lower')
 
-      @prompt.say 'Deduplicating accounts… for local accounts, you will be asked to chose which account to keep unchanged.'
+      say 'Deduplicating accounts… for local accounts, you will be asked to chose which account to keep unchanged.'
 
       find_duplicate_accounts.each do |row|
         accounts = Account.where(id: row['ids'].split(',')).to_a
@@ -196,14 +193,14 @@ module Mastodon::CLI
         end
       end
 
-      @prompt.say 'Restoring index_accounts_on_username_and_domain_lower…'
+      say 'Restoring index_accounts_on_username_and_domain_lower…'
       if ActiveRecord::Migrator.current_version < 2020_06_20_164023
         ActiveRecord::Base.connection.add_index :accounts, 'lower (username), lower(domain)', name: 'index_accounts_on_username_and_domain_lower', unique: true
       else
         ActiveRecord::Base.connection.add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true
       end
 
-      @prompt.say 'Reindexing textual indexes on accounts…'
+      say 'Reindexing textual indexes on accounts…'
       ActiveRecord::Base.connection.execute('REINDEX INDEX search_index;')
       ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_uri;')
       ActiveRecord::Base.connection.execute('REINDEX INDEX index_accounts_on_url;')
@@ -215,19 +212,18 @@ module Mastodon::CLI
       remove_index_if_exists!(:users, 'index_users_on_remember_token')
       remove_index_if_exists!(:users, 'index_users_on_reset_password_token')
 
-      @prompt.say 'Deduplicating user records…'
+      say 'Deduplicating user records…'
 
       # Deduplicating email
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users GROUP BY email HAVING count(*) > 1").each do |row|
         users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse
         ref_user = users.shift
-        @prompt.warn "Multiple users registered with e-mail address #{ref_user.email}."
-        @prompt.warn "e-mail will be disabled for the following accounts: #{user.map(&:account).map(&:acct).join(', ')}"
-        @prompt.warn 'Please reach out to them and set another address with `tootctl account modify` or delete them.'
+        say "Multiple users registered with e-mail address #{ref_user.email}.", :yellow
+        say "e-mail will be disabled for the following accounts: #{user.map(&:account).map(&:acct).join(', ')}", :yellow
+        say 'Please reach out to them and set another address with `tootctl account modify` or delete them.', :yellow
 
-        i = 0
-        users.each do |user|
-          user.update!(email: "#{i} " + user.email)
+        users.each_with_index do |user, index|
+          user.update!(email: "#{index} " + user.email)
         end
       end
 
@@ -235,7 +231,7 @@ module Mastodon::CLI
       deduplicate_users_process_remember_token
       deduplicate_users_process_password_token
 
-      @prompt.say 'Restoring users indexes…'
+      say 'Restoring users indexes…'
       ActiveRecord::Base.connection.add_index :users, ['confirmation_token'], name: 'index_users_on_confirmation_token', unique: true
       ActiveRecord::Base.connection.add_index :users, ['email'], name: 'index_users_on_email', unique: true
       ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if ActiveRecord::Migrator.current_version < 2022_01_18_183010
@@ -250,7 +246,7 @@ module Mastodon::CLI
     def deduplicate_users_process_confirmation_token
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE confirmation_token IS NOT NULL GROUP BY confirmation_token HAVING count(*) > 1").each do |row|
         users = User.where(id: row['ids'].split(',')).sort_by(&:created_at).reverse.drop(1)
-        @prompt.warn "Unsetting confirmation token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}"
+        say "Unsetting confirmation token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}", :yellow
 
         users.each do |user|
           user.update!(confirmation_token: nil)
@@ -262,7 +258,7 @@ module Mastodon::CLI
       if ActiveRecord::Migrator.current_version < 2022_01_18_183010
         ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE remember_token IS NOT NULL GROUP BY remember_token HAVING count(*) > 1").each do |row|
           users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1)
-          @prompt.warn "Unsetting remember token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}"
+          say "Unsetting remember token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}", :yellow
 
           users.each do |user|
             user.update!(remember_token: nil)
@@ -274,7 +270,7 @@ module Mastodon::CLI
     def deduplicate_users_process_password_token
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE reset_password_token IS NOT NULL GROUP BY reset_password_token HAVING count(*) > 1").each do |row|
         users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1)
-        @prompt.warn "Unsetting password reset token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}"
+        say "Unsetting password reset token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}", :yellow
 
         users.each do |user|
           user.update!(reset_password_token: nil)
@@ -285,12 +281,12 @@ module Mastodon::CLI
     def deduplicate_account_domain_blocks!
       remove_index_if_exists!(:account_domain_blocks, 'index_account_domain_blocks_on_account_id_and_domain')
 
-      @prompt.say 'Removing duplicate account domain blocks…'
+      say 'Removing duplicate account domain blocks…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM account_domain_blocks GROUP BY account_id, domain HAVING count(*) > 1").each do |row|
         AccountDomainBlock.where(id: row['ids'].split(',').drop(1)).delete_all
       end
 
-      @prompt.say 'Restoring account domain blocks indexes…'
+      say 'Restoring account domain blocks indexes…'
       ActiveRecord::Base.connection.add_index :account_domain_blocks, %w(account_id domain), name: 'index_account_domain_blocks_on_account_id_and_domain', unique: true
     end
 
@@ -299,12 +295,12 @@ module Mastodon::CLI
 
       remove_index_if_exists!(:account_identity_proofs, 'index_account_proofs_on_account_and_provider_and_username')
 
-      @prompt.say 'Removing duplicate account identity proofs…'
+      say 'Removing duplicate account identity proofs…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM account_identity_proofs GROUP BY account_id, provider, provider_username HAVING count(*) > 1").each do |row|
         AccountIdentityProof.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring account identity proofs indexes…'
+      say 'Restoring account identity proofs indexes…'
       ActiveRecord::Base.connection.add_index :account_identity_proofs, %w(account_id provider provider_username), name: 'index_account_proofs_on_account_and_provider_and_username', unique: true
     end
 
@@ -313,19 +309,19 @@ module Mastodon::CLI
 
       remove_index_if_exists!(:announcement_reactions, 'index_announcement_reactions_on_account_id_and_announcement_id')
 
-      @prompt.say 'Removing duplicate account identity proofs…'
+      say 'Removing duplicate account identity proofs…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM announcement_reactions GROUP BY account_id, announcement_id, name HAVING count(*) > 1").each do |row|
         AnnouncementReaction.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring announcement_reactions indexes…'
+      say 'Restoring announcement_reactions indexes…'
       ActiveRecord::Base.connection.add_index :announcement_reactions, %w(account_id announcement_id name), name: 'index_announcement_reactions_on_account_id_and_announcement_id', unique: true
     end
 
     def deduplicate_conversations!
       remove_index_if_exists!(:conversations, 'index_conversations_on_uri')
 
-      @prompt.say 'Deduplicating conversations…'
+      say 'Deduplicating conversations…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM conversations WHERE uri IS NOT NULL GROUP BY uri HAVING count(*) > 1").each do |row|
         conversations = Conversation.where(id: row['ids'].split(',')).sort_by(&:id).reverse
 
@@ -337,7 +333,7 @@ module Mastodon::CLI
         end
       end
 
-      @prompt.say 'Restoring conversations indexes…'
+      say 'Restoring conversations indexes…'
       if ActiveRecord::Migrator.current_version < 2022_03_07_083603
         ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true
       else
@@ -348,7 +344,7 @@ module Mastodon::CLI
     def deduplicate_custom_emojis!
       remove_index_if_exists!(:custom_emojis, 'index_custom_emojis_on_shortcode_and_domain')
 
-      @prompt.say 'Deduplicating custom_emojis…'
+      say 'Deduplicating custom_emojis…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM custom_emojis GROUP BY shortcode, domain HAVING count(*) > 1").each do |row|
         emojis = CustomEmoji.where(id: row['ids'].split(',')).sort_by(&:id).reverse
 
@@ -360,14 +356,14 @@ module Mastodon::CLI
         end
       end
 
-      @prompt.say 'Restoring custom_emojis indexes…'
+      say 'Restoring custom_emojis indexes…'
       ActiveRecord::Base.connection.add_index :custom_emojis, %w(shortcode domain), name: 'index_custom_emojis_on_shortcode_and_domain', unique: true
     end
 
     def deduplicate_custom_emoji_categories!
       remove_index_if_exists!(:custom_emoji_categories, 'index_custom_emoji_categories_on_name')
 
-      @prompt.say 'Deduplicating custom_emoji_categories…'
+      say 'Deduplicating custom_emoji_categories…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM custom_emoji_categories GROUP BY name HAVING count(*) > 1").each do |row|
         categories = CustomEmojiCategory.where(id: row['ids'].split(',')).sort_by(&:id).reverse
 
@@ -379,26 +375,26 @@ module Mastodon::CLI
         end
       end
 
-      @prompt.say 'Restoring custom_emoji_categories indexes…'
+      say 'Restoring custom_emoji_categories indexes…'
       ActiveRecord::Base.connection.add_index :custom_emoji_categories, ['name'], name: 'index_custom_emoji_categories_on_name', unique: true
     end
 
     def deduplicate_domain_allows!
       remove_index_if_exists!(:domain_allows, 'index_domain_allows_on_domain')
 
-      @prompt.say 'Deduplicating domain_allows…'
+      say 'Deduplicating domain_allows…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM domain_allows GROUP BY domain HAVING count(*) > 1").each do |row|
         DomainAllow.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring domain_allows indexes…'
+      say 'Restoring domain_allows indexes…'
       ActiveRecord::Base.connection.add_index :domain_allows, ['domain'], name: 'index_domain_allows_on_domain', unique: true
     end
 
     def deduplicate_domain_blocks!
       remove_index_if_exists!(:domain_blocks, 'index_domain_blocks_on_domain')
 
-      @prompt.say 'Deduplicating domain_allows…'
+      say 'Deduplicating domain_allows…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM domain_blocks GROUP BY domain HAVING count(*) > 1").each do |row|
         domain_blocks = DomainBlock.where(id: row['ids'].split(',')).by_severity.reverse.to_a
 
@@ -415,7 +411,7 @@ module Mastodon::CLI
         domain_blocks.each(&:destroy)
       end
 
-      @prompt.say 'Restoring domain_blocks indexes…'
+      say 'Restoring domain_blocks indexes…'
       ActiveRecord::Base.connection.add_index :domain_blocks, ['domain'], name: 'index_domain_blocks_on_domain', unique: true
     end
 
@@ -424,37 +420,37 @@ module Mastodon::CLI
 
       remove_index_if_exists!(:unavailable_domains, 'index_unavailable_domains_on_domain')
 
-      @prompt.say 'Deduplicating unavailable_domains…'
+      say 'Deduplicating unavailable_domains…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM unavailable_domains GROUP BY domain HAVING count(*) > 1").each do |row|
         UnavailableDomain.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring domain_allows indexes…'
+      say 'Restoring domain_allows indexes…'
       ActiveRecord::Base.connection.add_index :unavailable_domains, ['domain'], name: 'index_unavailable_domains_on_domain', unique: true
     end
 
     def deduplicate_email_domain_blocks!
       remove_index_if_exists!(:email_domain_blocks, 'index_email_domain_blocks_on_domain')
 
-      @prompt.say 'Deduplicating email_domain_blocks…'
+      say 'Deduplicating email_domain_blocks…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM email_domain_blocks GROUP BY domain HAVING count(*) > 1").each do |row|
         domain_blocks = EmailDomainBlock.where(id: row['ids'].split(',')).sort_by { |b| b.parent.nil? ? 1 : 0 }.to_a
         domain_blocks.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring email_domain_blocks indexes…'
+      say 'Restoring email_domain_blocks indexes…'
       ActiveRecord::Base.connection.add_index :email_domain_blocks, ['domain'], name: 'index_email_domain_blocks_on_domain', unique: true
     end
 
     def deduplicate_media_attachments!
       remove_index_if_exists!(:media_attachments, 'index_media_attachments_on_shortcode')
 
-      @prompt.say 'Deduplicating media_attachments…'
+      say 'Deduplicating media_attachments…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM media_attachments WHERE shortcode IS NOT NULL GROUP BY shortcode HAVING count(*) > 1").each do |row|
         MediaAttachment.where(id: row['ids'].split(',').drop(1)).update_all(shortcode: nil)
       end
 
-      @prompt.say 'Restoring media_attachments indexes…'
+      say 'Restoring media_attachments indexes…'
       if ActiveRecord::Migrator.current_version < 2022_03_10_060626
         ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true
       else
@@ -465,19 +461,19 @@ module Mastodon::CLI
     def deduplicate_preview_cards!
       remove_index_if_exists!(:preview_cards, 'index_preview_cards_on_url')
 
-      @prompt.say 'Deduplicating preview_cards…'
+      say 'Deduplicating preview_cards…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM preview_cards GROUP BY url HAVING count(*) > 1").each do |row|
         PreviewCard.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring preview_cards indexes…'
+      say 'Restoring preview_cards indexes…'
       ActiveRecord::Base.connection.add_index :preview_cards, ['url'], name: 'index_preview_cards_on_url', unique: true
     end
 
     def deduplicate_statuses!
       remove_index_if_exists!(:statuses, 'index_statuses_on_uri')
 
-      @prompt.say 'Deduplicating statuses…'
+      say 'Deduplicating statuses…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM statuses WHERE uri IS NOT NULL GROUP BY uri HAVING count(*) > 1").each do |row|
         statuses = Status.where(id: row['ids'].split(',')).sort_by(&:id)
         ref_status = statuses.shift
@@ -487,7 +483,7 @@ module Mastodon::CLI
         end
       end
 
-      @prompt.say 'Restoring statuses indexes…'
+      say 'Restoring statuses indexes…'
       if ActiveRecord::Migrator.current_version < 2022_03_10_060706
         ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true
       else
@@ -499,7 +495,7 @@ module Mastodon::CLI
       remove_index_if_exists!(:tags, 'index_tags_on_name_lower')
       remove_index_if_exists!(:tags, 'index_tags_on_name_lower_btree')
 
-      @prompt.say 'Deduplicating tags…'
+      say 'Deduplicating tags…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM tags GROUP BY lower((name)::text) HAVING count(*) > 1").each do |row|
         tags = Tag.where(id: row['ids'].split(',')).sort_by { |t| [t.usable?, t.trendable?, t.listable?].count(false) }
         ref_tag = tags.shift
@@ -509,7 +505,7 @@ module Mastodon::CLI
         end
       end
 
-      @prompt.say 'Restoring tags indexes…'
+      say 'Restoring tags indexes…'
       if ActiveRecord::Migrator.current_version < 2021_04_21_121431
         ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true
       else
@@ -522,12 +518,12 @@ module Mastodon::CLI
 
       remove_index_if_exists!(:webauthn_credentials, 'index_webauthn_credentials_on_external_id')
 
-      @prompt.say 'Deduplicating webauthn_credentials…'
+      say 'Deduplicating webauthn_credentials…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webauthn_credentials GROUP BY external_id HAVING count(*) > 1").each do |row|
         WebauthnCredential.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring webauthn_credentials indexes…'
+      say 'Restoring webauthn_credentials indexes…'
       ActiveRecord::Base.connection.add_index :webauthn_credentials, ['external_id'], name: 'index_webauthn_credentials_on_external_id', unique: true
     end
 
@@ -536,28 +532,37 @@ module Mastodon::CLI
 
       remove_index_if_exists!(:webhooks, 'index_webhooks_on_url')
 
-      @prompt.say 'Deduplicating webhooks…'
+      say 'Deduplicating webhooks…'
       ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM webhooks GROUP BY url HAVING count(*) > 1").each do |row|
         Webhooks.where(id: row['ids'].split(',')).sort_by(&:id).reverse.drop(1).each(&:destroy)
       end
 
-      @prompt.say 'Restoring webhooks indexes…'
+      say 'Restoring webhooks indexes…'
       ActiveRecord::Base.connection.add_index :webhooks, ['url'], name: 'index_webhooks_on_url', unique: true
     end
 
     def deduplicate_local_accounts!(accounts)
       accounts = accounts.sort_by(&:id).reverse
 
-      @prompt.warn "Multiple local accounts were found for username '#{accounts.first.username}'."
-      @prompt.warn 'All those accounts are distinct accounts but only the most recently-created one is fully-functional.'
+      say "Multiple local accounts were found for username '#{accounts.first.username}'.", :yellow
+      say 'All those accounts are distinct accounts but only the most recently-created one is fully-functional.', :yellow
 
       accounts.each_with_index do |account, idx|
-        @prompt.say format('%2d. %s: created at: %s; updated at: %s; last logged in at: %s; statuses: %5d; last status at: %s', idx, account.username, account.created_at, account.updated_at, account.user&.last_sign_in_at&.to_s || 'N/A', account.account_stat&.statuses_count || 0, account.account_stat&.last_status_at || 'N/A')
+        say format(
+          '%<index>2d. %<username>s: created at: %<created_at>s; updated at: %<updated_at>s; last logged in at: %<last_log_in_at>s; statuses: %<status_count>5d; last status at: %<last_status_at>s',
+          index: idx,
+          username: account.username,
+          created_at: account.created_at,
+          updated_at: account.updated_at,
+          last_log_in_at: account.user&.last_sign_in_at&.to_s || 'N/A',
+          status_count: account.account_stat&.statuses_count || 0,
+          last_status_at: account.account_stat&.last_status_at || 'N/A'
+        )
       end
 
-      @prompt.say 'Please chose the one to keep unchanged, other ones will be automatically renamed.'
+      say 'Please chose the one to keep unchanged, other ones will be automatically renamed.'
 
-      ref_id = @prompt.ask('Account to keep unchanged:') do |q|
+      ref_id = ask('Account to keep unchanged:') do |q|
         q.required true
         q.default 0
         q.convert :int
diff --git a/lib/mastodon/cli/media.rb b/lib/mastodon/cli/media.rb
index 045f6351ad..40b270ffb2 100644
--- a/lib/mastodon/cli/media.rb
+++ b/lib/mastodon/cli/media.rb
@@ -35,12 +35,12 @@ module Mastodon::CLI
         say('--prune-profiles and --remove-headers should not be specified simultaneously', :red, true)
         exit(1)
       end
+
       if options[:include_follows] && !(options[:prune_profiles] || options[:remove_headers])
         say('--include-follows can only be used with --prune-profiles or --remove-headers', :red, true)
         exit(1)
       end
-      time_ago        = options[:days].days.ago
-      dry_run         = options[:dry_run] ? ' (DRY RUN)' : ''
+      time_ago = options[:days].days.ago
 
       if options[:prune_profiles] || options[:remove_headers]
         processed, aggregate = parallelize_with_progress(Account.remote.where({ last_webfingered_at: ..time_ago, updated_at: ..time_ago })) do |account|
@@ -51,7 +51,7 @@ module Mastodon::CLI
           size = (account.header_file_size || 0)
           size += (account.avatar_file_size || 0) if options[:prune_profiles]
 
-          unless options[:dry_run]
+          unless dry_run?
             account.header.destroy
             account.avatar.destroy if options[:prune_profiles]
             account.save!
@@ -60,7 +60,7 @@ module Mastodon::CLI
           size
         end
 
-        say("Visited #{processed} accounts and removed profile media totaling #{number_to_human_size(aggregate)}#{dry_run}", :green, true)
+        say("Visited #{processed} accounts and removed profile media totaling #{number_to_human_size(aggregate)}#{dry_run_mode_suffix}", :green, true)
       end
 
       unless options[:prune_profiles] || options[:remove_headers]
@@ -69,7 +69,7 @@ module Mastodon::CLI
 
           size = (media_attachment.file_file_size || 0) + (media_attachment.thumbnail_file_size || 0)
 
-          unless options[:dry_run]
+          unless dry_run?
             media_attachment.file.destroy
             media_attachment.thumbnail.destroy
             media_attachment.save
@@ -78,7 +78,7 @@ module Mastodon::CLI
           size
         end
 
-        say("Removed #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
+        say("Removed #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run_mode_suffix}", :green, true)
       end
     end
 
@@ -97,7 +97,6 @@ module Mastodon::CLI
       progress        = create_progress_bar(nil)
       reclaimed_bytes = 0
       removed         = 0
-      dry_run         = options[:dry_run] ? ' (DRY RUN)' : ''
       prefix          = options[:prefix]
 
       case Paperclip::Attachment.default_options[:storage]
@@ -123,7 +122,7 @@ module Mastodon::CLI
           record_map = preload_records_from_mixed_objects(objects)
 
           objects.each do |object|
-            object.acl.put(acl: s3_permissions) if options[:fix_permissions] && !options[:dry_run]
+            object.acl.put(acl: s3_permissions) if options[:fix_permissions] && !dry_run?
 
             path_segments = object.key.split('/')
             path_segments.delete('cache')
@@ -145,7 +144,7 @@ module Mastodon::CLI
             next unless attachment.blank? || !attachment.variant?(file_name)
 
             begin
-              object.delete unless options[:dry_run]
+              object.delete unless dry_run?
 
               reclaimed_bytes += object.size
               removed += 1
@@ -194,7 +193,7 @@ module Mastodon::CLI
           begin
             size = File.size(path)
 
-            unless options[:dry_run]
+            unless dry_run?
               File.delete(path)
               begin
                 FileUtils.rmdir(File.dirname(path), parents: true)
@@ -216,7 +215,7 @@ module Mastodon::CLI
       progress.total = progress.progress
       progress.finish
 
-      say("Removed #{removed} orphans (approx. #{number_to_human_size(reclaimed_bytes)})#{dry_run}", :green, true)
+      say("Removed #{removed} orphans (approx. #{number_to_human_size(reclaimed_bytes)})#{dry_run_mode_suffix}", :green, true)
     end
 
     option :account, type: :string
@@ -246,8 +245,6 @@ module Mastodon::CLI
       not be re-downloaded. To force re-download of every URL, use --force.
     DESC
     def refresh
-      dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
-
       if options[:status]
         scope = MediaAttachment.where(status_id: options[:status])
       elsif options[:account]
@@ -274,7 +271,7 @@ module Mastodon::CLI
         next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?)
         next if DomainBlock.reject_media?(media_attachment.account.domain)
 
-        unless options[:dry_run]
+        unless dry_run?
           media_attachment.reset_file!
           media_attachment.reset_thumbnail!
           media_attachment.save
@@ -283,7 +280,7 @@ module Mastodon::CLI
         media_attachment.file_file_size + (media_attachment.thumbnail_file_size || 0)
       end
 
-      say("Downloaded #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
+      say("Downloaded #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run_mode_suffix}", :green, true)
     end
 
     desc 'usage', 'Calculate disk space consumed by Mastodon'
diff --git a/lib/mastodon/cli/preview_cards.rb b/lib/mastodon/cli/preview_cards.rb
index c66bcb504a..2df3d095da 100644
--- a/lib/mastodon/cli/preview_cards.rb
+++ b/lib/mastodon/cli/preview_cards.rb
@@ -27,7 +27,6 @@ module Mastodon::CLI
     DESC
     def remove
       time_ago = options[:days].days.ago
-      dry_run  = options[:dry_run] ? ' (DRY RUN)' : ''
       link     = options[:link] ? 'link-type ' : ''
       scope    = PreviewCard.cached
       scope    = scope.where(type: :link) if options[:link]
@@ -38,7 +37,7 @@ module Mastodon::CLI
 
         size = preview_card.image_file_size
 
-        unless options[:dry_run]
+        unless dry_run?
           preview_card.image.destroy
           preview_card.save
         end
@@ -46,7 +45,7 @@ module Mastodon::CLI
         size
       end
 
-      say("Removed #{processed} #{link}preview cards (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
+      say("Removed #{processed} #{link}preview cards (approx. #{number_to_human_size(aggregate)})#{dry_run_mode_suffix}", :green, true)
     end
   end
 end
diff --git a/lib/mastodon/cli/upgrade.rb b/lib/mastodon/cli/upgrade.rb
index e5c86b3d73..88390da5bf 100644
--- a/lib/mastodon/cli/upgrade.rb
+++ b/lib/mastodon/cli/upgrade.rb
@@ -17,7 +17,6 @@ module Mastodon::CLI
     LONG_DESC
     def storage_schema
       progress = create_progress_bar(nil)
-      dry_run  = dry_run? ? ' (DRY RUN)' : ''
       records  = 0
 
       klasses = [
@@ -69,7 +68,7 @@ module Mastodon::CLI
       progress.total = progress.progress
       progress.finish
 
-      say("Upgraded storage schema of #{records} records#{dry_run}", :green, true)
+      say("Upgraded storage schema of #{records} records#{dry_run_mode_suffix}", :green, true)
     end
 
     private
diff --git a/package.json b/package.json
index a5a0d74eee..4e0793c9af 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "@mastodon/mastodon",
   "license": "AGPL-3.0-or-later",
   "engines": {
-    "node": ">=14"
+    "node": ">=16"
   },
   "scripts": {
     "postversion": "git push --tags",
@@ -26,14 +26,14 @@
   },
   "private": true,
   "dependencies": {
-    "@babel/core": "^7.21.8",
-    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
+    "@babel/core": "^7.22.1",
+    "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.3",
     "@babel/plugin-transform-react-inline-elements": "^7.21.0",
-    "@babel/plugin-transform-runtime": "^7.21.4",
-    "@babel/preset-env": "^7.21.5",
-    "@babel/preset-react": "^7.18.6",
+    "@babel/plugin-transform-runtime": "^7.22.4",
+    "@babel/preset-env": "^7.22.4",
+    "@babel/preset-react": "^7.22.3",
     "@babel/preset-typescript": "^7.21.5",
-    "@babel/runtime": "^7.21.5",
+    "@babel/runtime": "^7.22.3",
     "@gamestdio/websocket": "^0.3.2",
     "@github/webauthn-json": "^2.1.1",
     "@rails/ujs": "^6.1.7",
@@ -77,7 +77,7 @@
     "intl-messageformat": "^2.2.0",
     "intl-relativeformat": "^6.4.3",
     "js-yaml": "^4.1.0",
-    "jsdom": "^22.0.0",
+    "jsdom": "^22.1.0",
     "lodash": "^4.17.21",
     "mark-loader": "^0.1.6",
     "marky": "^1.2.5",
@@ -87,7 +87,7 @@
     "path-complete-extname": "^1.0.0",
     "pg": "^8.5.0",
     "pg-connection-string": "^2.6.0",
-    "postcss": "^8.4.23",
+    "postcss": "^8.4.24",
     "postcss-loader": "^4.3.0",
     "prop-types": "^15.8.1",
     "punycode": "^2.3.0",
@@ -135,18 +135,18 @@
     "webpack-cli": "^3.3.12",
     "webpack-merge": "^5.9.0",
     "wicg-inert": "^3.1.2",
-    "workbox-expiration": "^6.5.4",
-    "workbox-precaching": "^6.5.4",
-    "workbox-routing": "^6.5.4",
-    "workbox-strategies": "^6.5.4",
-    "workbox-webpack-plugin": "^6.5.4",
-    "workbox-window": "^6.5.4",
+    "workbox-expiration": "^6.6.0",
+    "workbox-precaching": "^6.6.0",
+    "workbox-routing": "^6.6.0",
+    "workbox-strategies": "^6.6.0",
+    "workbox-webpack-plugin": "^6.6.0",
+    "workbox-window": "^6.6.0",
     "ws": "^8.12.1"
   },
   "devDependencies": {
     "@testing-library/jest-dom": "^5.16.5",
     "@testing-library/react": "^14.0.0",
-    "@types/babel__core": "^7.20.0",
+    "@types/babel__core": "^7.20.1",
     "@types/emoji-mart": "^3.0.9",
     "@types/escape-html": "^1.0.2",
     "@types/express": "^4.17.17",
@@ -154,18 +154,18 @@
     "@types/intl": "^1.2.0",
     "@types/jest": "^29.5.1",
     "@types/js-yaml": "^4.0.5",
-    "@types/lodash": "^4.14.194",
+    "@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.0.26",
+    "@types/react": "^18.2.7",
     "@types/react-dom": "^18.2.4",
     "@types/react-helmet": "^6.1.6",
     "@types/react-immutable-proptypes": "^2.1.0",
     "@types/react-intl": "2.3.18",
-    "@types/react-motion": "^0.0.33",
+    "@types/react-motion": "^0.0.34",
     "@types/react-overlays": "^3.1.0",
     "@types/react-router-dom": "^5.3.3",
     "@types/react-select": "^5.0.1",
@@ -179,15 +179,15 @@
     "@types/uuid": "^9.0.0",
     "@types/webpack": "^4.41.33",
     "@types/yargs": "^17.0.24",
-    "@typescript-eslint/eslint-plugin": "^5.59.7",
-    "@typescript-eslint/parser": "^5.59.7",
+    "@typescript-eslint/eslint-plugin": "^5.59.8",
+    "@typescript-eslint/parser": "^5.59.8",
     "babel-jest": "^29.5.0",
     "eslint": "^8.41.0",
     "eslint-config-prettier": "^8.8.0",
     "eslint-import-resolver-typescript": "^3.5.5",
     "eslint-plugin-formatjs": "^4.10.1",
     "eslint-plugin-import": "~2.27.5",
-    "eslint-plugin-jsdoc": "^44.2.5",
+    "eslint-plugin-jsdoc": "^45.0.0",
     "eslint-plugin-jsx-a11y": "~6.7.1",
     "eslint-plugin-prettier": "^4.2.1",
     "eslint-plugin-promise": "~6.1.1",
diff --git a/spec/controllers/admin/ip_blocks_controller_spec.rb b/spec/controllers/admin/ip_blocks_controller_spec.rb
index 873888afc7..05190f1340 100644
--- a/spec/controllers/admin/ip_blocks_controller_spec.rb
+++ b/spec/controllers/admin/ip_blocks_controller_spec.rb
@@ -18,4 +18,37 @@ describe Admin::IpBlocksController do
       expect(response).to have_http_status(:success)
     end
   end
+
+  describe 'GET #new' do
+    it 'returns http success and renders view' do
+      get :new
+
+      expect(response).to have_http_status(:success)
+      expect(response).to render_template(:new)
+    end
+  end
+
+  describe 'POST #create' do
+    context 'with valid data' do
+      it 'creates a new ip block and redirects' do
+        expect do
+          post :create, params: { ip_block: { ip: '1.1.1.1', severity: 'no_access', expires_in: 1.day.to_i.to_s } }
+        end.to change(IpBlock, :count).by(1)
+
+        expect(response).to redirect_to(admin_ip_blocks_path)
+        expect(flash.notice).to match(I18n.t('admin.ip_blocks.created_msg'))
+      end
+    end
+
+    context 'with invalid data' do
+      it 'does not create new a ip block and renders new' do
+        expect do
+          post :create, params: { ip_block: { ip: '1.1.1.1' } }
+        end.to_not change(IpBlock, :count)
+
+        expect(response).to have_http_status(:success)
+        expect(response).to render_template(:new)
+      end
+    end
+  end
 end
diff --git a/spec/controllers/admin/relays_controller_spec.rb b/spec/controllers/admin/relays_controller_spec.rb
index dfb9f3c048..261f302c05 100644
--- a/spec/controllers/admin/relays_controller_spec.rb
+++ b/spec/controllers/admin/relays_controller_spec.rb
@@ -18,4 +18,42 @@ describe Admin::RelaysController do
       expect(response).to have_http_status(:success)
     end
   end
+
+  describe 'GET #new' do
+    it 'returns http success and renders view' do
+      get :new
+
+      expect(response).to have_http_status(:success)
+      expect(response).to render_template(:new)
+    end
+  end
+
+  describe 'POST #create' do
+    context 'with valid data' do
+      let(:inbox_url) { 'https://example.com/inbox' }
+
+      before do
+        stub_request(:post, inbox_url).to_return(status: 200)
+      end
+
+      it 'creates a new relay and redirects' do
+        expect do
+          post :create, params: { relay: { inbox_url: inbox_url } }
+        end.to change(Relay, :count).by(1)
+
+        expect(response).to redirect_to(admin_relays_path)
+      end
+    end
+
+    context 'with invalid data' do
+      it 'does not create new a relay and renders new' do
+        expect do
+          post :create, params: { relay: { inbox_url: 'invalid' } }
+        end.to_not change(Relay, :count)
+
+        expect(response).to have_http_status(:success)
+        expect(response).to render_template(:new)
+      end
+    end
+  end
 end
diff --git a/spec/controllers/admin/rules_controller_spec.rb b/spec/controllers/admin/rules_controller_spec.rb
index d7b633c049..92ffb41567 100644
--- a/spec/controllers/admin/rules_controller_spec.rb
+++ b/spec/controllers/admin/rules_controller_spec.rb
@@ -18,4 +18,68 @@ describe Admin::RulesController do
       expect(response).to have_http_status(:success)
     end
   end
+
+  describe 'GET #edit' do
+    let(:rule) { Fabricate(:rule) }
+
+    it 'returns http success and renders edit' do
+      get :edit, params: { id: rule.id }
+
+      expect(response).to have_http_status(:success)
+      expect(response).to render_template(:edit)
+    end
+  end
+
+  describe 'POST #create' do
+    context 'with valid data' do
+      it 'creates a new rule and redirects' do
+        expect do
+          post :create, params: { rule: { text: 'The rule text.' } }
+        end.to change(Rule, :count).by(1)
+
+        expect(response).to redirect_to(admin_rules_path)
+      end
+    end
+
+    context 'with invalid data' do
+      it 'does creates a new rule and renders index' do
+        expect do
+          post :create, params: { rule: { text: '' } }
+        end.to_not change(Rule, :count)
+
+        expect(response).to render_template(:index)
+      end
+    end
+  end
+
+  describe 'PUT #update' do
+    let(:rule) { Fabricate(:rule, text: 'Original text') }
+
+    context 'with valid data' do
+      it 'updates the rule and redirects' do
+        put :update, params: { id: rule.id, rule: { text: 'Updated text.' } }
+
+        expect(response).to redirect_to(admin_rules_path)
+      end
+    end
+
+    context 'with invalid data' do
+      it 'does not update the rule and renders index' do
+        put :update, params: { id: rule.id, rule: { text: '' } }
+
+        expect(response).to render_template(:edit)
+      end
+    end
+  end
+
+  describe 'DELETE #destroy' do
+    let!(:rule) { Fabricate(:rule) }
+
+    it 'destroys the rule and redirects' do
+      delete :destroy, params: { id: rule.id }
+
+      expect(rule.reload).to be_discarded
+      expect(response).to redirect_to(admin_rules_path)
+    end
+  end
 end
diff --git a/spec/controllers/admin/webhooks_controller_spec.rb b/spec/controllers/admin/webhooks_controller_spec.rb
index 12727e142b..5e45c74082 100644
--- a/spec/controllers/admin/webhooks_controller_spec.rb
+++ b/spec/controllers/admin/webhooks_controller_spec.rb
@@ -18,4 +18,82 @@ describe Admin::WebhooksController do
       expect(response).to have_http_status(:success)
     end
   end
+
+  describe 'GET #new' do
+    it 'returns http success and renders view' do
+      get :new
+
+      expect(response).to have_http_status(:success)
+      expect(response).to render_template(:new)
+    end
+  end
+
+  describe 'POST #create' do
+    it 'creates a new webhook record with valid data' do
+      expect do
+        post :create, params: { webhook: { url: 'https://example.com/hook', events: ['account.approved'] } }
+      end.to change(Webhook, :count).by(1)
+
+      expect(response).to be_redirect
+    end
+
+    it 'does not create a new webhook record with invalid data' do
+      expect do
+        post :create, params: { webhook: { url: 'https://example.com/hook', events: [] } }
+      end.to_not change(Webhook, :count)
+
+      expect(response).to have_http_status(:success)
+      expect(response).to render_template(:new)
+    end
+  end
+
+  context 'with an existing record' do
+    let!(:webhook) { Fabricate :webhook }
+
+    describe 'GET #show' do
+      it 'returns http success and renders view' do
+        get :show, params: { id: webhook.id }
+
+        expect(response).to have_http_status(:success)
+        expect(response).to render_template(:show)
+      end
+    end
+
+    describe 'GET #edit' do
+      it 'returns http success and renders view' do
+        get :edit, params: { id: webhook.id }
+
+        expect(response).to have_http_status(:success)
+        expect(response).to render_template(:edit)
+      end
+    end
+
+    describe 'PUT #update' do
+      it 'updates the record with valid data' do
+        put :update, params: { id: webhook.id, webhook: { url: 'https://example.com/new/location' } }
+
+        expect(webhook.reload.url).to match(%r{new/location})
+        expect(response).to redirect_to(admin_webhook_path(webhook))
+      end
+
+      it 'does not update the record with invalid data' do
+        expect do
+          put :update, params: { id: webhook.id, webhook: { url: '' } }
+        end.to_not change(webhook, :url)
+
+        expect(response).to have_http_status(:success)
+        expect(response).to render_template(:show)
+      end
+    end
+
+    describe 'DELETE #destroy' do
+      it 'destroys the record' do
+        expect do
+          delete :destroy, params: { id: webhook.id }
+        end.to change(Webhook, :count).by(-1)
+
+        expect(response).to redirect_to(admin_webhooks_path)
+      end
+    end
+  end
 end
diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb
index 25f1311d40..8eee6f5381 100644
--- a/spec/lib/mastodon/cli/accounts_spec.rb
+++ b/spec/lib/mastodon/cli/accounts_spec.rb
@@ -4,9 +4,662 @@ require 'rails_helper'
 require 'mastodon/cli/accounts'
 
 describe Mastodon::CLI::Accounts do
+  let(:cli) { described_class.new }
+
   describe '.exit_on_failure?' do
     it 'returns true' do
       expect(described_class.exit_on_failure?).to be true
     end
   end
+
+  describe '#create' do
+    shared_examples 'a new user with given email address and username' do
+      it 'creates a new user with the specified email address' do
+        cli.invoke(:create, arguments, options)
+
+        expect(User.find_by(email: options[:email])).to be_present
+      end
+
+      it 'creates a new local account with the specified username' do
+        cli.invoke(:create, arguments, options)
+
+        expect(Account.find_local('tootctl_username')).to be_present
+      end
+
+      it 'returns "OK" and newly generated password' do
+        allow(SecureRandom).to receive(:hex).and_return('test_password')
+
+        expect { cli.invoke(:create, arguments, options) }.to output(
+          a_string_including("OK\nNew password: test_password")
+        ).to_stdout
+      end
+    end
+
+    context 'when required USERNAME and --email are provided' do
+      let(:arguments) { ['tootctl_username'] }
+
+      context 'with USERNAME and --email only' do
+        let(:options) { { email: 'tootctl@example.com' } }
+
+        it_behaves_like 'a new user with given email address and username'
+
+        context 'with invalid --email value' do
+          let(:options) { { email: 'invalid' } }
+
+          it 'exits with an error message' do
+            expect { cli.invoke(:create, arguments, options) }.to output(
+              a_string_including('Failure/Error: email')
+            ).to_stdout
+              .and raise_error(SystemExit)
+          end
+        end
+      end
+
+      context 'with --confirmed option' do
+        let(:options) { { email: 'tootctl@example.com', confirmed: true } }
+
+        it_behaves_like 'a new user with given email address and username'
+
+        it 'creates a new user with confirmed status' do
+          cli.invoke(:create, arguments, options)
+
+          user = User.find_by(email: options[:email])
+
+          expect(user.confirmed?).to be(true)
+        end
+      end
+
+      context 'with --approve option' do
+        let(:options) { { email: 'tootctl@example.com', approve: true } }
+
+        before do
+          Form::AdminSettings.new(registrations_mode: 'approved').save
+        end
+
+        it_behaves_like 'a new user with given email address and username'
+
+        it 'creates a new user with approved status' do
+          cli.invoke(:create, arguments, options)
+
+          user = User.find_by(email: options[:email])
+
+          expect(user.approved?).to be(true)
+        end
+      end
+
+      context 'with --role option' do
+        context 'when role exists' do
+          let(:default_role) { Fabricate(:user_role) }
+          let(:options) { { email: 'tootctl@example.com', role: default_role.name } }
+
+          it_behaves_like 'a new user with given email address and username'
+
+          it 'creates a new user and assigns the specified role' do
+            cli.invoke(:create, arguments, options)
+
+            role = User.find_by(email: options[:email])&.role
+
+            expect(role.name).to eq(default_role.name)
+          end
+        end
+
+        context 'when role does not exist' do
+          let(:options) { { email: 'tootctl@example.com', role: '404' } }
+
+          it 'exits with an error message indicating the role name was not found' do
+            expect { cli.invoke(:create, arguments, options) }.to output(
+              a_string_including('Cannot find user role with that name')
+            ).to_stdout
+              .and raise_error(SystemExit)
+          end
+        end
+      end
+
+      context 'with --reattach option' do
+        context "when account's user is present" do
+          let(:options) { { email: 'tootctl_new@example.com', reattach: true } }
+          let(:user) { Fabricate.build(:user, email: 'tootctl@example.com') }
+
+          before do
+            Fabricate(:account, username: 'tootctl_username', user: user)
+          end
+
+          it 'returns an error message indicating the username is already taken' do
+            expect { cli.invoke(:create, arguments, options) }.to output(
+              a_string_including("The chosen username is currently in use\nUse --force to reattach it anyway and delete the other user")
+            ).to_stdout
+          end
+
+          context 'with --force option' do
+            let(:options) { { email: 'tootctl_new@example.com', reattach: true, force: true } }
+
+            it 'reattaches the account to the new user and deletes the previous user' do
+              cli.invoke(:create, arguments, options)
+
+              user = Account.find_local('tootctl_username')&.user
+
+              expect(user.email).to eq(options[:email])
+            end
+          end
+        end
+
+        context "when account's user is not present" do
+          let(:options) { { email: 'tootctl@example.com', reattach: true } }
+
+          before do
+            Fabricate(:account, username: 'tootctl_username', user: nil)
+          end
+
+          it_behaves_like 'a new user with given email address and username'
+        end
+      end
+    end
+
+    context 'when required --email option is not provided' do
+      let(:arguments) { ['tootctl_username'] }
+
+      it 'raises a required argument missing error (Thor::RequiredArgumentMissingError)' do
+        expect { cli.invoke(:create, arguments) }
+          .to raise_error(Thor::RequiredArgumentMissingError)
+      end
+    end
+  end
+
+  describe '#modify' do
+    context 'when the given username is not found' do
+      let(:arguments) { ['non_existent_username'] }
+
+      it 'exits with an error message indicating the user was not found' do
+        expect { cli.invoke(:modify, arguments) }.to output(
+          a_string_including('No user with such username')
+        ).to_stdout
+          .and raise_error(SystemExit)
+      end
+    end
+
+    context 'when the given username is found' do
+      let(:user) { Fabricate(:user) }
+      let(:arguments) { [user.account.username] }
+
+      context 'when no option is provided' do
+        it 'returns a successful message' do
+          expect { cli.invoke(:modify, arguments) }.to output(
+            a_string_including('OK')
+          ).to_stdout
+        end
+
+        it 'does not modify the user' do
+          cli.invoke(:modify, arguments)
+
+          expect(user).to eq(user.reload)
+        end
+      end
+
+      context 'with --role option' do
+        context 'when the given role is not found' do
+          let(:options) { { role: '404' } }
+
+          it 'exits with an error message indicating the role was not found' do
+            expect { cli.invoke(:modify, arguments, options) }.to output(
+              a_string_including('Cannot find user role with that name')
+            ).to_stdout
+              .and raise_error(SystemExit)
+          end
+        end
+
+        context 'when the given role is found' do
+          let(:default_role) { Fabricate(:user_role) }
+          let(:options) { { role: default_role.name } }
+
+          it "updates the user's role to the specified role" do
+            cli.invoke(:modify, arguments, options)
+
+            role = user.reload.role
+
+            expect(role.name).to eq(default_role.name)
+          end
+        end
+      end
+
+      context 'with --remove-role option' do
+        let(:options) { { remove_role: true } }
+        let(:role) { Fabricate(:user_role) }
+        let(:user) { Fabricate(:user, role: role) }
+
+        it "removes the user's role successfully" do
+          cli.invoke(:modify, arguments, options)
+
+          role = user.reload.role
+
+          expect(role.name).to be_empty
+        end
+      end
+
+      context 'with --email option' do
+        let(:user) { Fabricate(:user, email: 'old_email@email.com') }
+        let(:options) { { email: 'new_email@email.com' } }
+
+        it "sets the user's unconfirmed email to the provided email address" do
+          cli.invoke(:modify, arguments, options)
+
+          expect(user.reload.unconfirmed_email).to eq(options[:email])
+        end
+
+        it "does not update the user's original email address" do
+          cli.invoke(:modify, arguments, options)
+
+          expect(user.reload.email).to eq('old_email@email.com')
+        end
+
+        context 'with --confirm option' do
+          let(:user) { Fabricate(:user, email: 'old_email@email.com', confirmed_at: nil) }
+          let(:options) { { email: 'new_email@email.com', confirm: true } }
+
+          it "updates the user's email address to the provided email" do
+            cli.invoke(:modify, arguments, options)
+
+            expect(user.reload.email).to eq(options[:email])
+          end
+
+          it "sets the user's email address as confirmed" do
+            cli.invoke(:modify, arguments, options)
+
+            expect(user.reload.confirmed?).to be(true)
+          end
+        end
+      end
+
+      context 'with --confirm option' do
+        let(:user) { Fabricate(:user, confirmed_at: nil) }
+        let(:options) { { confirm: true } }
+
+        it "confirms the user's email address" do
+          cli.invoke(:modify, arguments, options)
+
+          expect(user.reload.confirmed?).to be(true)
+        end
+      end
+
+      context 'with --approve option' do
+        let(:user) { Fabricate(:user, approved: false) }
+        let(:options) { { approve: true } }
+
+        before do
+          Form::AdminSettings.new(registrations_mode: 'approved').save
+        end
+
+        it 'approves the user' do
+          expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.approved }.from(false).to(true)
+        end
+      end
+
+      context 'with --disable option' do
+        let(:user) { Fabricate(:user, disabled: false) }
+        let(:options) { { disable: true } }
+
+        it 'disables the user' do
+          expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.disabled }.from(false).to(true)
+        end
+      end
+
+      context 'with --enable option' do
+        let(:user) { Fabricate(:user, disabled: true) }
+        let(:options) { { enable: true } }
+
+        it 'enables the user' do
+          expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.disabled }.from(true).to(false)
+        end
+      end
+
+      context 'with --reset-password option' do
+        let(:options) { { reset_password: true } }
+
+        it 'returns a new password for the user' do
+          allow(SecureRandom).to receive(:hex).and_return('new_password')
+
+          expect { cli.invoke(:modify, arguments, options) }.to output(
+            a_string_including('new_password')
+          ).to_stdout
+        end
+      end
+
+      context 'with --disable-2fa option' do
+        let(:user) { Fabricate(:user, otp_required_for_login: true) }
+        let(:options) { { disable_2fa: true } }
+
+        it 'disables the two-factor authentication for the user' do
+          expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.otp_required_for_login }.from(true).to(false)
+        end
+      end
+
+      context 'when provided data is invalid' do
+        let(:user) { Fabricate(:user) }
+        let(:options) { { email: 'invalid' } }
+
+        it 'exits with an error message' do
+          expect { cli.invoke(:modify, arguments, options) }.to output(
+            a_string_including('Failure/Error: email')
+          ).to_stdout
+            .and raise_error(SystemExit)
+        end
+      end
+    end
+  end
+
+  describe '#delete' do
+    let(:account) { Fabricate(:account) }
+    let(:arguments) { [account.username] }
+    let(:options) { { email: account.user.email } }
+    let(:delete_account_service) { instance_double(DeleteAccountService) }
+
+    before do
+      allow(DeleteAccountService).to receive(:new).and_return(delete_account_service)
+      allow(delete_account_service).to receive(:call)
+    end
+
+    context 'when both username and --email are provided' do
+      it 'exits with an error message indicating that only one should be used' do
+        expect { cli.invoke(:delete, arguments, options) }.to output(
+          a_string_including('Use username or --email, not both')
+        ).to_stdout
+          .and raise_error(SystemExit)
+      end
+    end
+
+    context 'when neither username nor --email are provided' do
+      it 'exits with an error message indicating that no username was provided' do
+        expect { cli.invoke(:delete) }.to output(
+          a_string_including('No username provided')
+        ).to_stdout
+          .and raise_error(SystemExit)
+      end
+    end
+
+    context 'when username is provided' do
+      it 'deletes the specified user successfully' do
+        cli.invoke(:delete, arguments)
+
+        expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
+      end
+
+      context 'with --dry-run option' do
+        let(:options) { { dry_run: true } }
+
+        it 'does not delete the specified user' do
+          cli.invoke(:delete, arguments, options)
+
+          expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false)
+        end
+
+        it 'outputs a successful message in dry run mode' do
+          expect { cli.invoke(:delete, arguments, options) }.to output(
+            a_string_including('OK (DRY RUN)')
+          ).to_stdout
+        end
+      end
+
+      context 'when the given username is not found' do
+        let(:arguments) { ['non_existent_username'] }
+
+        it 'exits with an error message indicating that no user was found' do
+          expect { cli.invoke(:delete, arguments) }.to output(
+            a_string_including('No user with such username')
+          ).to_stdout
+            .and raise_error(SystemExit)
+        end
+      end
+    end
+
+    context 'when --email is provided' do
+      it 'deletes the specified user successfully' do
+        cli.invoke(:delete, nil, options)
+
+        expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
+      end
+
+      context 'with --dry-run option' do
+        let(:options) { { email: account.user.email, dry_run: true } }
+
+        it 'does not delete the user' do
+          cli.invoke(:delete, nil, options)
+
+          expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false)
+        end
+
+        it 'outputs a successful message in dry run mode' do
+          expect { cli.invoke(:delete, nil, options) }.to output(
+            a_string_including('OK (DRY RUN)')
+          ).to_stdout
+        end
+      end
+
+      context 'when the given email address is not found' do
+        let(:options) { { email: '404@example.com' } }
+
+        it 'exits with an error message indicating that no user was found' do
+          expect { cli.invoke(:delete, nil, options) }.to output(
+            a_string_including('No user with such email')
+          ).to_stdout
+            .and raise_error(SystemExit)
+        end
+      end
+    end
+  end
+
+  describe '#approve' do
+    let(:total_users) { 10 }
+
+    before do
+      Form::AdminSettings.new(registrations_mode: 'approved').save
+      Fabricate.times(total_users, :user)
+    end
+
+    context 'with --all option' do
+      it 'approves all pending registrations' do
+        cli.invoke(:approve, nil, all: true)
+
+        expect(User.pluck(:approved).all?(true)).to be(true)
+      end
+    end
+
+    context 'with --number option' do
+      context 'when the number is positive' do
+        let(:options) { { number: 3 } }
+
+        it 'approves the earliest n pending registrations' do
+          cli.invoke(:approve, nil, options)
+
+          n_earliest_pending_registrations = User.order(created_at: :asc).first(options[:number])
+
+          expect(n_earliest_pending_registrations.all?(&:approved?)).to be(true)
+        end
+
+        it 'does not approve the remaining pending registrations' do
+          cli.invoke(:approve, nil, options)
+
+          pending_registrations = User.order(created_at: :asc).last(total_users - options[:number])
+
+          expect(pending_registrations.all?(&:approved?)).to be(false)
+        end
+      end
+
+      context 'when the number is negative' do
+        it 'exits with an error message indicating that the number must be positive' do
+          expect { cli.invoke(:approve, nil, number: -1) }.to output(
+            a_string_including('Number must be positive')
+          ).to_stdout
+            .and raise_error(SystemExit)
+        end
+      end
+
+      context 'when the given number is greater than the number of users' do
+        let(:options) { { number: total_users * 2 } }
+
+        it 'approves all users' do
+          cli.invoke(:approve, nil, options)
+
+          expect(User.pluck(:approved).all?(true)).to be(true)
+        end
+
+        it 'does not raise any error' do
+          expect { cli.invoke(:approve, nil, options) }
+            .to_not raise_error
+        end
+      end
+    end
+
+    context 'with username argument' do
+      context 'when the given username is found' do
+        let(:user) { User.last }
+        let(:arguments) { [user.account.username] }
+
+        it 'approves the specified user successfully' do
+          cli.invoke(:approve, arguments)
+
+          expect(user.reload.approved?).to be(true)
+        end
+      end
+
+      context 'when the given username is not found' do
+        let(:arguments) { ['non_existent_username'] }
+
+        it 'exits with an error message indicating that no such account was found' do
+          expect { cli.invoke(:approve, arguments) }.to output(
+            a_string_including('No such account')
+          ).to_stdout
+            .and raise_error(SystemExit)
+        end
+      end
+    end
+  end
+
+  describe '#follow' do
+    context 'when the given username is not found' do
+      let(:arguments) { ['non_existent_username'] }
+
+      it 'exits with an error message indicating that no account with the given username was found' do
+        expect { cli.invoke(:follow, arguments) }.to output(
+          a_string_including('No such account')
+        ).to_stdout
+          .and raise_error(SystemExit)
+      end
+    end
+
+    context 'when the given username is found' do
+      let!(:target_account)   { Fabricate(:account) }
+      let!(:follower_bob)     { Fabricate(:account, username: 'bob') }
+      let!(:follower_rony)    { Fabricate(:account, username: 'rony') }
+      let!(:follower_charles) { Fabricate(:account, username: 'charles') }
+      let(:follow_service)    { instance_double(FollowService, call: nil) }
+      let(:scope)             { Account.local.without_suspended }
+
+      before do
+        allow(cli).to receive(:parallelize_with_progress).and_yield(follower_bob)
+                                                         .and_yield(follower_rony)
+                                                         .and_yield(follower_charles)
+                                                         .and_return([3, nil])
+        allow(FollowService).to receive(:new).and_return(follow_service)
+      end
+
+      it 'makes all local accounts follow the target account' do
+        cli.follow(target_account.username)
+
+        expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+        expect(follow_service).to have_received(:call).with(follower_bob, target_account, any_args).once
+        expect(follow_service).to have_received(:call).with(follower_rony, target_account, any_args).once
+        expect(follow_service).to have_received(:call).with(follower_charles, target_account, any_args).once
+      end
+
+      it 'displays a successful message' do
+        expect { cli.follow(target_account.username) }.to output(
+          a_string_including('OK, followed target from 3 accounts')
+        ).to_stdout
+      end
+    end
+  end
+
+  describe '#unfollow' do
+    context 'when the given username is not found' do
+      let(:arguments) { ['non_existent_username'] }
+
+      it 'exits with an error message indicating that no account with the given username was found' do
+        expect { cli.invoke(:unfollow, arguments) }.to output(
+          a_string_including('No such account')
+        ).to_stdout
+          .and raise_error(SystemExit)
+      end
+    end
+
+    context 'when the given username is found' do
+      let!(:target_account)  { Fabricate(:account) }
+      let!(:follower_chris)  { Fabricate(:account, username: 'chris') }
+      let!(:follower_rambo)  { Fabricate(:account, username: 'rambo') }
+      let!(:follower_ana)    { Fabricate(:account, username: 'ana') }
+      let(:unfollow_service) { instance_double(UnfollowService, call: nil) }
+      let(:scope)            { target_account.followers.local }
+
+      before do
+        accounts = [follower_chris, follower_rambo, follower_ana]
+        accounts.each { |account| target_account.follow!(account) }
+        allow(cli).to receive(:parallelize_with_progress).and_yield(follower_chris)
+                                                         .and_yield(follower_rambo)
+                                                         .and_yield(follower_ana)
+                                                         .and_return([3, nil])
+        allow(UnfollowService).to receive(:new).and_return(unfollow_service)
+      end
+
+      it 'makes all local accounts unfollow the target account' do
+        cli.unfollow(target_account.username)
+
+        expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+        expect(unfollow_service).to have_received(:call).with(follower_chris, target_account).once
+        expect(unfollow_service).to have_received(:call).with(follower_rambo, target_account).once
+        expect(unfollow_service).to have_received(:call).with(follower_ana, target_account).once
+      end
+
+      it 'displays a successful message' do
+        expect { cli.unfollow(target_account.username) }.to output(
+          a_string_including('OK, unfollowed target from 3 accounts')
+        ).to_stdout
+      end
+    end
+  end
+
+  describe '#backup' do
+    context 'when the given username is not found' do
+      let(:arguments) { ['non_existent_username'] }
+
+      it 'exits with an error message indicating that there is no such account' do
+        expect { cli.invoke(:backup, arguments) }.to output(
+          a_string_including('No user with such username')
+        ).to_stdout
+          .and raise_error(SystemExit)
+      end
+    end
+
+    context 'when the given username is found' do
+      let(:account) { Fabricate(:account) }
+      let(:user) { account.user }
+      let(:arguments) { [account.username] }
+
+      it 'creates a new backup for the specified user' do
+        expect { cli.invoke(:backup, arguments) }.to change { user.backups.count }.by(1)
+      end
+
+      it 'creates a backup job' do
+        allow(BackupWorker).to receive(:perform_async)
+
+        cli.invoke(:backup, arguments)
+        latest_backup = user.backups.last
+
+        expect(BackupWorker).to have_received(:perform_async).with(latest_backup.id).once
+      end
+
+      it 'displays a successful message' do
+        expect { cli.invoke(:backup, arguments) }.to output(
+          a_string_including('OK')
+        ).to_stdout
+      end
+    end
+  end
 end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 22078a6cbc..d7e2b5c185 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -62,6 +62,10 @@ RSpec.configure do |config|
   config.infer_spec_type_from_file_location!
   config.filter_rails_from_backtrace!
 
+  config.define_derived_metadata(file_path: Regexp.new('spec/lib/mastodon/cli')) do |metadata|
+    metadata[:type] = :cli
+  end
+
   config.include Devise::Test::ControllerHelpers, type: :controller
   config.include Devise::Test::ControllerHelpers, type: :helper
   config.include Devise::Test::ControllerHelpers, type: :view
@@ -73,6 +77,10 @@ RSpec.configure do |config|
   config.include Redisable
   config.include SignedRequestHelpers, type: :request
 
+  config.before :each, type: :cli do
+    stub_stdout
+  end
+
   config.before :each, type: :feature do
     https = ENV['LOCAL_HTTPS'] == 'true'
     Capybara.app_host = "http#{https ? 's' : ''}://#{ENV.fetch('LOCAL_DOMAIN')}"
@@ -106,6 +114,10 @@ def attachment_fixture(name)
   Rails.root.join('spec', 'fixtures', 'files', name).open
 end
 
+def stub_stdout
+  allow($stdout).to receive(:write)
+end
+
 def stub_jsonld_contexts!
   stub_request(:get, 'https://www.w3.org/ns/activitystreams').to_return(request_fixture('json-ld.activitystreams.txt'))
   stub_request(:get, 'https://w3id.org/identity/v1').to_return(request_fixture('json-ld.identity.txt'))
diff --git a/yarn.lock b/yarn.lock
index 0bc5cffb35..de407b0d55 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24,45 +24,45 @@
     jsonpointer "^5.0.0"
     leven "^3.1.0"
 
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4":
   version "7.21.4"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39"
   integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==
   dependencies:
     "@babel/highlight" "^7.18.6"
 
-"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5":
-  version "7.21.7"
-  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc"
-  integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==
+"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.22.0", "@babel/compat-data@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e"
+  integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==
 
-"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.21.8", "@babel/core@^7.7.2":
-  version "7.21.8"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4"
-  integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==
+"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.22.1", "@babel/core@^7.7.2":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.1.tgz#5de51c5206f4c6f5533562838337a603c1033cfd"
+  integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==
   dependencies:
     "@ampproject/remapping" "^2.2.0"
     "@babel/code-frame" "^7.21.4"
-    "@babel/generator" "^7.21.5"
-    "@babel/helper-compilation-targets" "^7.21.5"
-    "@babel/helper-module-transforms" "^7.21.5"
-    "@babel/helpers" "^7.21.5"
-    "@babel/parser" "^7.21.8"
-    "@babel/template" "^7.20.7"
-    "@babel/traverse" "^7.21.5"
-    "@babel/types" "^7.21.5"
+    "@babel/generator" "^7.22.0"
+    "@babel/helper-compilation-targets" "^7.22.1"
+    "@babel/helper-module-transforms" "^7.22.1"
+    "@babel/helpers" "^7.22.0"
+    "@babel/parser" "^7.22.0"
+    "@babel/template" "^7.21.9"
+    "@babel/traverse" "^7.22.1"
+    "@babel/types" "^7.22.0"
     convert-source-map "^1.7.0"
     debug "^4.1.0"
     gensync "^1.0.0-beta.2"
     json5 "^2.2.2"
     semver "^6.3.0"
 
-"@babel/generator@^7.21.5", "@babel/generator@^7.7.2":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f"
-  integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==
+"@babel/generator@^7.22.0", "@babel/generator@^7.22.3", "@babel/generator@^7.7.2":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e"
+  integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==
   dependencies:
-    "@babel/types" "^7.21.5"
+    "@babel/types" "^7.22.3"
     "@jridgewell/gen-mapping" "^0.3.2"
     "@jridgewell/trace-mapping" "^0.3.17"
     jsesc "^2.5.1"
@@ -90,31 +90,17 @@
     "@babel/helper-annotate-as-pure" "^7.18.6"
     "@babel/types" "^7.19.0"
 
-"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366"
-  integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==
+"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.1":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58"
+  integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==
   dependencies:
-    "@babel/compat-data" "^7.21.5"
+    "@babel/compat-data" "^7.22.0"
     "@babel/helper-validator-option" "^7.21.0"
     browserslist "^4.21.3"
     lru-cache "^5.1.1"
     semver "^6.3.0"
 
-"@babel/helper-create-class-features-plugin@^7.18.6":
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9"
-  integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==
-  dependencies:
-    "@babel/helper-annotate-as-pure" "^7.18.6"
-    "@babel/helper-environment-visitor" "^7.18.9"
-    "@babel/helper-function-name" "^7.21.0"
-    "@babel/helper-member-expression-to-functions" "^7.21.0"
-    "@babel/helper-optimise-call-expression" "^7.18.6"
-    "@babel/helper-replace-supers" "^7.20.7"
-    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
-    "@babel/helper-split-export-declaration" "^7.18.6"
-
 "@babel/helper-create-class-features-plugin@^7.21.0":
   version "7.21.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18"
@@ -129,6 +115,21 @@
     "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
     "@babel/helper-split-export-declaration" "^7.18.6"
 
+"@babel/helper-create-class-features-plugin@^7.22.1":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz#ae3de70586cc757082ae3eba57240d42f468c41b"
+  integrity sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.18.6"
+    "@babel/helper-environment-visitor" "^7.22.1"
+    "@babel/helper-function-name" "^7.21.0"
+    "@babel/helper-member-expression-to-functions" "^7.22.0"
+    "@babel/helper-optimise-call-expression" "^7.18.6"
+    "@babel/helper-replace-supers" "^7.22.1"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+    semver "^6.3.0"
+
 "@babel/helper-create-regexp-features-plugin@^7.18.6":
   version "7.19.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b"
@@ -137,18 +138,19 @@
     "@babel/helper-annotate-as-pure" "^7.18.6"
     regexpu-core "^5.1.0"
 
-"@babel/helper-create-regexp-features-plugin@^7.20.5":
-  version "7.21.4"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.4.tgz#40411a8ab134258ad2cf3a3d987ec6aa0723cee5"
-  integrity sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==
+"@babel/helper-create-regexp-features-plugin@^7.22.1":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz#a7ed9a8488b45b467fca353cd1a44dc5f0cf5c70"
+  integrity sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.18.6"
     regexpu-core "^5.3.1"
+    semver "^6.3.0"
 
-"@babel/helper-define-polyfill-provider@^0.3.3":
-  version "0.3.3"
-  resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a"
-  integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==
+"@babel/helper-define-polyfill-provider@^0.4.0":
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz#487053f103110f25b9755c5980e031e93ced24d8"
+  integrity sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg==
   dependencies:
     "@babel/helper-compilation-targets" "^7.17.7"
     "@babel/helper-plugin-utils" "^7.16.7"
@@ -162,10 +164,10 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
   integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
 
-"@babel/helper-environment-visitor@^7.21.5":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba"
-  integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==
+"@babel/helper-environment-visitor@^7.22.1":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8"
+  integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==
 
 "@babel/helper-explode-assignable-expression@^7.18.6":
   version "7.18.6"
@@ -211,6 +213,13 @@
   dependencies:
     "@babel/types" "^7.21.0"
 
+"@babel/helper-member-expression-to-functions@^7.22.0":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz#4b77a12c1b4b8e9e28736ed47d8b91f00976911f"
+  integrity sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==
+  dependencies:
+    "@babel/types" "^7.22.3"
+
 "@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4":
   version "7.21.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af"
@@ -218,19 +227,19 @@
   dependencies:
     "@babel/types" "^7.21.4"
 
-"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420"
-  integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==
+"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.1":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63"
+  integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==
   dependencies:
-    "@babel/helper-environment-visitor" "^7.21.5"
+    "@babel/helper-environment-visitor" "^7.22.1"
     "@babel/helper-module-imports" "^7.21.4"
     "@babel/helper-simple-access" "^7.21.5"
     "@babel/helper-split-export-declaration" "^7.18.6"
     "@babel/helper-validator-identifier" "^7.19.1"
-    "@babel/template" "^7.20.7"
-    "@babel/traverse" "^7.21.5"
-    "@babel/types" "^7.21.5"
+    "@babel/template" "^7.21.9"
+    "@babel/traverse" "^7.22.1"
+    "@babel/types" "^7.22.0"
 
 "@babel/helper-optimise-call-expression@^7.18.6":
   version "7.18.6"
@@ -266,6 +275,18 @@
     "@babel/traverse" "^7.20.7"
     "@babel/types" "^7.20.7"
 
+"@babel/helper-replace-supers@^7.22.1":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz#38cf6e56f7dc614af63a21b45565dd623f0fdc95"
+  integrity sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ==
+  dependencies:
+    "@babel/helper-environment-visitor" "^7.22.1"
+    "@babel/helper-member-expression-to-functions" "^7.22.0"
+    "@babel/helper-optimise-call-expression" "^7.18.6"
+    "@babel/template" "^7.21.9"
+    "@babel/traverse" "^7.22.1"
+    "@babel/types" "^7.22.0"
+
 "@babel/helper-simple-access@^7.21.5":
   version "7.21.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee"
@@ -297,7 +318,7 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
   integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
 
-"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0":
+"@babel/helper-validator-option@^7.21.0":
   version "7.21.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
   integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==
@@ -312,14 +333,14 @@
     "@babel/traverse" "^7.18.10"
     "@babel/types" "^7.18.10"
 
-"@babel/helpers@^7.21.5":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08"
-  integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==
+"@babel/helpers@^7.22.0":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.3.tgz#53b74351da9684ea2f694bf0877998da26dd830e"
+  integrity sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==
   dependencies:
-    "@babel/template" "^7.20.7"
-    "@babel/traverse" "^7.21.5"
-    "@babel/types" "^7.21.5"
+    "@babel/template" "^7.21.9"
+    "@babel/traverse" "^7.22.1"
+    "@babel/types" "^7.22.3"
 
 "@babel/highlight@^7.18.6":
   version "7.18.6"
@@ -330,10 +351,10 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8":
-  version "7.21.8"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8"
-  integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==
+"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.9", "@babel/parser@^7.22.0", "@babel/parser@^7.22.4":
+  version "7.22.4"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32"
+  integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==
 
 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
   version "7.18.6"
@@ -342,125 +363,14 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1"
-  integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz#a75be1365c0c3188c51399a662168c1c98108659"
+  integrity sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ==
   dependencies:
-    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/helper-plugin-utils" "^7.21.5"
     "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
-    "@babel/plugin-proposal-optional-chaining" "^7.20.7"
-
-"@babel/plugin-proposal-async-generator-functions@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326"
-  integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==
-  dependencies:
-    "@babel/helper-environment-visitor" "^7.18.9"
-    "@babel/helper-plugin-utils" "^7.20.2"
-    "@babel/helper-remap-async-to-generator" "^7.18.9"
-    "@babel/plugin-syntax-async-generators" "^7.8.4"
-
-"@babel/plugin-proposal-class-properties@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3"
-  integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==
-  dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.6"
-
-"@babel/plugin-proposal-class-static-block@^7.21.0":
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d"
-  integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==
-  dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.21.0"
-    "@babel/helper-plugin-utils" "^7.20.2"
-    "@babel/plugin-syntax-class-static-block" "^7.14.5"
-
-"@babel/plugin-proposal-dynamic-import@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94"
-  integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/plugin-syntax-dynamic-import" "^7.8.3"
-
-"@babel/plugin-proposal-export-namespace-from@^7.18.9":
-  version "7.18.9"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203"
-  integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.18.9"
-    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
-
-"@babel/plugin-proposal-json-strings@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b"
-  integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/plugin-syntax-json-strings" "^7.8.3"
-
-"@babel/plugin-proposal-logical-assignment-operators@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83"
-  integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.20.2"
-    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
-
-"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1"
-  integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
-
-"@babel/plugin-proposal-numeric-separator@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75"
-  integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
-
-"@babel/plugin-proposal-object-rest-spread@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a"
-  integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==
-  dependencies:
-    "@babel/compat-data" "^7.20.5"
-    "@babel/helper-compilation-targets" "^7.20.7"
-    "@babel/helper-plugin-utils" "^7.20.2"
-    "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
-    "@babel/plugin-transform-parameters" "^7.20.7"
-
-"@babel/plugin-proposal-optional-catch-binding@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb"
-  integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
-
-"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0":
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea"
-  integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.20.2"
-    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
-    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
-
-"@babel/plugin-proposal-private-methods@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea"
-  integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==
-  dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.6"
+    "@babel/plugin-transform-optional-chaining" "^7.22.3"
 
 "@babel/plugin-proposal-private-property-in-object@^7.21.0":
   version "7.21.0"
@@ -472,7 +382,7 @@
     "@babel/helper-plugin-utils" "^7.20.2"
     "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
 
-"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e"
   integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==
@@ -529,6 +439,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.19.0"
 
+"@babel/plugin-syntax-import-attributes@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz#d7168f22b9b49a6cc1792cec78e06a18ad2e7b4b"
+  integrity sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+
 "@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
@@ -543,7 +460,7 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
-"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2":
+"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2":
   version "7.21.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2"
   integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==
@@ -620,6 +537,14 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
+"@babel/plugin-syntax-unicode-sets-regex@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357"
+  integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.18.6"
+
 "@babel/plugin-transform-arrow-functions@^7.21.5":
   version "7.21.5"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929"
@@ -627,6 +552,16 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.21.5"
 
+"@babel/plugin-transform-async-generator-functions@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz#3ed99924c354fb9e80dabb2cc8d002c702e94527"
+  integrity sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA==
+  dependencies:
+    "@babel/helper-environment-visitor" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/helper-remap-async-to-generator" "^7.18.9"
+    "@babel/plugin-syntax-async-generators" "^7.8.4"
+
 "@babel/plugin-transform-async-to-generator@^7.20.7":
   version "7.20.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354"
@@ -650,6 +585,23 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.20.2"
 
+"@babel/plugin-transform-class-properties@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz#3407145e513830df77f0cef828b8b231c166fe4c"
+  integrity sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+
+"@babel/plugin-transform-class-static-block@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz#e352cf33567385c731a8f21192efeba760358773"
+  integrity sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-class-static-block" "^7.14.5"
+
 "@babel/plugin-transform-classes@^7.21.0":
   version "7.21.0"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665"
@@ -695,6 +647,14 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.9"
 
+"@babel/plugin-transform-dynamic-import@^7.22.1":
+  version "7.22.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz#6c56afaf896a07026330cf39714532abed8d9ed1"
+  integrity sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+
 "@babel/plugin-transform-exponentiation-operator@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd"
@@ -703,6 +663,14 @@
     "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6"
     "@babel/helper-plugin-utils" "^7.18.6"
 
+"@babel/plugin-transform-export-namespace-from@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz#9b8700aa495007d3bebac8358d1c562434b680b9"
+  integrity sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
 "@babel/plugin-transform-for-of@^7.21.5":
   version "7.21.5"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc"
@@ -719,6 +687,14 @@
     "@babel/helper-function-name" "^7.18.9"
     "@babel/helper-plugin-utils" "^7.18.9"
 
+"@babel/plugin-transform-json-strings@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz#a181b8679cf7c93e9d0e3baa5b1776d65be601a9"
+  integrity sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-json-strings" "^7.8.3"
+
 "@babel/plugin-transform-literals@^7.18.9":
   version "7.18.9"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc"
@@ -726,6 +702,14 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.9"
 
+"@babel/plugin-transform-logical-assignment-operators@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz#9e021455810f33b0baccb82fb759b194f5dc36f0"
+  integrity sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
 "@babel/plugin-transform-member-expression-literals@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e"
@@ -750,14 +734,14 @@
     "@babel/helper-plugin-utils" "^7.21.5"
     "@babel/helper-simple-access" "^7.21.5"
 
-"@babel/plugin-transform-modules-systemjs@^7.20.11":
-  version "7.20.11"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e"
-  integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==
+"@babel/plugin-transform-modules-systemjs@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz#cc507e03e88d87b016feaeb5dae941e6ef50d91e"
+  integrity sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw==
   dependencies:
     "@babel/helper-hoist-variables" "^7.18.6"
-    "@babel/helper-module-transforms" "^7.20.11"
-    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/helper-module-transforms" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
     "@babel/helper-validator-identifier" "^7.19.1"
 
 "@babel/plugin-transform-modules-umd@^7.18.6":
@@ -768,20 +752,47 @@
     "@babel/helper-module-transforms" "^7.18.6"
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5":
-  version "7.20.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8"
-  integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==
+"@babel/plugin-transform-named-capturing-groups-regex@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz#db6fb77e6b3b53ec3b8d370246f0b7cf67d35ab4"
+  integrity sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q==
   dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.20.5"
-    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/helper-create-regexp-features-plugin" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
 
-"@babel/plugin-transform-new-target@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8"
-  integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==
+"@babel/plugin-transform-new-target@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz#deb0377d741cbee2f45305868b9026dcd6dd96e2"
+  integrity sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w==
   dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.21.5"
+
+"@babel/plugin-transform-nullish-coalescing-operator@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz#8c519f8bf5af94a9ca6f65cf422a9d3396e542b9"
+  integrity sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+
+"@babel/plugin-transform-numeric-separator@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz#02493070ca6685884b0eee705363ee4da2132ab0"
+  integrity sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-transform-object-rest-spread@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz#da6fba693effb8c203d8c3bdf7bf4e2567e802e9"
+  integrity sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw==
+  dependencies:
+    "@babel/compat-data" "^7.22.3"
+    "@babel/helper-compilation-targets" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+    "@babel/plugin-transform-parameters" "^7.22.3"
 
 "@babel/plugin-transform-object-super@^7.18.6":
   version "7.18.6"
@@ -791,12 +802,47 @@
     "@babel/helper-plugin-utils" "^7.18.6"
     "@babel/helper-replace-supers" "^7.18.6"
 
-"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3":
-  version "7.21.3"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db"
-  integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==
+"@babel/plugin-transform-optional-catch-binding@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz#e971a083fc7d209d9cd18253853af1db6d8dc42f"
+  integrity sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g==
   dependencies:
-    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+
+"@babel/plugin-transform-optional-chaining@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz#5fd24a4a7843b76da6aeec23c7f551da5d365290"
+  integrity sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
+"@babel/plugin-transform-parameters@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz#24477acfd2fd2bc901df906c9bf17fbcfeee900d"
+  integrity sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.21.5"
+
+"@babel/plugin-transform-private-methods@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz#adac38020bab5047482d3297107c1f58e9c574f6"
+  integrity sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+
+"@babel/plugin-transform-private-property-in-object@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz#031621b02c7b7d95389de1a3dba2fe9e8c548e56"
+  integrity sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.18.6"
+    "@babel/helper-create-class-features-plugin" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
 
 "@babel/plugin-transform-property-literals@^7.18.6":
   version "7.18.6"
@@ -827,16 +873,16 @@
   dependencies:
     "@babel/plugin-transform-react-jsx" "^7.18.6"
 
-"@babel/plugin-transform-react-jsx@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff"
-  integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw==
+"@babel/plugin-transform-react-jsx@^7.18.6", "@babel/plugin-transform-react-jsx@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz#5a1f380df3703ba92eb1a930a539c6d88836f690"
+  integrity sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.18.6"
-    "@babel/helper-module-imports" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/plugin-syntax-jsx" "^7.18.6"
-    "@babel/types" "^7.18.6"
+    "@babel/helper-module-imports" "^7.21.4"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/plugin-syntax-jsx" "^7.21.4"
+    "@babel/types" "^7.22.3"
 
 "@babel/plugin-transform-react-pure-annotations@^7.18.6":
   version "7.18.6"
@@ -861,16 +907,16 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/plugin-transform-runtime@^7.21.4":
-  version "7.21.4"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz#2e1da21ca597a7d01fc96b699b21d8d2023191aa"
-  integrity sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==
+"@babel/plugin-transform-runtime@^7.22.4":
+  version "7.22.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.4.tgz#f8353f313f18c3ce1315688631ec48657b97af42"
+  integrity sha512-Urkiz1m4zqiRo17klj+l3nXgiRTFQng91Bc1eiLF7BMQu1e7wE5Gcq9xSv062IF068NHjcutSbIMev60gXxAvA==
   dependencies:
     "@babel/helper-module-imports" "^7.21.4"
-    "@babel/helper-plugin-utils" "^7.20.2"
-    babel-plugin-polyfill-corejs2 "^0.3.3"
-    babel-plugin-polyfill-corejs3 "^0.6.0"
-    babel-plugin-polyfill-regenerator "^0.4.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    babel-plugin-polyfill-corejs2 "^0.4.3"
+    babel-plugin-polyfill-corejs3 "^0.8.1"
+    babel-plugin-polyfill-regenerator "^0.5.0"
     semver "^6.3.0"
 
 "@babel/plugin-transform-shorthand-properties@^7.18.6":
@@ -926,6 +972,14 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.21.5"
 
+"@babel/plugin-transform-unicode-property-regex@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz#597b6a614dc93eaae605ee293e674d79d32eb380"
+  integrity sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+
 "@babel/plugin-transform-unicode-regex@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca"
@@ -934,38 +988,33 @@
     "@babel/helper-create-regexp-features-plugin" "^7.18.6"
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.21.5":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb"
-  integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==
+"@babel/plugin-transform-unicode-sets-regex@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz#7c14ee33fa69782b0101d0f7143d3fc73ce00700"
+  integrity sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw==
   dependencies:
-    "@babel/compat-data" "^7.21.5"
-    "@babel/helper-compilation-targets" "^7.21.5"
+    "@babel/helper-create-regexp-features-plugin" "^7.22.1"
+    "@babel/helper-plugin-utils" "^7.21.5"
+
+"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.22.4":
+  version "7.22.4"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.4.tgz#c86a82630f0e8c61d9bb9327b7b896732028cbed"
+  integrity sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ==
+  dependencies:
+    "@babel/compat-data" "^7.22.3"
+    "@babel/helper-compilation-targets" "^7.22.1"
     "@babel/helper-plugin-utils" "^7.21.5"
     "@babel/helper-validator-option" "^7.21.0"
     "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6"
-    "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7"
-    "@babel/plugin-proposal-async-generator-functions" "^7.20.7"
-    "@babel/plugin-proposal-class-properties" "^7.18.6"
-    "@babel/plugin-proposal-class-static-block" "^7.21.0"
-    "@babel/plugin-proposal-dynamic-import" "^7.18.6"
-    "@babel/plugin-proposal-export-namespace-from" "^7.18.9"
-    "@babel/plugin-proposal-json-strings" "^7.18.6"
-    "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7"
-    "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6"
-    "@babel/plugin-proposal-numeric-separator" "^7.18.6"
-    "@babel/plugin-proposal-object-rest-spread" "^7.20.7"
-    "@babel/plugin-proposal-optional-catch-binding" "^7.18.6"
-    "@babel/plugin-proposal-optional-chaining" "^7.21.0"
-    "@babel/plugin-proposal-private-methods" "^7.18.6"
+    "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.3"
     "@babel/plugin-proposal-private-property-in-object" "^7.21.0"
-    "@babel/plugin-proposal-unicode-property-regex" "^7.18.6"
     "@babel/plugin-syntax-async-generators" "^7.8.4"
     "@babel/plugin-syntax-class-properties" "^7.12.13"
     "@babel/plugin-syntax-class-static-block" "^7.14.5"
     "@babel/plugin-syntax-dynamic-import" "^7.8.3"
     "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
     "@babel/plugin-syntax-import-assertions" "^7.20.0"
+    "@babel/plugin-syntax-import-attributes" "^7.22.3"
     "@babel/plugin-syntax-import-meta" "^7.10.4"
     "@babel/plugin-syntax-json-strings" "^7.8.3"
     "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
@@ -976,28 +1025,43 @@
     "@babel/plugin-syntax-optional-chaining" "^7.8.3"
     "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
     "@babel/plugin-syntax-top-level-await" "^7.14.5"
+    "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6"
     "@babel/plugin-transform-arrow-functions" "^7.21.5"
+    "@babel/plugin-transform-async-generator-functions" "^7.22.3"
     "@babel/plugin-transform-async-to-generator" "^7.20.7"
     "@babel/plugin-transform-block-scoped-functions" "^7.18.6"
     "@babel/plugin-transform-block-scoping" "^7.21.0"
+    "@babel/plugin-transform-class-properties" "^7.22.3"
+    "@babel/plugin-transform-class-static-block" "^7.22.3"
     "@babel/plugin-transform-classes" "^7.21.0"
     "@babel/plugin-transform-computed-properties" "^7.21.5"
     "@babel/plugin-transform-destructuring" "^7.21.3"
     "@babel/plugin-transform-dotall-regex" "^7.18.6"
     "@babel/plugin-transform-duplicate-keys" "^7.18.9"
+    "@babel/plugin-transform-dynamic-import" "^7.22.1"
     "@babel/plugin-transform-exponentiation-operator" "^7.18.6"
+    "@babel/plugin-transform-export-namespace-from" "^7.22.3"
     "@babel/plugin-transform-for-of" "^7.21.5"
     "@babel/plugin-transform-function-name" "^7.18.9"
+    "@babel/plugin-transform-json-strings" "^7.22.3"
     "@babel/plugin-transform-literals" "^7.18.9"
+    "@babel/plugin-transform-logical-assignment-operators" "^7.22.3"
     "@babel/plugin-transform-member-expression-literals" "^7.18.6"
     "@babel/plugin-transform-modules-amd" "^7.20.11"
     "@babel/plugin-transform-modules-commonjs" "^7.21.5"
-    "@babel/plugin-transform-modules-systemjs" "^7.20.11"
+    "@babel/plugin-transform-modules-systemjs" "^7.22.3"
     "@babel/plugin-transform-modules-umd" "^7.18.6"
-    "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5"
-    "@babel/plugin-transform-new-target" "^7.18.6"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.3"
+    "@babel/plugin-transform-new-target" "^7.22.3"
+    "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.3"
+    "@babel/plugin-transform-numeric-separator" "^7.22.3"
+    "@babel/plugin-transform-object-rest-spread" "^7.22.3"
     "@babel/plugin-transform-object-super" "^7.18.6"
-    "@babel/plugin-transform-parameters" "^7.21.3"
+    "@babel/plugin-transform-optional-catch-binding" "^7.22.3"
+    "@babel/plugin-transform-optional-chaining" "^7.22.3"
+    "@babel/plugin-transform-parameters" "^7.22.3"
+    "@babel/plugin-transform-private-methods" "^7.22.3"
+    "@babel/plugin-transform-private-property-in-object" "^7.22.3"
     "@babel/plugin-transform-property-literals" "^7.18.6"
     "@babel/plugin-transform-regenerator" "^7.21.5"
     "@babel/plugin-transform-reserved-words" "^7.18.6"
@@ -1007,13 +1071,15 @@
     "@babel/plugin-transform-template-literals" "^7.18.9"
     "@babel/plugin-transform-typeof-symbol" "^7.18.9"
     "@babel/plugin-transform-unicode-escapes" "^7.21.5"
+    "@babel/plugin-transform-unicode-property-regex" "^7.22.3"
     "@babel/plugin-transform-unicode-regex" "^7.18.6"
+    "@babel/plugin-transform-unicode-sets-regex" "^7.22.3"
     "@babel/preset-modules" "^0.1.5"
-    "@babel/types" "^7.21.5"
-    babel-plugin-polyfill-corejs2 "^0.3.3"
-    babel-plugin-polyfill-corejs3 "^0.6.0"
-    babel-plugin-polyfill-regenerator "^0.4.1"
-    core-js-compat "^3.25.1"
+    "@babel/types" "^7.22.4"
+    babel-plugin-polyfill-corejs2 "^0.4.3"
+    babel-plugin-polyfill-corejs3 "^0.8.1"
+    babel-plugin-polyfill-regenerator "^0.5.0"
+    core-js-compat "^3.30.2"
     semver "^6.3.0"
 
 "@babel/preset-modules@^0.1.5":
@@ -1027,15 +1093,15 @@
     "@babel/types" "^7.4.4"
     esutils "^2.0.2"
 
-"@babel/preset-react@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d"
-  integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==
+"@babel/preset-react@^7.22.3":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.22.3.tgz#2ec7f91d0c924fa2ea0c7cfbbf690bc62b79cd84"
+  integrity sha512-lxDz1mnZ9polqClBCVBjIVUypoB4qV3/tZUDb/IlYbW1kiiLaXaX+bInbRjl+lNQ/iUZraQ3+S8daEmoELMWug==
   dependencies:
-    "@babel/helper-plugin-utils" "^7.18.6"
-    "@babel/helper-validator-option" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.21.5"
+    "@babel/helper-validator-option" "^7.21.0"
     "@babel/plugin-transform-react-display-name" "^7.18.6"
-    "@babel/plugin-transform-react-jsx" "^7.18.6"
+    "@babel/plugin-transform-react-jsx" "^7.22.3"
     "@babel/plugin-transform-react-jsx-development" "^7.18.6"
     "@babel/plugin-transform-react-pure-annotations" "^7.18.6"
 
@@ -1062,42 +1128,42 @@
   dependencies:
     regenerator-runtime "^0.12.0"
 
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
-  integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.22.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
+  version "7.22.3"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb"
+  integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==
   dependencies:
     regenerator-runtime "^0.13.11"
 
-"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
-  integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==
-  dependencies:
-    "@babel/code-frame" "^7.18.6"
-    "@babel/parser" "^7.20.7"
-    "@babel/types" "^7.20.7"
-
-"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.5", "@babel/traverse@^7.7.2":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133"
-  integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==
+"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9", "@babel/template@^7.3.3":
+  version "7.21.9"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb"
+  integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==
   dependencies:
     "@babel/code-frame" "^7.21.4"
-    "@babel/generator" "^7.21.5"
-    "@babel/helper-environment-visitor" "^7.21.5"
+    "@babel/parser" "^7.21.9"
+    "@babel/types" "^7.21.5"
+
+"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.22.1", "@babel/traverse@^7.7.2":
+  version "7.22.4"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0"
+  integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==
+  dependencies:
+    "@babel/code-frame" "^7.21.4"
+    "@babel/generator" "^7.22.3"
+    "@babel/helper-environment-visitor" "^7.22.1"
     "@babel/helper-function-name" "^7.21.0"
     "@babel/helper-hoist-variables" "^7.18.6"
     "@babel/helper-split-export-declaration" "^7.18.6"
-    "@babel/parser" "^7.21.5"
-    "@babel/types" "^7.21.5"
+    "@babel/parser" "^7.22.4"
+    "@babel/types" "^7.22.4"
     debug "^4.1.0"
     globals "^11.1.0"
 
-"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
-  version "7.21.5"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6"
-  integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==
+"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+  version "7.22.4"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071"
+  integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==
   dependencies:
     "@babel/helper-string-parser" "^7.21.5"
     "@babel/helper-validator-identifier" "^7.19.1"
@@ -1867,21 +1933,10 @@
   resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc"
   integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==
 
-"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3":
-  version "7.1.18"
-  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8"
-  integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==
-  dependencies:
-    "@babel/parser" "^7.1.0"
-    "@babel/types" "^7.0.0"
-    "@types/babel__generator" "*"
-    "@types/babel__template" "*"
-    "@types/babel__traverse" "*"
-
-"@types/babel__core@^7.20.0":
-  version "7.20.0"
-  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891"
-  integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==
+"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3", "@types/babel__core@^7.20.1":
+  version "7.20.1"
+  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b"
+  integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==
   dependencies:
     "@babel/parser" "^7.20.7"
     "@babel/types" "^7.20.7"
@@ -2087,10 +2142,10 @@
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
   integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
 
-"@types/lodash@^4.14.194":
-  version "4.14.194"
-  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76"
-  integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==
+"@types/lodash@^4.14.195":
+  version "4.14.195"
+  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632"
+  integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==
 
 "@types/mime@*":
   version "3.0.1"
@@ -2156,12 +2211,7 @@
   resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0"
   integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==
 
-"@types/prop-types@*":
-  version "15.7.3"
-  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
-  integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
-
-"@types/prop-types@^15.7.5":
+"@types/prop-types@*", "@types/prop-types@^15.7.5":
   version "15.7.5"
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
   integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
@@ -2208,10 +2258,10 @@
   resolved "https://registry.yarnpkg.com/@types/react-intl/-/react-intl-2.3.18.tgz#fd2d8b7f4d0a1dd05b5f1784ab0d7fe1786a690d"
   integrity sha512-DVNJs49zUxKRZng8VuILE886Yihdsf3yLr5vHk9zJrmF8SyRSK3sxNSvikAKxNkv9hX55XBTJShz6CkJnbNjgg==
 
-"@types/react-motion@^0.0.33":
-  version "0.0.33"
-  resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.33.tgz#c156c400ace995584990344cc0239e41f411f425"
-  integrity sha512-R9grd4EwdDBcKKq7Zhszd8ukyy2BLKN6ooNI0V39nUl/sui+m7VI94cdebYemBteoPHmO7J7BZk+cIf+Xnk4TA==
+"@types/react-motion@^0.0.34":
+  version "0.0.34"
+  resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.34.tgz#789ff2063e2f7fbb6085d291135c442e8b35291a"
+  integrity sha512-/rFI22Vg4Xzb47hXtS06WkzUGRu+Vb3yDleuxiqzGj0JbXYXQUCgwSa2ZU12K7ubKi4C8xsdIN3xt4Z4fjSdPw==
   dependencies:
     "@types/react" "*"
 
@@ -2288,10 +2338,10 @@
   dependencies:
     "@types/react" "*"
 
-"@types/react@*", "@types/react@>=16.9.11", "@types/react@^18.0.26":
-  version "18.2.6"
-  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571"
-  integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==
+"@types/react@*", "@types/react@>=16.9.11", "@types/react@^18.0.26", "@types/react@^18.2.7":
+  version "18.2.7"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.7.tgz#dfb4518042a3117a045b8c222316f83414a783b3"
+  integrity sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==
   dependencies:
     "@types/prop-types" "*"
     "@types/scheduler" "*"
@@ -2434,15 +2484,15 @@
   dependencies:
     "@types/yargs-parser" "*"
 
-"@typescript-eslint/eslint-plugin@^5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.7.tgz#e470af414f05ecfdc05a23e9ce6ec8f91db56fe2"
-  integrity sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==
+"@typescript-eslint/eslint-plugin@^5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz#1e7a3e5318ece22251dfbc5c9c6feeb4793cc509"
+  integrity sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==
   dependencies:
     "@eslint-community/regexpp" "^4.4.0"
-    "@typescript-eslint/scope-manager" "5.59.7"
-    "@typescript-eslint/type-utils" "5.59.7"
-    "@typescript-eslint/utils" "5.59.7"
+    "@typescript-eslint/scope-manager" "5.59.8"
+    "@typescript-eslint/type-utils" "5.59.8"
+    "@typescript-eslint/utils" "5.59.8"
     debug "^4.3.4"
     grapheme-splitter "^1.0.4"
     ignore "^5.2.0"
@@ -2450,31 +2500,31 @@
     semver "^7.3.7"
     tsutils "^3.21.0"
 
-"@typescript-eslint/parser@^5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.7.tgz#02682554d7c1028b89aa44a48bf598db33048caa"
-  integrity sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==
+"@typescript-eslint/parser@^5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.8.tgz#60cbb00671d86cf746044ab797900b1448188567"
+  integrity sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==
   dependencies:
-    "@typescript-eslint/scope-manager" "5.59.7"
-    "@typescript-eslint/types" "5.59.7"
-    "@typescript-eslint/typescript-estree" "5.59.7"
+    "@typescript-eslint/scope-manager" "5.59.8"
+    "@typescript-eslint/types" "5.59.8"
+    "@typescript-eslint/typescript-estree" "5.59.8"
     debug "^4.3.4"
 
-"@typescript-eslint/scope-manager@5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz#0243f41f9066f3339d2f06d7f72d6c16a16769e2"
-  integrity sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==
+"@typescript-eslint/scope-manager@5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz#ff4ad4fec6433647b817c4a7d4b4165d18ea2fa8"
+  integrity sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==
   dependencies:
-    "@typescript-eslint/types" "5.59.7"
-    "@typescript-eslint/visitor-keys" "5.59.7"
+    "@typescript-eslint/types" "5.59.8"
+    "@typescript-eslint/visitor-keys" "5.59.8"
 
-"@typescript-eslint/type-utils@5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.7.tgz#89c97291371b59eb18a68039857c829776f1426d"
-  integrity sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==
+"@typescript-eslint/type-utils@5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz#aa6c029a9d7706d26bbd25eb4666398781df6ea2"
+  integrity sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==
   dependencies:
-    "@typescript-eslint/typescript-estree" "5.59.7"
-    "@typescript-eslint/utils" "5.59.7"
+    "@typescript-eslint/typescript-estree" "5.59.8"
+    "@typescript-eslint/utils" "5.59.8"
     debug "^4.3.4"
     tsutils "^3.21.0"
 
@@ -2483,10 +2533,10 @@
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32"
   integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==
 
-"@typescript-eslint/types@5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.7.tgz#6f4857203fceee91d0034ccc30512d2939000742"
-  integrity sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==
+"@typescript-eslint/types@5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.8.tgz#212e54414733618f5d0fd50b2da2717f630aebf8"
+  integrity sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==
 
 "@typescript-eslint/typescript-estree@5.59.0":
   version "5.59.0"
@@ -2501,30 +2551,30 @@
     semver "^7.3.7"
     tsutils "^3.21.0"
 
-"@typescript-eslint/typescript-estree@5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz#b887acbd4b58e654829c94860dbff4ac55c5cff8"
-  integrity sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==
+"@typescript-eslint/typescript-estree@5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz#801a7b1766481629481b3b0878148bd7a1f345d7"
+  integrity sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==
   dependencies:
-    "@typescript-eslint/types" "5.59.7"
-    "@typescript-eslint/visitor-keys" "5.59.7"
+    "@typescript-eslint/types" "5.59.8"
+    "@typescript-eslint/visitor-keys" "5.59.8"
     debug "^4.3.4"
     globby "^11.1.0"
     is-glob "^4.0.3"
     semver "^7.3.7"
     tsutils "^3.21.0"
 
-"@typescript-eslint/utils@5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.7.tgz#7adf068b136deae54abd9a66ba5a8780d2d0f898"
-  integrity sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==
+"@typescript-eslint/utils@5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.8.tgz#34d129f35a2134c67fdaf024941e8f96050dca2b"
+  integrity sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==
   dependencies:
     "@eslint-community/eslint-utils" "^4.2.0"
     "@types/json-schema" "^7.0.9"
     "@types/semver" "^7.3.12"
-    "@typescript-eslint/scope-manager" "5.59.7"
-    "@typescript-eslint/types" "5.59.7"
-    "@typescript-eslint/typescript-estree" "5.59.7"
+    "@typescript-eslint/scope-manager" "5.59.8"
+    "@typescript-eslint/types" "5.59.8"
+    "@typescript-eslint/typescript-estree" "5.59.8"
     eslint-scope "^5.1.1"
     semver "^7.3.7"
 
@@ -2536,12 +2586,12 @@
     "@typescript-eslint/types" "5.59.0"
     eslint-visitor-keys "^3.3.0"
 
-"@typescript-eslint/visitor-keys@5.59.7":
-  version "5.59.7"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz#09c36eaf268086b4fbb5eb9dc5199391b6485fc5"
-  integrity sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==
+"@typescript-eslint/visitor-keys@5.59.8":
+  version "5.59.8"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz#aa6a7ef862add919401470c09e1609392ef3cc40"
+  integrity sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==
   dependencies:
-    "@typescript-eslint/types" "5.59.7"
+    "@typescript-eslint/types" "5.59.8"
     eslint-visitor-keys "^3.3.0"
 
 "@webassemblyjs/ast@1.9.0":
@@ -3219,29 +3269,29 @@ babel-plugin-macros@^3.0.1:
     cosmiconfig "^7.0.0"
     resolve "^1.19.0"
 
-babel-plugin-polyfill-corejs2@^0.3.3:
-  version "0.3.3"
-  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122"
-  integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==
+babel-plugin-polyfill-corejs2@^0.4.3:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz#75044d90ba5043a5fb559ac98496f62f3eb668fd"
+  integrity sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw==
   dependencies:
     "@babel/compat-data" "^7.17.7"
-    "@babel/helper-define-polyfill-provider" "^0.3.3"
+    "@babel/helper-define-polyfill-provider" "^0.4.0"
     semver "^6.1.1"
 
-babel-plugin-polyfill-corejs3@^0.6.0:
-  version "0.6.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a"
-  integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==
+babel-plugin-polyfill-corejs3@^0.8.1:
+  version "0.8.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz#39248263c38191f0d226f928d666e6db1b4b3a8a"
+  integrity sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q==
   dependencies:
-    "@babel/helper-define-polyfill-provider" "^0.3.3"
-    core-js-compat "^3.25.1"
+    "@babel/helper-define-polyfill-provider" "^0.4.0"
+    core-js-compat "^3.30.1"
 
-babel-plugin-polyfill-regenerator@^0.4.1:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747"
-  integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==
+babel-plugin-polyfill-regenerator@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz#e7344d88d9ef18a3c47ded99362ae4a757609380"
+  integrity sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g==
   dependencies:
-    "@babel/helper-define-polyfill-provider" "^0.3.3"
+    "@babel/helper-define-polyfill-provider" "^0.4.0"
 
 babel-plugin-preval@^5.1.0:
   version "5.1.0"
@@ -4093,12 +4143,12 @@ copy-descriptor@^0.1.0:
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
   integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
 
-core-js-compat@^3.25.1:
-  version "3.25.2"
-  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.2.tgz#7875573586809909c69e03ef310810c1969ee138"
-  integrity sha512-TxfyECD4smdn3/CjWxczVtJqVLEEC2up7/82t7vC0AzNogr+4nQ8vyF7abxAuTXWvjTClSbvGhU0RgqA4ToQaQ==
+core-js-compat@^3.30.1, core-js-compat@^3.30.2:
+  version "3.30.2"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b"
+  integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==
   dependencies:
-    browserslist "^4.21.4"
+    browserslist "^4.21.5"
 
 core-js@^2.5.0:
   version "2.6.12"
@@ -5080,10 +5130,10 @@ eslint-plugin-import@~2.27.5:
     semver "^6.3.0"
     tsconfig-paths "^3.14.1"
 
-eslint-plugin-jsdoc@^44.2.5:
-  version "44.2.5"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-44.2.5.tgz#f3186f57f112a230b3b7af34bf236d207bc8d5d7"
-  integrity sha512-KtuhaYy2GmdY2IQE5t+1lup8O4P05c+V4gKcj45PCxFM0OxmRq2uQlfOS1AgYVgPYIBKGE86DxrbKP24HKpORA==
+eslint-plugin-jsdoc@^45.0.0:
+  version "45.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-45.0.0.tgz#6be84e4842a7138cc571a907ea9c31c42eaac5c0"
+  integrity sha512-l2+Jcs/Ps7oFA+SWY+0sweU/e5LgricnEl6EsDlyRTF5y0+NWL1y9Qwz9PHwHAxtdJq6lxPjEQWmYLMkvhzD4g==
   dependencies:
     "@es-joy/jsdoccomment" "~0.39.4"
     are-docs-informative "^0.0.2"
@@ -7475,10 +7525,10 @@ jsdom@^20.0.0:
     ws "^8.11.0"
     xml-name-validator "^4.0.0"
 
-jsdom@^22.0.0:
-  version "22.0.0"
-  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.0.0.tgz#3295c6992c70089c4b8f5cf060489fddf7ee9816"
-  integrity sha512-p5ZTEb5h+O+iU02t0GfEjAnkdYPrQSkfuTSMkMYyIoMvUNEHsbG0bHHbfXIcfTqD2UfvjQX7mmgiFsyRwGscVw==
+jsdom@^22.1.0:
+  version "22.1.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8"
+  integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==
   dependencies:
     abab "^2.0.6"
     cssstyle "^3.0.0"
@@ -9256,10 +9306,10 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
   integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
 
-postcss@^8.2.15, postcss@^8.4.23:
-  version "8.4.23"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
-  integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
+postcss@^8.2.15, postcss@^8.4.23, postcss@^8.4.24:
+  version "8.4.24"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df"
+  integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==
   dependencies:
     nanoid "^3.3.6"
     picocolors "^1.0.0"
@@ -12145,25 +12195,25 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
   integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
 
-workbox-background-sync@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz#3141afba3cc8aa2ae14c24d0f6811374ba8ff6a9"
-  integrity sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==
+workbox-background-sync@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f"
+  integrity sha512-trJd3ovpWCvzu4sW0E8rV3FUyIcC0W8G+AZ+VcqzzA890AsWZlUGOTSxIMmIHVusUw/FDq1HFWfy/kC/WTRqSg==
   dependencies:
     idb "^7.0.1"
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-broadcast-update@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz#8441cff5417cd41f384ba7633ca960a7ffe40f66"
-  integrity sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==
+workbox-broadcast-update@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.6.1.tgz#0fad9454cf8e4ace0c293e5617c64c75d8a8c61e"
+  integrity sha512-fBhffRdaANdeQ1V8s692R9l/gzvjjRtydBOvR6WCSB0BNE2BacA29Z4r9/RHd9KaXCPl6JTdI9q0bR25YKP8TQ==
   dependencies:
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-build@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.5.4.tgz#7d06d31eb28a878817e1c991c05c5b93409f0389"
-  integrity sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==
+workbox-build@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.6.1.tgz#6010e9ce550910156761448f2dbea8cfcf759cb0"
+  integrity sha512-INPgDx6aRycAugUixbKgiEQBWD0MPZqU5r0jyr24CehvNuLPSXp/wGOpdRJmts656lNiXwqV7dC2nzyrzWEDnw==
   dependencies:
     "@apideck/better-ajv-errors" "^0.3.1"
     "@babel/core" "^7.11.1"
@@ -12187,132 +12237,132 @@ workbox-build@6.5.4:
     strip-comments "^2.0.1"
     tempy "^0.6.0"
     upath "^1.2.0"
-    workbox-background-sync "6.5.4"
-    workbox-broadcast-update "6.5.4"
-    workbox-cacheable-response "6.5.4"
-    workbox-core "6.5.4"
-    workbox-expiration "6.5.4"
-    workbox-google-analytics "6.5.4"
-    workbox-navigation-preload "6.5.4"
-    workbox-precaching "6.5.4"
-    workbox-range-requests "6.5.4"
-    workbox-recipes "6.5.4"
-    workbox-routing "6.5.4"
-    workbox-strategies "6.5.4"
-    workbox-streams "6.5.4"
-    workbox-sw "6.5.4"
-    workbox-window "6.5.4"
+    workbox-background-sync "6.6.1"
+    workbox-broadcast-update "6.6.1"
+    workbox-cacheable-response "6.6.1"
+    workbox-core "6.6.1"
+    workbox-expiration "6.6.1"
+    workbox-google-analytics "6.6.1"
+    workbox-navigation-preload "6.6.1"
+    workbox-precaching "6.6.1"
+    workbox-range-requests "6.6.1"
+    workbox-recipes "6.6.1"
+    workbox-routing "6.6.1"
+    workbox-strategies "6.6.1"
+    workbox-streams "6.6.1"
+    workbox-sw "6.6.1"
+    workbox-window "6.6.1"
 
-workbox-cacheable-response@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz#a5c6ec0c6e2b6f037379198d4ef07d098f7cf137"
-  integrity sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==
+workbox-cacheable-response@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.6.1.tgz#284c2b86be3f4fd191970ace8c8e99797bcf58e9"
+  integrity sha512-85LY4veT2CnTCDxaVG7ft3NKaFbH6i4urZXgLiU4AiwvKqS2ChL6/eILiGRYXfZ6gAwDnh5RkuDbr/GMS4KSag==
   dependencies:
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-core@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.5.4.tgz#df48bf44cd58bb1d1726c49b883fb1dffa24c9ba"
-  integrity sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==
+workbox-core@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.6.1.tgz#7184776d4134c5ed2f086878c882728fc9084265"
+  integrity sha512-ZrGBXjjaJLqzVothoE12qTbVnOAjFrHDXpZe7coCb6q65qI/59rDLwuFMO4PcZ7jcbxY+0+NhUVztzR/CbjEFw==
 
-workbox-expiration@6.5.4, workbox-expiration@^6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.5.4.tgz#501056f81e87e1d296c76570bb483ce5e29b4539"
-  integrity sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==
+workbox-expiration@6.6.1, workbox-expiration@^6.6.0:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.6.1.tgz#a841fa36676104426dbfb9da1ef6a630b4f93739"
+  integrity sha512-qFiNeeINndiOxaCrd2DeL1Xh1RFug3JonzjxUHc5WkvkD2u5abY3gZL1xSUNt3vZKsFFGGORItSjVTVnWAZO4A==
   dependencies:
     idb "^7.0.1"
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-google-analytics@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz#c74327f80dfa4c1954cbba93cd7ea640fe7ece7d"
-  integrity sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==
+workbox-google-analytics@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.6.1.tgz#a07a6655ab33d89d1b0b0a935ffa5dea88618c5d"
+  integrity sha512-1TjSvbFSLmkpqLcBsF7FuGqqeDsf+uAXO/pjiINQKg3b1GN0nBngnxLcXDYo1n/XxK4N7RaRrpRlkwjY/3ocuA==
   dependencies:
-    workbox-background-sync "6.5.4"
-    workbox-core "6.5.4"
-    workbox-routing "6.5.4"
-    workbox-strategies "6.5.4"
+    workbox-background-sync "6.6.1"
+    workbox-core "6.6.1"
+    workbox-routing "6.6.1"
+    workbox-strategies "6.6.1"
 
-workbox-navigation-preload@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz#ede56dd5f6fc9e860a7e45b2c1a8f87c1c793212"
-  integrity sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==
+workbox-navigation-preload@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.6.1.tgz#61a34fe125558dd88cf09237f11bd966504ea059"
+  integrity sha512-DQCZowCecO+wRoIxJI2V6bXWK6/53ff+hEXLGlQL4Rp9ZaPDLrgV/32nxwWIP7QpWDkVEtllTAK5h6cnhxNxDA==
   dependencies:
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-precaching@6.5.4, workbox-precaching@^6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.5.4.tgz#740e3561df92c6726ab5f7471e6aac89582cab72"
-  integrity sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==
+workbox-precaching@6.6.1, workbox-precaching@^6.6.0:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.6.1.tgz#dedeeba10a2d163d990bf99f1c2066ac0d1a19e2"
+  integrity sha512-K4znSJ7IKxCnCYEdhNkMr7X1kNh8cz+mFgx9v5jFdz1MfI84pq8C2zG+oAoeE5kFrUf7YkT5x4uLWBNg0DVZ5A==
   dependencies:
-    workbox-core "6.5.4"
-    workbox-routing "6.5.4"
-    workbox-strategies "6.5.4"
+    workbox-core "6.6.1"
+    workbox-routing "6.6.1"
+    workbox-strategies "6.6.1"
 
-workbox-range-requests@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz#86b3d482e090433dab38d36ae031b2bb0bd74399"
-  integrity sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==
+workbox-range-requests@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.6.1.tgz#ddaf7e73af11d362fbb2f136a9063a4c7f507a39"
+  integrity sha512-4BDzk28govqzg2ZpX0IFkthdRmCKgAKreontYRC5YsAPB2jDtPNxqx3WtTXgHw1NZalXpcH/E4LqUa9+2xbv1g==
   dependencies:
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-recipes@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.5.4.tgz#cca809ee63b98b158b2702dcfb741b5cc3e24acb"
-  integrity sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==
+workbox-recipes@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.6.1.tgz#ea70d2b2b0b0bce8de0a9d94f274d4a688e69fae"
+  integrity sha512-/oy8vCSzromXokDA+X+VgpeZJvtuf8SkQ8KL0xmRivMgJZrjwM3c2tpKTJn6PZA6TsbxGs3Sc7KwMoZVamcV2g==
   dependencies:
-    workbox-cacheable-response "6.5.4"
-    workbox-core "6.5.4"
-    workbox-expiration "6.5.4"
-    workbox-precaching "6.5.4"
-    workbox-routing "6.5.4"
-    workbox-strategies "6.5.4"
+    workbox-cacheable-response "6.6.1"
+    workbox-core "6.6.1"
+    workbox-expiration "6.6.1"
+    workbox-precaching "6.6.1"
+    workbox-routing "6.6.1"
+    workbox-strategies "6.6.1"
 
-workbox-routing@6.5.4, workbox-routing@^6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.5.4.tgz#6a7fbbd23f4ac801038d9a0298bc907ee26fe3da"
-  integrity sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==
+workbox-routing@6.6.1, workbox-routing@^6.6.0:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.6.1.tgz#cba9a1c7e0d1ea11e24b6f8c518840efdc94f581"
+  integrity sha512-j4ohlQvfpVdoR8vDYxTY9rA9VvxTHogkIDwGdJ+rb2VRZQ5vt1CWwUUZBeD/WGFAni12jD1HlMXvJ8JS7aBWTg==
   dependencies:
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-strategies@6.5.4, workbox-strategies@^6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.5.4.tgz#4edda035b3c010fc7f6152918370699334cd204d"
-  integrity sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==
+workbox-strategies@6.6.1, workbox-strategies@^6.6.0:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.6.1.tgz#38d0f0fbdddba97bd92e0c6418d0b1a2ccd5b8bf"
+  integrity sha512-WQLXkRnsk4L81fVPkkgon1rZNxnpdO5LsO+ws7tYBC6QQQFJVI6v98klrJEjFtZwzw/mB/HT5yVp7CcX0O+mrw==
   dependencies:
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
-workbox-streams@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.5.4.tgz#1cb3c168a6101df7b5269d0353c19e36668d7d69"
-  integrity sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==
+workbox-streams@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.6.1.tgz#b2f7ba7b315c27a6e3a96a476593f99c5d227d26"
+  integrity sha512-maKG65FUq9e4BLotSKWSTzeF0sgctQdYyTMq529piEN24Dlu9b6WhrAfRpHdCncRS89Zi2QVpW5V33NX8PgH3Q==
   dependencies:
-    workbox-core "6.5.4"
-    workbox-routing "6.5.4"
+    workbox-core "6.6.1"
+    workbox-routing "6.6.1"
 
-workbox-sw@6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.5.4.tgz#d93e9c67924dd153a61367a4656ff4d2ae2ed736"
-  integrity sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==
+workbox-sw@6.6.1:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.6.1.tgz#d4c4ca3125088e8b9fd7a748ed537fa0247bd72c"
+  integrity sha512-R7whwjvU2abHH/lR6kQTTXLHDFU2izht9kJOvBRYK65FbwutT4VvnUAJIgHvfWZ/fokrOPhfoWYoPCMpSgUKHQ==
 
-workbox-webpack-plugin@^6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz#baf2d3f4b8f435f3469887cf4fba2b7fac3d0fd7"
-  integrity sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==
+workbox-webpack-plugin@^6.6.0:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.1.tgz#4f81cc1ad4e5d2cd7477a86ba83c84ee2d187531"
+  integrity sha512-zpZ+ExFj9NmiI66cFEApyjk7hGsfJ1YMOaLXGXBoZf0v7Iu6hL0ZBe+83mnDq3YYWAfA3fnyFejritjOHkFcrA==
   dependencies:
     fast-json-stable-stringify "^2.1.0"
     pretty-bytes "^5.4.1"
     upath "^1.2.0"
     webpack-sources "^1.4.3"
-    workbox-build "6.5.4"
+    workbox-build "6.6.1"
 
-workbox-window@6.5.4, workbox-window@^6.5.4:
-  version "6.5.4"
-  resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.5.4.tgz#d991bc0a94dff3c2dbb6b84558cff155ca878e91"
-  integrity sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==
+workbox-window@6.6.1, workbox-window@^6.6.0:
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.6.1.tgz#f22a394cbac36240d0dadcbdebc35f711bb7b89e"
+  integrity sha512-wil4nwOY58nTdCvif/KEZjQ2NP8uk3gGeRNy2jPBbzypU4BT4D9L8xiwbmDBpZlSgJd2xsT9FvSNU0gsxV51JQ==
   dependencies:
     "@types/trusted-types" "^2.0.2"
-    workbox-core "6.5.4"
+    workbox-core "6.6.1"
 
 wrap-ansi@^5.1.0:
   version "5.1.0"