diff --git a/.dockerignore b/.dockerignore
index 52397e75dc..fedbea236d 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -15,6 +15,7 @@ vendor/bundle
*.swp
*~
postgres
+postgres14
redis
elasticsearch
chart
diff --git a/.gitignore b/.gitignore
index b4d2712ffc..25c8388e16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,6 +40,7 @@
# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose
/postgres
+/postgres14
/redis
/elasticsearch
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index 1dd7430e09..948e70d5b5 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -2,13 +2,24 @@
module Admin
class AccountsController < BaseController
- before_action :set_account, except: [:index]
+ before_action :set_account, except: [:index, :batch]
before_action :require_remote_account!, only: [:redownload]
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
def index
authorize :account, :index?
+
@accounts = filtered_accounts.page(params[:page])
+ @form = Form::AccountBatch.new
+ end
+
+ def batch
+ @form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
+ @form.save
+ rescue ActionController::ParameterMissing
+ flash[:alert] = I18n.t('admin.accounts.no_account_selected')
+ ensure
+ redirect_to admin_accounts_path(filter_params)
end
def show
@@ -38,13 +49,13 @@ module Admin
def approve
authorize @account.user, :approve?
@account.user.approve!
- redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
+ redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
end
def reject
authorize @account.user, :reject?
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
- redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
+ redirect_to admin_accounts_path(status: 'pending'), notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
end
def destroy
@@ -121,11 +132,25 @@ module Admin
end
def filtered_accounts
- AccountFilter.new(filter_params).results
+ AccountFilter.new(filter_params.with_defaults(order: 'recent')).results
end
def filter_params
params.slice(*AccountFilter::KEYS).permit(*AccountFilter::KEYS)
end
+
+ def form_account_batch_params
+ params.require(:form_account_batch).permit(:action, account_ids: [])
+ end
+
+ def action_from_button
+ if params[:suspend]
+ 'suspend'
+ elsif params[:approve]
+ 'approve'
+ elsif params[:reject]
+ 'reject'
+ end
+ end
end
end
diff --git a/app/controllers/admin/pending_accounts_controller.rb b/app/controllers/admin/pending_accounts_controller.rb
deleted file mode 100644
index b62a9bc846..0000000000
--- a/app/controllers/admin/pending_accounts_controller.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# frozen_string_literal: true
-
-module Admin
- class PendingAccountsController < BaseController
- before_action :set_accounts, only: :index
-
- def index
- @form = Form::AccountBatch.new
- end
-
- def batch
- @form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
- @form.save
- rescue ActionController::ParameterMissing
- flash[:alert] = I18n.t('admin.accounts.no_account_selected')
- ensure
- redirect_to admin_pending_accounts_path(current_params)
- end
-
- def approve_all
- Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'approve').save
- redirect_to admin_pending_accounts_path(current_params)
- end
-
- def reject_all
- Form::AccountBatch.new(current_account: current_account, account_ids: User.pending.pluck(:account_id), action: 'reject').save
- redirect_to admin_pending_accounts_path(current_params)
- end
-
- private
-
- def set_accounts
- @accounts = Account.joins(:user).merge(User.pending.recent).includes(user: :invite_request).page(params[:page])
- end
-
- def form_account_batch_params
- params.require(:form_account_batch).permit(:action, account_ids: [])
- end
-
- def action_from_button
- if params[:approve]
- 'approve'
- elsif params[:reject]
- 'reject'
- end
- end
-
- def current_params
- params.slice(:page).permit(:page)
- end
- end
-end
diff --git a/app/controllers/concerns/accountable_concern.rb b/app/controllers/concerns/accountable_concern.rb
index 3cdcffc51c..87d62478da 100644
--- a/app/controllers/concerns/accountable_concern.rb
+++ b/app/controllers/concerns/accountable_concern.rb
@@ -3,7 +3,7 @@
module AccountableConcern
extend ActiveSupport::Concern
- def log_action(action, target)
- Admin::ActionLog.create(account: current_account, action: action, target: target)
+ def log_action(action, target, options = {})
+ Admin::ActionLog.create(account: current_account, action: action, target: target, recorded_changes: options.stringify_keys)
end
end
diff --git a/app/controllers/concerns/two_factor_authentication_concern.rb b/app/controllers/concerns/two_factor_authentication_concern.rb
index 39dd71fca4..c9477a1d42 100644
--- a/app/controllers/concerns/two_factor_authentication_concern.rb
+++ b/app/controllers/concerns/two_factor_authentication_concern.rb
@@ -57,7 +57,7 @@ module TwoFactorAuthenticationConcern
if valid_webauthn_credential?(user, webauthn_credential)
on_authentication_success(user, :webauthn)
- render json: { redirect_path: root_path }, status: :ok
+ render json: { redirect_path: after_sign_in_path_for(user) }, status: :ok
else
on_authentication_failure(user, :webauthn, :invalid_credential)
render json: { error: t('webauthn_credentials.invalid_credential') }, status: :unprocessable_entity
diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb
index e9a298a248..ae96f7a344 100644
--- a/app/helpers/admin/action_logs_helper.rb
+++ b/app/helpers/admin/action_logs_helper.rb
@@ -36,6 +36,8 @@ module Admin::ActionLogsHelper
def log_target_from_history(type, attributes)
case type
+ when 'User'
+ attributes['username']
when 'CustomEmoji'
attributes['shortcode']
when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain'
diff --git a/app/helpers/admin/dashboard_helper.rb b/app/helpers/admin/dashboard_helper.rb
index 4ee2cdef47..32aaf9f5e7 100644
--- a/app/helpers/admin/dashboard_helper.rb
+++ b/app/helpers/admin/dashboard_helper.rb
@@ -1,10 +1,41 @@
# frozen_string_literal: true
module Admin::DashboardHelper
- def feature_hint(feature, enabled)
- indicator = safe_join([enabled ? t('simple_form.yes') : t('simple_form.no'), fa_icon('power-off fw')], ' ')
- class_names = enabled ? 'pull-right positive-hint' : 'pull-right neutral-hint'
+ def relevant_account_ip(account, ip_query)
+ default_ip = [account.user_current_sign_in_ip || account.user_sign_up_ip]
- safe_join([feature, content_tag(:span, indicator, class: class_names)])
+ matched_ip = begin
+ ip_query_addr = IPAddr.new(ip_query)
+ account.user.recent_ips.find { |(_, ip)| ip_query_addr.include?(ip) } || default_ip
+ rescue IPAddr::Error
+ default_ip
+ end.last
+
+ if matched_ip
+ link_to matched_ip, admin_accounts_path(ip: matched_ip)
+ else
+ '-'
+ end
+ end
+
+ def relevant_account_timestamp(account)
+ timestamp, exact = begin
+ if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago
+ [account.user_current_sign_in_at, true]
+ elsif account.user_current_sign_in_at
+ [account.user_current_sign_in_at, false]
+ elsif account.user_pending?
+ [account.user_created_at, true]
+ elsif account.last_status_at.present?
+ [account.last_status_at, true]
+ else
+ [nil, false]
+ end
+ end
+
+ return '-' if timestamp.nil?
+ return t('generic.today') unless exact
+
+ content_tag(:time, l(timestamp), class: 'time-ago', datetime: timestamp.iso8601, title: l(timestamp))
end
end
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js
index 40d566d244..afd42bdefd 100644
--- a/app/javascript/mastodon/actions/compose.js
+++ b/app/javascript/mastodon/actions/compose.js
@@ -37,6 +37,7 @@ export const THUMBNAIL_UPLOAD_PROGRESS = 'THUMBNAIL_UPLOAD_PROGRESS';
export const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
export const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
+export const COMPOSE_SUGGESTION_IGNORE = 'COMPOSE_SUGGESTION_IGNORE';
export const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
export const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
@@ -536,13 +537,25 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
startPosition = position;
}
- dispatch({
- type: COMPOSE_SUGGESTION_SELECT,
- position: startPosition,
- token,
- completion,
- path,
- });
+ // We don't want to replace hashtags that vary only in case due to accessibility, but we need to fire off an event so that
+ // the suggestions are dismissed and the cursor moves forward.
+ if (suggestion.type !== 'hashtag' || token.slice(1).localeCompare(suggestion.name, undefined, { sensitivity: 'accent' }) !== 0) {
+ dispatch({
+ type: COMPOSE_SUGGESTION_SELECT,
+ position: startPosition,
+ token,
+ completion,
+ path,
+ });
+ } else {
+ dispatch({
+ type: COMPOSE_SUGGESTION_IGNORE,
+ position: startPosition,
+ token,
+ completion,
+ path,
+ });
+ }
};
};
diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js
index 34c7c4deaa..06a908e9d4 100644
--- a/app/javascript/mastodon/reducers/compose.js
+++ b/app/javascript/mastodon/reducers/compose.js
@@ -21,6 +21,7 @@ import {
COMPOSE_SUGGESTIONS_CLEAR,
COMPOSE_SUGGESTIONS_READY,
COMPOSE_SUGGESTION_SELECT,
+ COMPOSE_SUGGESTION_IGNORE,
COMPOSE_SUGGESTION_TAGS_UPDATE,
COMPOSE_TAG_HISTORY_UPDATE,
COMPOSE_SENSITIVITY_CHANGE,
@@ -165,6 +166,17 @@ const insertSuggestion = (state, position, token, completion, path) => {
});
};
+const ignoreSuggestion = (state, position, token, completion, path) => {
+ return state.withMutations(map => {
+ map.updateIn(path, oldText => `${oldText.slice(0, position + token.length)} ${oldText.slice(position + token.length)}`);
+ map.set('suggestion_token', null);
+ map.set('suggestions', ImmutableList());
+ map.set('focusDate', new Date());
+ map.set('caretPosition', position + token.length + 1);
+ map.set('idempotencyKey', uuid());
+ });
+};
+
const sortHashtagsByUse = (state, tags) => {
const personalHistory = state.get('tagHistory');
@@ -398,6 +410,8 @@ export default function compose(state = initialState, action) {
return state.set('suggestions', ImmutableList(normalizeSuggestions(state, action))).set('suggestion_token', action.token);
case COMPOSE_SUGGESTION_SELECT:
return insertSuggestion(state, action.position, action.token, action.completion, action.path);
+ case COMPOSE_SUGGESTION_IGNORE:
+ return ignoreSuggestion(state, action.position, action.token, action.completion, action.path);
case COMPOSE_SUGGESTION_TAGS_UPDATE:
return updateSuggestionTags(state, action.token);
case COMPOSE_TAG_HISTORY_UPDATE:
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 2166d8df0d..7ebe8b4d0c 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -103,7 +103,9 @@ function main() {
delegate(document, '#registration_user_password_confirmation,#registration_user_password', 'input', () => {
const password = document.getElementById('registration_user_password');
const confirmation = document.getElementById('registration_user_password_confirmation');
- if (password.value && password.value !== confirmation.value) {
+ if (confirmation.value && confirmation.value.length > password.maxLength) {
+ confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.exceeds_maxlength'] || 'Password confirmation exceeds the maximum password length', locale)).format());
+ } else if (password.value && password.value !== confirmation.value) {
confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.mismatching'] || 'Password confirmation does not match', locale)).format());
} else {
confirmation.setCustomValidity('');
@@ -115,7 +117,9 @@ function main() {
const confirmation = document.getElementById('user_password_confirmation');
if (!confirmation) return;
- if (password.value && password.value !== confirmation.value) {
+ if (confirmation.value && confirmation.value.length > password.maxLength) {
+ confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.exceeds_maxlength'] || 'Password confirmation exceeds the maximum password length', locale)).format());
+ } else if (password.value && password.value !== confirmation.value) {
confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.mismatching'] || 'Password confirmation does not match', locale)).format());
} else {
confirmation.setCustomValidity('');
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index b8a6c80188..485fe4a9dc 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -326,7 +326,12 @@
}
}
-.batch-table__row--muted .pending-account__header {
+.batch-table__row--muted {
+ color: lighten($ui-base-color, 26%);
+}
+
+.batch-table__row--muted .pending-account__header,
+.batch-table__row--muted .accounts-table {
&,
a,
strong {
@@ -334,10 +339,31 @@
}
}
-.batch-table__row--attention .pending-account__header {
+.batch-table__row--muted .accounts-table {
+ tbody td.accounts-table__extra,
+ &__count,
+ &__count small {
+ color: lighten($ui-base-color, 26%);
+ }
+}
+
+.batch-table__row--attention {
+ color: $gold-star;
+}
+
+.batch-table__row--attention .pending-account__header,
+.batch-table__row--attention .accounts-table {
&,
a,
strong {
color: $gold-star;
}
}
+
+.batch-table__row--attention .accounts-table {
+ tbody td.accounts-table__extra,
+ &__count,
+ &__count small {
+ color: $gold-star;
+ }
+}
diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss
index 62f5554ffc..36bc07a72b 100644
--- a/app/javascript/styles/mastodon/tables.scss
+++ b/app/javascript/styles/mastodon/tables.scss
@@ -237,6 +237,11 @@ a.table-action-link {
flex: 1 1 auto;
}
+ &__quote {
+ padding: 12px;
+ padding-top: 0;
+ }
+
&__extra {
flex: 0 0 auto;
text-align: right;
diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss
index 4e03868a68..43284eb482 100644
--- a/app/javascript/styles/mastodon/widgets.scss
+++ b/app/javascript/styles/mastodon/widgets.scss
@@ -443,6 +443,24 @@
}
}
+ tbody td.accounts-table__extra {
+ width: 120px;
+ text-align: right;
+ color: $darker-text-color;
+ padding-right: 16px;
+
+ a {
+ text-decoration: none;
+ color: inherit;
+
+ &:focus,
+ &:hover,
+ &:active {
+ text-decoration: underline;
+ }
+ }
+ }
+
&__comment {
width: 50%;
vertical-align: initial !important;
diff --git a/app/models/account.rb b/app/models/account.rb
index e0e916eb2d..5476272f9f 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -129,6 +129,8 @@ class Account < ApplicationRecord
:unconfirmed_email,
:current_sign_in_ip,
:current_sign_in_at,
+ :created_at,
+ :sign_up_ip,
:confirmed?,
:approved?,
:pending?,
diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb
index 2b001385f2..defd531acb 100644
--- a/app/models/account_filter.rb
+++ b/app/models/account_filter.rb
@@ -2,18 +2,15 @@
class AccountFilter
KEYS = %i(
- local
- remote
- by_domain
- active
- pending
- silenced
- suspended
+ origin
+ status
+ permissions
username
+ by_domain
display_name
email
ip
- staff
+ invited_by
order
).freeze
@@ -21,11 +18,10 @@ class AccountFilter
def initialize(params)
@params = params
- set_defaults!
end
def results
- scope = Account.includes(:user).reorder(nil)
+ scope = Account.includes(:account_stat, user: [:session_activations, :invite_request]).without_instance_actor.reorder(nil)
params.each do |key, value|
scope.merge!(scope_for(key, value.to_s.strip)) if value.present?
@@ -36,30 +32,16 @@ class AccountFilter
private
- def set_defaults!
- params['local'] = '1' if params['remote'].blank?
- params['active'] = '1' if params['suspended'].blank? && params['silenced'].blank? && params['pending'].blank?
- params['order'] = 'recent' if params['order'].blank?
- end
-
def scope_for(key, value)
case key.to_s
- when 'local'
- Account.local.without_instance_actor
- when 'remote'
- Account.remote
+ when 'origin'
+ origin_scope(value)
+ when 'permissions'
+ permissions_scope(value)
+ when 'status'
+ status_scope(value)
when 'by_domain'
Account.where(domain: value)
- when 'active'
- Account.without_suspended
- when 'pending'
- accounts_with_users.merge(User.pending)
- when 'disabled'
- accounts_with_users.merge(User.disabled)
- when 'silenced'
- Account.silenced
- when 'suspended'
- Account.suspended
when 'username'
Account.matches_username(value)
when 'display_name'
@@ -68,8 +50,8 @@ class AccountFilter
accounts_with_users.merge(User.matches_email(value))
when 'ip'
valid_ip?(value) ? accounts_with_users.merge(User.matches_ip(value)) : Account.none
- when 'staff'
- accounts_with_users.merge(User.staff)
+ when 'invited_by'
+ invited_by_scope(value)
when 'order'
order_scope(value)
else
@@ -77,21 +59,56 @@ class AccountFilter
end
end
- def order_scope(value)
- case value
+ def origin_scope(value)
+ case value.to_s
+ when 'local'
+ Account.local
+ when 'remote'
+ Account.remote
+ else
+ raise "Unknown origin: #{value}"
+ end
+ end
+
+ def status_scope(value)
+ case value.to_s
when 'active'
- params['remote'] ? Account.joins(:account_stat).by_recent_status : Account.joins(:user).by_recent_sign_in
+ Account.without_suspended
+ when 'pending'
+ accounts_with_users.merge(User.pending)
+ when 'suspended'
+ Account.suspended
+ else
+ raise "Unknown status: #{value}"
+ end
+ end
+
+ def order_scope(value)
+ case value.to_s
+ when 'active'
+ accounts_with_users.left_joins(:account_stat).order(Arel.sql('coalesce(users.current_sign_in_at, account_stats.last_status_at, to_timestamp(0)) desc, accounts.id desc'))
when 'recent'
Account.recent
- when 'alphabetic'
- Account.alphabetic
else
raise "Unknown order: #{value}"
end
end
+ def invited_by_scope(value)
+ Account.left_joins(user: :invite).merge(Invite.where(user_id: value.to_s))
+ end
+
+ def permissions_scope(value)
+ case value.to_s
+ when 'staff'
+ accounts_with_users.merge(User.staff)
+ else
+ raise "Unknown permissions: #{value}"
+ end
+ end
+
def accounts_with_users
- Account.joins(:user)
+ Account.left_joins(:user)
end
def valid_ip?(value)
diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb
index 1d1db1b7a9..852bff7134 100644
--- a/app/models/admin/action_log.rb
+++ b/app/models/admin/action_log.rb
@@ -17,7 +17,7 @@ class Admin::ActionLog < ApplicationRecord
serialize :recorded_changes
belongs_to :account
- belongs_to :target, polymorphic: true
+ belongs_to :target, polymorphic: true, optional: true
default_scope -> { order('id desc') }
diff --git a/app/models/admin/action_log_filter.rb b/app/models/admin/action_log_filter.rb
index 6e19dcf708..2af9d7c9c6 100644
--- a/app/models/admin/action_log_filter.rb
+++ b/app/models/admin/action_log_filter.rb
@@ -11,6 +11,8 @@ class Admin::ActionLogFilter
assigned_to_self_report: { target_type: 'Report', action: 'assigned_to_self' }.freeze,
change_email_user: { target_type: 'User', action: 'change_email' }.freeze,
confirm_user: { target_type: 'User', action: 'confirm' }.freeze,
+ approve_user: { target_type: 'User', action: 'approve' }.freeze,
+ reject_user: { target_type: 'User', action: 'reject' }.freeze,
create_account_warning: { target_type: 'AccountWarning', action: 'create' }.freeze,
create_announcement: { target_type: 'Announcement', action: 'create' }.freeze,
create_custom_emoji: { target_type: 'CustomEmoji', action: 'create' }.freeze,
diff --git a/app/models/form/account_batch.rb b/app/models/form/account_batch.rb
index f1e1c8a655..4bf1775bb9 100644
--- a/app/models/form/account_batch.rb
+++ b/app/models/form/account_batch.rb
@@ -3,6 +3,7 @@
class Form::AccountBatch
include ActiveModel::Model
include Authorization
+ include AccountableConcern
include Payloadable
attr_accessor :account_ids, :action, :current_account
@@ -25,19 +26,21 @@ class Form::AccountBatch
suppress_follow_recommendation!
when 'unsuppress_follow_recommendation'
unsuppress_follow_recommendation!
+ when 'suspend'
+ suspend!
end
end
private
def follow!
- accounts.find_each do |target_account|
+ accounts.each do |target_account|
FollowService.new.call(current_account, target_account)
end
end
def unfollow!
- accounts.find_each do |target_account|
+ accounts.each do |target_account|
UnfollowService.new.call(current_account, target_account)
end
end
@@ -61,23 +64,31 @@ class Form::AccountBatch
end
def approve!
- users = accounts.includes(:user).map(&:user)
-
- users.each { |user| authorize(user, :approve?) }
- .each(&:approve!)
+ accounts.includes(:user).find_each do |account|
+ approve_account(account)
+ end
end
def reject!
- records = accounts.includes(:user)
+ accounts.includes(:user).find_each do |account|
+ reject_account(account)
+ end
+ end
- records.each { |account| authorize(account.user, :reject?) }
- .each { |account| DeleteAccountService.new.call(account, reserve_email: false, reserve_username: false) }
+ def suspend!
+ accounts.find_each do |account|
+ if account.user_pending?
+ reject_account(account)
+ else
+ suspend_account(account)
+ end
+ end
end
def suppress_follow_recommendation!
authorize(:follow_recommendation, :suppress?)
- accounts.each do |account|
+ accounts.find_each do |account|
FollowRecommendationSuppression.create(account: account)
end
end
@@ -87,4 +98,24 @@ class Form::AccountBatch
FollowRecommendationSuppression.where(account_id: account_ids).destroy_all
end
+
+ def reject_account(account)
+ authorize(account.user, :reject?)
+ log_action(:reject, account.user, username: account.username)
+ account.suspend!(origin: :local)
+ AccountDeletionWorker.perform_async(account.id, reserve_username: false)
+ end
+
+ def suspend_account(account)
+ authorize(account, :suspend?)
+ log_action(:suspend, account)
+ account.suspend!(origin: :local)
+ Admin::SuspensionWorker.perform_async(account.id)
+ end
+
+ def approve_account(account)
+ authorize(account.user, :approve?)
+ log_action(:approve, account.user)
+ account.user.approve!
+ end
end
diff --git a/app/models/trends/tags.rb b/app/models/trends/tags.rb
index 13e0ab56b4..a425fd2072 100644
--- a/app/models/trends/tags.rb
+++ b/app/models/trends/tags.rb
@@ -4,7 +4,7 @@ class Trends::Tags < Trends::Base
PREFIX = 'trending_tags'
self.default_options = {
- threshold: 15,
+ threshold: 5,
review_threshold: 10,
max_score_cooldown: 2.days.freeze,
max_score_halflife: 4.hours.freeze,
diff --git a/app/views/admin/accounts/_account.html.haml b/app/views/admin/accounts/_account.html.haml
index c9bd8c686c..2df91301ed 100644
--- a/app/views/admin/accounts/_account.html.haml
+++ b/app/views/admin/accounts/_account.html.haml
@@ -1,24 +1,35 @@
-%tr
- %td
- = admin_account_link_to(account)
- %td
- %div.account-badges= account_badge(account, all: true)
- %td
- - if account.user_current_sign_in_ip
- %samp.ellipsized-ip{ title: account.user_current_sign_in_ip }= account.user_current_sign_in_ip
- - else
- \-
- %td
- - if account.user_current_sign_in_at
- %time.time-ago{ datetime: account.user_current_sign_in_at.iso8601, title: l(account.user_current_sign_in_at) }= l account.user_current_sign_in_at
- - elsif account.last_status_at.present?
- %time.time-ago{ datetime: account.last_status_at.iso8601, title: l(account.last_status_at) }= l account.last_status_at
- - else
- \-
- %td
- - if account.local? && account.user_pending?
- = table_link_to 'check', t('admin.accounts.approve'), approve_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:approve, account.user)
- = table_link_to 'times', t('admin.accounts.reject'), reject_admin_account_path(account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:reject, account.user)
- - else
- = table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}")
- = table_link_to 'globe', t('admin.accounts.public'), ActivityPub::TagManager.instance.url_for(account)
+.batch-table__row{ class: [!account.suspended? && account.user_pending? && 'batch-table__row--attention', account.suspended? && 'batch-table__row--muted'] }
+ %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
+ = f.check_box :account_ids, { multiple: true, include_hidden: false }, account.id
+ .batch-table__row__content.batch-table__row__content--unpadded
+ %table.accounts-table
+ %tbody
+ %tr
+ %td
+ = account_link_to account, path: admin_account_path(account.id)
+ %td.accounts-table__count.optional
+ - if account.suspended? || account.user_pending?
+ \-
+ - else
+ = friendly_number_to_human account.statuses_count
+ %small= t('accounts.posts', count: account.statuses_count).downcase
+ %td.accounts-table__count.optional
+ - if account.suspended? || account.user_pending?
+ \-
+ - else
+ = friendly_number_to_human account.followers_count
+ %small= t('accounts.followers', count: account.followers_count).downcase
+ %td.accounts-table__count
+ = relevant_account_timestamp(account)
+ %small= t('accounts.last_active')
+ %td.accounts-table__extra
+ - if account.local?
+ - if account.user_email
+ = link_to account.user_email.split('@').last, admin_accounts_path(email: "%@#{account.user_email.split('@').last}"), title: account.user_email
+ - else
+ \-
+ %br/
+ %samp.ellipsized-ip= relevant_account_ip(account, params[:ip])
+ - if !account.suspended? && account.user_pending? && account.user&.invite_request&.text&.present?
+ .batch-table__row__content__quote
+ %p= account.user&.invite_request&.text
diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index 398ab4bb46..fc667b376c 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -5,30 +5,30 @@
.filter-subset
%strong= t('admin.accounts.location.title')
%ul
- %li= filter_link_to t('admin.accounts.location.local'), remote: nil
- %li= filter_link_to t('admin.accounts.location.remote'), remote: '1'
+ %li= filter_link_to t('generic.all'), origin: nil
+ %li= filter_link_to t('admin.accounts.location.local'), origin: 'local'
+ %li= filter_link_to t('admin.accounts.location.remote'), origin: 'remote'
.filter-subset
%strong= t('admin.accounts.moderation.title')
%ul
- %li= link_to safe_join([t('admin.accounts.moderation.pending'), "(#{number_with_delimiter(User.pending.count)})"], ' '), admin_pending_accounts_path
- %li= filter_link_to t('admin.accounts.moderation.active'), silenced: nil, suspended: nil, pending: nil
- %li= filter_link_to t('admin.accounts.moderation.silenced'), silenced: '1', suspended: nil, pending: nil
- %li= filter_link_to t('admin.accounts.moderation.suspended'), suspended: '1', silenced: nil, pending: nil
+ %li= filter_link_to t('generic.all'), status: nil
+ %li= filter_link_to t('admin.accounts.moderation.active'), status: 'active'
+ %li= filter_link_to t('admin.accounts.moderation.suspended'), status: 'suspended'
+ %li= filter_link_to safe_join([t('admin.accounts.moderation.pending'), "(#{number_with_delimiter(User.pending.count)})"], ' '), status: 'pending'
.filter-subset
%strong= t('admin.accounts.role')
%ul
- %li= filter_link_to t('admin.accounts.moderation.all'), staff: nil
- %li= filter_link_to t('admin.accounts.roles.staff'), staff: '1'
+ %li= filter_link_to t('admin.accounts.moderation.all'), permissions: nil
+ %li= filter_link_to t('admin.accounts.roles.staff'), permissions: 'staff'
.filter-subset
%strong= t 'generic.order_by'
%ul
%li= filter_link_to t('relationships.most_recent'), order: nil
- %li= filter_link_to t('admin.accounts.username'), order: 'alphabetic'
%li= filter_link_to t('relationships.last_active'), order: 'active'
= form_tag admin_accounts_url, method: 'GET', class: 'simple_form' do
.fields-group
- - AccountFilter::KEYS.each do |key|
+ - (AccountFilter::KEYS - %i(origin status permissions)).each do |key|
- if params[key].present?
= hidden_field_tag key, params[key]
@@ -41,16 +41,27 @@
%button.button= t('admin.accounts.search')
= link_to t('admin.accounts.reset'), admin_accounts_path, class: 'button negative'
-.table-wrapper
- %table.table
- %thead
- %tr
- %th= t('admin.accounts.username')
- %th= t('admin.accounts.role')
- %th= t('admin.accounts.most_recent_ip')
- %th= t('admin.accounts.most_recent_activity')
- %th
- %tbody
- = render partial: 'account', collection: @accounts
+= form_for(@form, url: batch_admin_accounts_path) do |f|
+ = hidden_field_tag :page, params[:page] || 1
+
+ - AccountFilter::KEYS.each do |key|
+ = hidden_field_tag key, params[key] if params[key].present?
+
+ .batch-table
+ .batch-table__toolbar
+ %label.batch-table__toolbar__select.batch-checkbox-all
+ = check_box_tag :batch_checkbox_all, nil, false
+ .batch-table__toolbar__actions
+ - if @accounts.any? { |account| account.user_pending? }
+ = f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ = f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ = f.button safe_join([fa_icon('lock'), t('admin.accounts.perform_full_suspension')]), name: :suspend, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+ .batch-table__body
+ - if @accounts.empty?
+ = nothing_here 'nothing-here--under-tabs'
+ - else
+ = render partial: 'account', collection: @accounts, locals: { f: f }
= paginate @accounts
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index b27676e4c4..2ee13b9e29 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -35,7 +35,7 @@
%span= t('admin.dashboard.pending_reports_html', count: @pending_reports_count)
= fa_icon 'chevron-right fw'
- = link_to admin_pending_accounts_path, class: 'dashboard__quick-access' do
+ = link_to admin_accounts_path(status: 'pending'), class: 'dashboard__quick-access' do
%span= t('admin.dashboard.pending_users_html', count: @pending_users_count)
= fa_icon 'chevron-right fw'
diff --git a/app/views/admin/instances/show.html.haml b/app/views/admin/instances/show.html.haml
index 4625293389..d6542ac3e2 100644
--- a/app/views/admin/instances/show.html.haml
+++ b/app/views/admin/instances/show.html.haml
@@ -15,7 +15,7 @@
.dashboard__counters
%div
- = link_to admin_accounts_path(remote: '1', by_domain: @instance.domain) do
+ = link_to admin_accounts_path(origin: 'remote', by_domain: @instance.domain) do
.dashboard__counters__num= number_with_delimiter @instance.accounts_count
.dashboard__counters__label= t 'admin.accounts.title'
%div
diff --git a/app/views/admin/ip_blocks/_ip_block.html.haml b/app/views/admin/ip_blocks/_ip_block.html.haml
index e07e2b4448..b8d3ac0e86 100644
--- a/app/views/admin/ip_blocks/_ip_block.html.haml
+++ b/app/views/admin/ip_blocks/_ip_block.html.haml
@@ -1,9 +1,9 @@
.batch-table__row
%label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
= f.check_box :ip_block_ids, { multiple: true, include_hidden: false }, ip_block.id
- .batch-table__row__content
- .batch-table__row__content__text
- %samp= "#{ip_block.ip}/#{ip_block.ip.prefix}"
+ .batch-table__row__content.pending-account
+ .pending-account__header
+ %samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}")
- if ip_block.comment.present?
•
= ip_block.comment
diff --git a/app/views/admin/pending_accounts/_account.html.haml b/app/views/admin/pending_accounts/_account.html.haml
deleted file mode 100644
index 5b475b59a9..0000000000
--- a/app/views/admin/pending_accounts/_account.html.haml
+++ /dev/null
@@ -1,16 +0,0 @@
-.batch-table__row
- %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
- = f.check_box :account_ids, { multiple: true, include_hidden: false }, account.id
- .batch-table__row__content.pending-account
- .pending-account__header
- = link_to admin_account_path(account.id) do
- %strong= account.user_email
- = "(@#{account.username})"
- %br/
- %samp= account.user_current_sign_in_ip
- •
- = t 'admin.accounts.time_in_queue', time: time_ago_in_words(account.user&.created_at)
-
- - if account.user&.invite_request&.text&.present?
- .pending-account__body
- %p= account.user&.invite_request&.text
diff --git a/app/views/admin/pending_accounts/index.html.haml b/app/views/admin/pending_accounts/index.html.haml
deleted file mode 100644
index 8101d7f99a..0000000000
--- a/app/views/admin/pending_accounts/index.html.haml
+++ /dev/null
@@ -1,30 +0,0 @@
-- content_for :page_title do
- = t('admin.pending_accounts.title', count: User.pending.count)
-
-= form_for(@form, url: batch_admin_pending_accounts_path) do |f|
- = hidden_field_tag :page, params[:page] || 1
-
- .batch-table
- .batch-table__toolbar
- %label.batch-table__toolbar__select.batch-checkbox-all
- = check_box_tag :batch_checkbox_all, nil, false
- .batch-table__toolbar__actions
- = f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
-
- = f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
- .batch-table__body
- - if @accounts.empty?
- = nothing_here 'nothing-here--under-tabs'
- - else
- = render partial: 'account', collection: @accounts, locals: { f: f }
-
-= paginate @accounts
-
-%hr.spacer/
-
-%div.action-buttons
- %div
- = link_to t('admin.accounts.approve_all'), approve_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button'
-
- %div
- = link_to t('admin.accounts.reject_all'), reject_all_admin_pending_accounts_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive'
diff --git a/app/views/admin_mailer/new_pending_account.text.erb b/app/views/admin_mailer/new_pending_account.text.erb
index a466ee2de7..bcc2518190 100644
--- a/app/views/admin_mailer/new_pending_account.text.erb
+++ b/app/views/admin_mailer/new_pending_account.text.erb
@@ -9,4 +9,4 @@
<%= quote_wrap(@account.user&.invite_request&.text) %>
<% end %>
-<%= raw t('application_mailer.view')%> <%= admin_pending_accounts_url %>
+<%= raw t('application_mailer.view')%> <%= admin_accounts_url(status: 'pending') %>
diff --git a/app/workers/scheduler/follow_recommendations_scheduler.rb b/app/workers/scheduler/follow_recommendations_scheduler.rb
index cb1e159617..effc63e598 100644
--- a/app/workers/scheduler/follow_recommendations_scheduler.rb
+++ b/app/workers/scheduler/follow_recommendations_scheduler.rb
@@ -16,12 +16,12 @@ class Scheduler::FollowRecommendationsScheduler
AccountSummary.refresh
FollowRecommendation.refresh
- fallback_recommendations = FollowRecommendation.limit(SET_SIZE).index_by(&:account_id)
+ fallback_recommendations = FollowRecommendation.order(rank: :desc).limit(SET_SIZE).index_by(&:account_id)
I18n.available_locales.each do |locale|
recommendations = begin
if AccountSummary.safe.filtered.localized(locale).exists? # We can skip the work if no accounts with that language exist
- FollowRecommendation.localized(locale).limit(SET_SIZE).index_by(&:account_id)
+ FollowRecommendation.localized(locale).order(rank: :desc).limit(SET_SIZE).index_by(&:account_id)
else
{}
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 1aa96ba0f7..e9a0aea548 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -99,7 +99,6 @@ en:
accounts:
add_email_domain_block: Block e-mail domain
approve: Approve
- approve_all: Approve all
approved_msg: Successfully approved %{username}'s sign-up application
are_you_sure: Are you sure?
avatar: Avatar
@@ -153,7 +152,6 @@ en:
active: Active
all: All
pending: Pending
- silenced: Limited
suspended: Suspended
title: Moderation
moderation_notes: Moderation notes
@@ -171,7 +169,6 @@ en:
redownload: Refresh profile
redownloaded_msg: Successfully refreshed %{username}'s profile from origin
reject: Reject
- reject_all: Reject all
rejected_msg: Successfully rejected %{username}'s sign-up application
remove_avatar: Remove avatar
remove_header: Remove header
@@ -210,7 +207,6 @@ en:
suspended: Suspended
suspension_irreversible: The data of this account has been irreversibly deleted. You can unsuspend the account to make it usable but it will not recover any data it previously had.
suspension_reversible_hint_html: The account has been suspended, and the data will be fully removed on %{date}. Until then, the account can be restored without any ill effects. If you wish to remove all of the account's data immediately, you can do so below.
- time_in_queue: Waiting in queue %{time}
title: Accounts
unconfirmed_email: Unconfirmed email
undo_sensitized: Undo force-sensitive
@@ -226,6 +222,7 @@ en:
whitelisted: Allowed for federation
action_logs:
action_types:
+ approve_user: Approve User
assigned_to_self_report: Assign Report
change_email_user: Change E-mail for User
confirm_user: Confirm User
@@ -255,6 +252,7 @@ en:
enable_user: Enable User
memorialize_account: Memorialize Account
promote_user: Promote User
+ reject_user: Reject User
remove_avatar_user: Remove Avatar
reopen_report: Reopen Report
reset_password_user: Reset Password
@@ -271,6 +269,7 @@ en:
update_domain_block: Update Domain Block
update_status: Update Post
actions:
+ approve_user_html: "%{name} approved sign-up from %{target}"
assigned_to_self_report_html: "%{name} assigned report %{target} to themselves"
change_email_user_html: "%{name} changed the e-mail address of user %{target}"
confirm_user_html: "%{name} confirmed e-mail address of user %{target}"
@@ -300,6 +299,7 @@ en:
enable_user_html: "%{name} enabled login for user %{target}"
memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page"
promote_user_html: "%{name} promoted user %{target}"
+ reject_user_html: "%{name} rejected sign-up from %{target}"
remove_avatar_user_html: "%{name} removed %{target}'s avatar"
reopen_report_html: "%{name} reopened report %{target}"
reset_password_user_html: "%{name} reset password of user %{target}"
@@ -377,13 +377,13 @@ en:
new_users: new users
opened_reports: reports opened
pending_reports_html:
- one: "1 pending reports"
+ one: "1 pending report"
other: "%{count} pending reports"
pending_tags_html:
- one: "1 pending hashtags"
+ one: "1 pending hashtag"
other: "%{count} pending hashtags"
pending_users_html:
- one: "1 pending users"
+ one: "1 pending user"
other: "%{count} pending users"
resolved_reports: reports resolved
software: Software
@@ -519,8 +519,6 @@ en:
title: Create new IP rule
no_ip_block_selected: No IP rules were changed as none were selected
title: IP rules
- pending_accounts:
- title: Pending accounts (%{count})
relationships:
title: "%{acct}'s relationships"
relays:
@@ -980,6 +978,7 @@ en:
none: None
order_by: Order by
save_changes: Save changes
+ today: today
validation_errors:
one: Something isn't quite right yet! Please review the error below
other: Something isn't quite right yet! Please review %{count} errors below
diff --git a/config/navigation.rb b/config/navigation.rb
index 53ee3d6c12..a5590d2ea6 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -47,7 +47,7 @@ SimpleNavigation::Configuration.run do |navigation|
n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |s|
s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url
s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports}
- s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts}
+ s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url(origin: 'local'), highlights_on: %r{/admin/accounts|/admin/pending_accounts}
s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path
s.item :follow_recommendations, safe_join([fa_icon('user-plus fw'), t('admin.follow_recommendations.title')]), admin_follow_recommendations_path, highlights_on: %r{/admin/follow_recommendations}
s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: whitelist_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.admin? }
diff --git a/config/routes.rb b/config/routes.rb
index 19d87e6d5c..8b1da422f6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -253,6 +253,10 @@ Rails.application.routes.draw do
post :reject
end
+ collection do
+ post :batch
+ end
+
resource :change_email, only: [:show, :update]
resource :reset, only: [:create]
resource :action, only: [:new, :create], controller: 'account_actions'
@@ -273,14 +277,6 @@ Rails.application.routes.draw do
end
end
- resources :pending_accounts, only: [:index] do
- collection do
- post :approve_all
- post :reject_all
- post :batch
- end
- end
-
resources :users, only: [] do
resource :two_factor_authentication, only: [:destroy]
resource :sign_in_token_authentication, only: [:create, :destroy]
diff --git a/crowdin.yml b/crowdin.yml
index 88a24d6211..6d84ab0a10 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -2,19 +2,13 @@ commit_message: '[ci skip]'
files:
- source: /app/javascript/mastodon/locales/en.json
translation: /app/javascript/mastodon/locales/%two_letters_code%.json
- update_option: update_as_unapproved
- source: /config/locales/en.yml
translation: /config/locales/%two_letters_code%.yml
- update_option: update_as_unapproved
- source: /config/locales/simple_form.en.yml
translation: /config/locales/simple_form.%two_letters_code%.yml
- update_option: update_as_unapproved
- source: /config/locales/activerecord.en.yml
translation: /config/locales/activerecord.%two_letters_code%.yml
- update_option: update_as_unapproved
- source: /config/locales/devise.en.yml
translation: /config/locales/devise.%two_letters_code%.yml
- update_option: update_as_unapproved
- source: /config/locales/doorkeeper.en.yml
translation: /config/locales/doorkeeper.%two_letters_code%.yml
- update_option: update_as_unapproved
diff --git a/db/migrate/20211213040746_update_account_summaries_to_version_2.rb b/db/migrate/20211213040746_update_account_summaries_to_version_2.rb
new file mode 100644
index 0000000000..0d1f092ec6
--- /dev/null
+++ b/db/migrate/20211213040746_update_account_summaries_to_version_2.rb
@@ -0,0 +1,24 @@
+class UpdateAccountSummariesToVersion2 < ActiveRecord::Migration[6.1]
+ def up
+ reapplication_follow_recommendations_v2 do
+ drop_view :account_summaries, materialized: true
+ create_view :account_summaries, version: 2, materialized: { no_data: true }
+ safety_assured { add_index :account_summaries, :account_id, unique: true }
+ end
+ end
+
+ def down
+ reapplication_follow_recommendations_v2 do
+ drop_view :account_summaries, materialized: true
+ create_view :account_summaries, version: 1, materialized: { no_data: true }
+ safety_assured { add_index :account_summaries, :account_id, unique: true }
+ end
+ end
+
+ def reapplication_follow_recommendations_v2
+ drop_view :follow_recommendations, materialized: true
+ yield
+ create_view :follow_recommendations, version: 2, materialized: { no_data: true }
+ safety_assured { add_index :follow_recommendations, :account_id, unique: true }
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index edadeba1f3..d3337ac046 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_11_26_000907) do
+ActiveRecord::Schema.define(version: 2021_12_13_040746) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1131,7 +1131,7 @@ ActiveRecord::Schema.define(version: 2021_11_26_000907) do
statuses.language,
statuses.sensitive
FROM statuses
- WHERE ((statuses.account_id = accounts.id) AND (statuses.deleted_at IS NULL))
+ WHERE ((statuses.account_id = accounts.id) AND (statuses.deleted_at IS NULL) AND (statuses.reblog_of_id IS NULL))
ORDER BY statuses.id DESC
LIMIT 20) t0)
WHERE ((accounts.suspended_at IS NULL) AND (accounts.silenced_at IS NULL) AND (accounts.moved_to_account_id IS NULL) AND (accounts.discoverable = true) AND (accounts.locked = false))
diff --git a/db/views/account_summaries_v02.sql b/db/views/account_summaries_v02.sql
new file mode 100644
index 0000000000..17f5605f8f
--- /dev/null
+++ b/db/views/account_summaries_v02.sql
@@ -0,0 +1,23 @@
+SELECT
+ accounts.id AS account_id,
+ mode() WITHIN GROUP (ORDER BY language ASC) AS language,
+ mode() WITHIN GROUP (ORDER BY sensitive ASC) AS sensitive
+FROM accounts
+CROSS JOIN LATERAL (
+ SELECT
+ statuses.account_id,
+ statuses.language,
+ statuses.sensitive
+ FROM statuses
+ WHERE statuses.account_id = accounts.id
+ AND statuses.deleted_at IS NULL
+ AND statuses.reblog_of_id IS NULL
+ ORDER BY statuses.id DESC
+ LIMIT 20
+) t0
+WHERE accounts.suspended_at IS NULL
+ AND accounts.silenced_at IS NULL
+ AND accounts.moved_to_account_id IS NULL
+ AND accounts.discoverable = 't'
+ AND accounts.locked = 'f'
+GROUP BY accounts.id
diff --git a/lib/mastodon/statuses_cli.rb b/lib/mastodon/statuses_cli.rb
index f841529e05..e273e2614b 100644
--- a/lib/mastodon/statuses_cli.rb
+++ b/lib/mastodon/statuses_cli.rb
@@ -14,16 +14,21 @@ module Mastodon
end
option :days, type: :numeric, default: 90
- option :clean_followed, type: :boolean
- option :skip_media_remove, type: :boolean
- option :vacuum, type: :boolean, default: false, desc: 'Reduce the file size and update the statistics. This option locks the table for a long time, so run it offline'
option :batch_size, type: :numeric, default: 1_000, aliases: [:b], desc: 'Number of records in each batch'
+ option :continue, type: :boolean, default: false, desc: 'If remove is not completed, execute from the previous continuation'
+ option :clean_followed, type: :boolean, default: false, desc: 'Include the status of remote accounts that are followed by local accounts as candidates for remove'
+ option :skip_status_remove, type: :boolean, default: false, desc: 'Skip status remove (run only cleanup tasks)'
+ option :skip_media_remove, type: :boolean, default: false, desc: 'Skip remove orphaned media attachments'
+ option :compress_database, type: :boolean, default: false, desc: 'Compress database and update the statistics. This option locks the table for a long time, so run it offline'
desc 'remove', 'Remove unreferenced statuses'
long_desc <<~LONG_DESC
Remove statuses that are not referenced by local user activity, such as
ones that came from relays, or belonging to users that were once followed
by someone locally but no longer are.
+ It also removes orphaned records and performs additional cleanup tasks
+ such as updating statistics and recovering disk space.
+
This is a computationally heavy procedure that creates extra database
indices before commencing, and removes them afterward.
LONG_DESC
@@ -33,41 +38,56 @@ module Mastodon
exit(1)
end
+ remove_statuses
+ vacuum_and_analyze_statuses
+ remove_orphans_media_attachments
+ remove_orphans_conversations
+ vacuum_and_analyze_conversations
+ end
+
+ private
+
+ def remove_statuses
+ return if options[:skip_status_remove]
+
say('Creating temporary database indices...')
- ActiveRecord::Base.connection.add_index(:accounts, :id, name: :index_accounts_local, where: 'domain is null', algorithm: :concurrently, if_not_exists: true)
- ActiveRecord::Base.connection.add_index(:status_pins, :status_id, name: :index_status_pins_status_id, algorithm: :concurrently, if_not_exists: true)
ActiveRecord::Base.connection.add_index(:media_attachments, :remote_url, name: :index_media_attachments_remote_url, where: 'remote_url is not null', algorithm: :concurrently, if_not_exists: true)
max_id = Mastodon::Snowflake.id_at(options[:days].days.ago)
start_at = Time.now.to_f
- say('Extract the deletion target... This might take a while...')
+ unless options[:continue] && ActiveRecord::Base.connection.table_exists?('statuses_to_be_deleted')
+ ActiveRecord::Base.connection.add_index(:accounts, :id, name: :index_accounts_local, where: 'domain is null', algorithm: :concurrently, if_not_exists: true)
+ ActiveRecord::Base.connection.add_index(:status_pins, :status_id, name: :index_status_pins_status_id, algorithm: :concurrently, if_not_exists: true)
- ActiveRecord::Base.connection.create_table('statuses_to_be_deleted', temporary: true)
+ say('Extract the deletion target from statuses... This might take a while...')
- # Skip accounts followed by local accounts
- clean_followed_sql = 'AND NOT EXISTS (SELECT 1 FROM follows WHERE statuses.account_id = follows.target_account_id)' unless options[:clean_followed]
+ ActiveRecord::Base.connection.create_table('statuses_to_be_deleted', force: true)
- ActiveRecord::Base.connection.exec_insert(<<-SQL.squish, 'SQL', [[nil, max_id]])
- INSERT INTO statuses_to_be_deleted (id)
- SELECT statuses.id FROM statuses WHERE deleted_at IS NULL AND NOT local AND uri IS NOT NULL AND (id < $1)
- AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses.id = statuses1.in_reply_to_id)
- AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses1.id = statuses.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local))
- AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses.id = statuses1.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local OR statuses1.id >= $1))
- AND NOT EXISTS (SELECT 1 FROM status_pins WHERE statuses.id = status_id)
- AND NOT EXISTS (SELECT 1 FROM mentions WHERE statuses.id = mentions.status_id AND mentions.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))
- AND NOT EXISTS (SELECT 1 FROM favourites WHERE statuses.id = favourites.status_id AND favourites.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))
- AND NOT EXISTS (SELECT 1 FROM bookmarks WHERE statuses.id = bookmarks.status_id AND bookmarks.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))
- #{clean_followed_sql}
- SQL
+ # Skip accounts followed by local accounts
+ clean_followed_sql = 'AND NOT EXISTS (SELECT 1 FROM follows WHERE statuses.account_id = follows.target_account_id)' unless options[:clean_followed]
- say('Removing temporary database indices to restore write performance...')
+ ActiveRecord::Base.connection.exec_insert(<<-SQL.squish, 'SQL', [[nil, max_id]])
+ INSERT INTO statuses_to_be_deleted (id)
+ SELECT statuses.id FROM statuses WHERE deleted_at IS NULL AND NOT local AND uri IS NOT NULL AND (id < $1)
+ AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses.id = statuses1.in_reply_to_id)
+ AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses1.id = statuses.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local))
+ AND NOT EXISTS (SELECT 1 FROM statuses AS statuses1 WHERE statuses.id = statuses1.reblog_of_id AND (statuses1.uri IS NULL OR statuses1.local OR statuses1.id >= $1))
+ AND NOT EXISTS (SELECT 1 FROM status_pins WHERE statuses.id = status_id)
+ AND NOT EXISTS (SELECT 1 FROM mentions WHERE statuses.id = mentions.status_id AND mentions.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))
+ AND NOT EXISTS (SELECT 1 FROM favourites WHERE statuses.id = favourites.status_id AND favourites.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))
+ AND NOT EXISTS (SELECT 1 FROM bookmarks WHERE statuses.id = bookmarks.status_id AND bookmarks.account_id IN (SELECT accounts.id FROM accounts WHERE domain IS NULL))
+ #{clean_followed_sql}
+ SQL
- ActiveRecord::Base.connection.remove_index(:accounts, name: :index_accounts_local, if_exists: true)
- ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id, if_exists: true)
+ say('Removing temporary database indices to restore write performance...')
- say('Beginning removal... This might take a while...')
+ ActiveRecord::Base.connection.remove_index(:accounts, name: :index_accounts_local, if_exists: true)
+ ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id, if_exists: true)
+ end
+
+ say('Beginning statuses removal... This might take a while...')
klass = Class.new(ApplicationRecord) do |c|
c.table_name = 'statuses_to_be_deleted'
@@ -89,20 +109,7 @@ module Mastodon
progress.stop
- if options[:vacuum]
- say('Run VACUUM and ANALYZE to statuses...')
-
- ActiveRecord::Base.connection.execute('VACUUM FULL ANALYZE statuses')
- else
- say('Run ANALYZE to statuses...')
-
- ActiveRecord::Base.connection.execute('ANALYZE statuses')
- end
-
- unless options[:skip_media_remove]
- say('Beginning removal of now-orphaned media attachments to free up disk space...')
- Scheduler::MediaCleanupScheduler.new.perform
- end
+ ActiveRecord::Base.connection.drop_table('statuses_to_be_deleted')
say("Done after #{Time.now.to_f - start_at}s, removed #{removed} out of #{processed} statuses.", :green)
ensure
@@ -112,5 +119,108 @@ module Mastodon
ActiveRecord::Base.connection.remove_index(:status_pins, name: :index_status_pins_status_id, if_exists: true)
ActiveRecord::Base.connection.remove_index(:media_attachments, name: :index_media_attachments_remote_url, if_exists: true)
end
+
+ def remove_orphans_media_attachments
+ return if options[:skip_media_remove]
+
+ start_at = Time.now.to_f
+
+ say('Beginning removal of now-orphaned media attachments to free up disk space...')
+
+ scope = MediaAttachment.reorder(nil).unattached.where('created_at < ?', options[:days].pred.days.ago)
+ processed = 0
+ removed = 0
+ progress = create_progress_bar(scope.count)
+
+ scope.find_each do |media_attachment|
+ media_attachment.destroy!
+
+ removed += 1
+ rescue => e
+ progress.log pastel.red("Error processing #{media_attachment.id}: #{e}")
+ ensure
+ progress.increment
+ processed += 1
+ end
+
+ progress.stop
+
+ say("Done after #{Time.now.to_f - start_at}s, removed #{removed} out of #{processed} media_attachments.", :green)
+ end
+
+ def remove_orphans_conversations
+ start_at = Time.now.to_f
+
+ unless options[:continue] && ActiveRecord::Base.connection.table_exists?('conversations_to_be_deleted')
+ say('Creating temporary database indices...')
+
+ ActiveRecord::Base.connection.add_index(:statuses, :conversation_id, name: :index_statuses_conversation_id, algorithm: :concurrently, if_not_exists: true)
+
+ say('Extract the deletion target from coversations... This might take a while...')
+
+ ActiveRecord::Base.connection.create_table('conversations_to_be_deleted', force: true)
+
+ ActiveRecord::Base.connection.exec_insert(<<-SQL.squish, 'SQL')
+ INSERT INTO conversations_to_be_deleted (id)
+ SELECT id FROM conversations WHERE NOT EXISTS (SELECT 1 FROM statuses WHERE statuses.conversation_id = conversations.id)
+ SQL
+
+ say('Removing temporary database indices to restore write performance...')
+ ActiveRecord::Base.connection.remove_index(:statuses, name: :index_statuses_conversation_id, if_exists: true)
+ end
+
+ say('Beginning orphans removal... This might take a while...')
+
+ klass = Class.new(ApplicationRecord) do |c|
+ c.table_name = 'conversations_to_be_deleted'
+ end
+
+ Object.const_set('ConversationsToBeDeleted', klass)
+
+ scope = ConversationsToBeDeleted
+ processed = 0
+ removed = 0
+ progress = create_progress_bar(scope.count.fdiv(options[:batch_size]).ceil)
+
+ scope.in_batches(of: options[:batch_size]) do |relation|
+ ids = relation.pluck(:id)
+ processed += ids.count
+ removed += Conversation.unscoped.where(id: ids).delete_all
+ progress.increment
+ end
+
+ progress.stop
+
+ ActiveRecord::Base.connection.drop_table('conversations_to_be_deleted')
+
+ say("Done after #{Time.now.to_f - start_at}s, removed #{removed} out of #{processed} conversations.", :green)
+ ensure
+ say('Removing temporary database indices to restore write performance...')
+ ActiveRecord::Base.connection.remove_index(:statuses, name: :index_statuses_conversation_id, if_exists: true)
+ end
+
+ def vacuum_and_analyze_statuses
+ if options[:compress_database]
+ say('Run VACUUM FULL ANALYZE to statuses...')
+ ActiveRecord::Base.connection.execute('VACUUM FULL ANALYZE statuses')
+ say('Run REINDEX to statuses...')
+ ActiveRecord::Base.connection.execute('REINDEX TABLE statuses')
+ else
+ say('Run ANALYZE to statuses...')
+ ActiveRecord::Base.connection.execute('ANALYZE statuses')
+ end
+ end
+
+ def vacuum_and_analyze_conversations
+ if options[:compress_database]
+ say('Run VACUUM FULL ANALYZE to conversations...')
+ ActiveRecord::Base.connection.execute('VACUUM FULL ANALYZE conversations')
+ say('Run REINDEX to conversations...')
+ ActiveRecord::Base.connection.execute('REINDEX TABLE conversations')
+ else
+ say('Run ANALYZE to conversations...')
+ ActiveRecord::Base.connection.execute('ANALYZE conversations')
+ end
+ end
end
end
diff --git a/package.json b/package.json
index 2c39b67bbc..874170f7c3 100644
--- a/package.json
+++ b/package.json
@@ -149,13 +149,13 @@
"redis": "^3.1.2",
"redux": "^4.1.2",
"redux-immutable": "^4.0.0",
- "redux-thunk": "^2.4.0",
+ "redux-thunk": "^2.4.1",
"regenerator-runtime": "^0.13.9",
"rellax": "^1.12.1",
"requestidlecallback": "^0.3.0",
- "reselect": "^4.1.4",
+ "reselect": "^4.1.5",
"rimraf": "^3.0.2",
- "sass": "^1.43.4",
+ "sass": "^1.43.5",
"sass-loader": "^10.2.0",
"stacktrace-js": "^2.0.2",
"stringz": "^2.1.0",
@@ -172,19 +172,19 @@
"webpack-cli": "^3.3.12",
"webpack-merge": "^5.8.0",
"wicg-inert": "^3.1.1",
- "ws": "^8.2.3"
+ "ws": "^8.3.0"
},
"devDependencies": {
- "@testing-library/jest-dom": "^5.15.0",
+ "@testing-library/jest-dom": "^5.16.0",
"@testing-library/react": "^12.1.2",
"babel-eslint": "^10.1.0",
- "babel-jest": "^27.3.1",
+ "babel-jest": "^27.4.0",
"eslint": "^7.32.0",
"eslint-plugin-import": "~2.25.3",
"eslint-plugin-jsx-a11y": "~6.5.1",
"eslint-plugin-promise": "~5.1.1",
"eslint-plugin-react": "~7.27.1",
- "jest": "^27.3.1",
+ "jest": "^27.4.3",
"raf": "^3.4.1",
"react-intl-translations-manager": "^5.0.3",
"react-test-renderer": "^16.14.0",
diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb
index 608606ff90..a5ef396ae4 100644
--- a/spec/controllers/admin/accounts_controller_spec.rb
+++ b/spec/controllers/admin/accounts_controller_spec.rb
@@ -21,12 +21,9 @@ RSpec.describe Admin::AccountsController, type: :controller do
expect(AccountFilter).to receive(:new) do |params|
h = params.to_h
- expect(h[:local]).to eq '1'
- expect(h[:remote]).to eq '1'
+ expect(h[:origin]).to eq 'local'
expect(h[:by_domain]).to eq 'domain'
- expect(h[:active]).to eq '1'
- expect(h[:silenced]).to eq '1'
- expect(h[:suspended]).to eq '1'
+ expect(h[:status]).to eq 'active'
expect(h[:username]).to eq 'username'
expect(h[:display_name]).to eq 'display name'
expect(h[:email]).to eq 'local-part@domain'
@@ -36,12 +33,9 @@ RSpec.describe Admin::AccountsController, type: :controller do
end
get :index, params: {
- local: '1',
- remote: '1',
+ origin: 'local',
by_domain: 'domain',
- active: '1',
- silenced: '1',
- suspended: '1',
+ status: 'active',
username: 'username',
display_name: 'display name',
email: 'local-part@domain',
diff --git a/spec/models/account_filter_spec.rb b/spec/models/account_filter_spec.rb
index 0cdb373f6a..c2bd8c2202 100644
--- a/spec/models/account_filter_spec.rb
+++ b/spec/models/account_filter_spec.rb
@@ -2,10 +2,10 @@ require 'rails_helper'
describe AccountFilter do
describe 'with empty params' do
- it 'defaults to recent local not-suspended account list' do
+ it 'excludes instance actor by default' do
filter = described_class.new({})
- expect(filter.results).to eq Account.local.without_instance_actor.recent.without_suspended
+ expect(filter.results).to eq Account.without_instance_actor
end
end
@@ -16,42 +16,4 @@ describe AccountFilter do
expect { filter.results }.to raise_error(/wrong/)
end
end
-
- describe 'with valid params' do
- it 'combines filters on Account' do
- filter = described_class.new(
- by_domain: 'test.com',
- silenced: true,
- username: 'test',
- display_name: 'name',
- email: 'user@example.com',
- )
-
- allow(Account).to receive(:where).and_return(Account.none)
- allow(Account).to receive(:silenced).and_return(Account.none)
- allow(Account).to receive(:matches_display_name).and_return(Account.none)
- allow(Account).to receive(:matches_username).and_return(Account.none)
- allow(User).to receive(:matches_email).and_return(User.none)
-
- filter.results
-
- expect(Account).to have_received(:where).with(domain: 'test.com')
- expect(Account).to have_received(:silenced)
- expect(Account).to have_received(:matches_username).with('test')
- expect(Account).to have_received(:matches_display_name).with('name')
- expect(User).to have_received(:matches_email).with('user@example.com')
- end
-
- describe 'that call account methods' do
- %i(local remote silenced suspended).each do |option|
- it "delegates the #{option} option" do
- allow(Account).to receive(option).and_return(Account.none)
- filter = described_class.new({ option => true })
- filter.results
-
- expect(Account).to have_received(option).at_least(1)
- end
- end
- end
- end
end
diff --git a/yarn.lock b/yarn.lock
index d996ebc2b7..54f9bbc612 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -104,20 +104,6 @@
"@babel/helper-annotate-as-pure" "^7.16.0"
regexpu-core "^4.7.1"
-"@babel/helper-define-polyfill-provider@^0.2.4":
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.4.tgz#8867aed79d3ea6cade40f801efb7ac5c66916b10"
- integrity sha512-OrpPZ97s+aPi6h2n1OXzdhVis1SGSsMU2aMHgLcOKfsp4/v1NWpx3CWT3lBj5eeBq9cDkPkh+YCfdF7O12uNDQ==
- dependencies:
- "@babel/helper-compilation-targets" "^7.13.0"
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/helper-plugin-utils" "^7.13.0"
- "@babel/traverse" "^7.13.0"
- debug "^4.1.1"
- lodash.debounce "^4.0.8"
- resolve "^1.14.2"
- semver "^6.1.2"
-
"@babel/helper-define-polyfill-provider@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.0.tgz#c5b10cf4b324ff840140bb07e05b8564af2ae971"
@@ -1189,93 +1175,93 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
-"@jest/console@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.3.1.tgz#e8ea3a475d3f8162f23d69efbfaa9cbe486bee93"
- integrity sha512-RkFNWmv0iui+qsOr/29q9dyfKTTT5DCuP31kUwg7rmOKPT/ozLeGLKJKVIiOfbiKyleUZKIrHwhmiZWVe8IMdw==
+"@jest/console@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.4.2.tgz#7a95612d38c007ddb528ee446fe5e5e785e685ce"
+ integrity sha512-xknHThRsPB/To1FUbi6pCe43y58qFC03zfb6R7fDb/FfC7k2R3i1l+izRBJf8DI46KhYGRaF14Eo9A3qbBoixg==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
chalk "^4.0.0"
- jest-message-util "^27.3.1"
- jest-util "^27.3.1"
+ jest-message-util "^27.4.2"
+ jest-util "^27.4.2"
slash "^3.0.0"
-"@jest/core@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.3.1.tgz#04992ef1b58b17c459afb87ab56d81e63d386925"
- integrity sha512-DMNE90RR5QKx0EA+wqe3/TNEwiRpOkhshKNxtLxd4rt3IZpCt+RSL+FoJsGeblRZmqdK4upHA/mKKGPPRAifhg==
+"@jest/core@^27.4.3":
+ version "27.4.3"
+ resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.3.tgz#9b9b34f4e6429a633085f476402aa2e3ce707877"
+ integrity sha512-V9ms3zSxUHxh1E/ZLAiXF7SLejsdFnjWTFizWotMOWvjho0lW5kSjZymhQSodNW0T0ZMQRiha7f8+NcFVm3hJQ==
dependencies:
- "@jest/console" "^27.3.1"
- "@jest/reporters" "^27.3.1"
- "@jest/test-result" "^27.3.1"
- "@jest/transform" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/console" "^27.4.2"
+ "@jest/reporters" "^27.4.2"
+ "@jest/test-result" "^27.4.2"
+ "@jest/transform" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
ansi-escapes "^4.2.1"
chalk "^4.0.0"
emittery "^0.8.1"
exit "^0.1.2"
graceful-fs "^4.2.4"
- jest-changed-files "^27.3.0"
- jest-config "^27.3.1"
- jest-haste-map "^27.3.1"
- jest-message-util "^27.3.1"
- jest-regex-util "^27.0.6"
- jest-resolve "^27.3.1"
- jest-resolve-dependencies "^27.3.1"
- jest-runner "^27.3.1"
- jest-runtime "^27.3.1"
- jest-snapshot "^27.3.1"
- jest-util "^27.3.1"
- jest-validate "^27.3.1"
- jest-watcher "^27.3.1"
+ jest-changed-files "^27.4.2"
+ jest-config "^27.4.3"
+ jest-haste-map "^27.4.2"
+ jest-message-util "^27.4.2"
+ jest-regex-util "^27.4.0"
+ jest-resolve "^27.4.2"
+ jest-resolve-dependencies "^27.4.2"
+ jest-runner "^27.4.3"
+ jest-runtime "^27.4.2"
+ jest-snapshot "^27.4.2"
+ jest-util "^27.4.2"
+ jest-validate "^27.4.2"
+ jest-watcher "^27.4.2"
micromatch "^4.0.4"
rimraf "^3.0.0"
slash "^3.0.0"
strip-ansi "^6.0.0"
-"@jest/environment@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.3.1.tgz#2182defbce8d385fd51c5e7c7050f510bd4c86b1"
- integrity sha512-BCKCj4mOVLme6Tanoyc9k0ultp3pnmuyHw73UHRPeeZxirsU/7E3HC4le/VDb/SMzE1JcPnto+XBKFOcoiJzVw==
+"@jest/environment@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.2.tgz#03efabce528dbb09bffd3ec7e39bb0f3f7475cc2"
+ integrity sha512-uSljKxh/rGlHlmhyeG4ZoVK9hOec+EPBkwTHkHKQ2EqDu5K+MaG9uJZ8o1CbRsSdZqSuhXvJCYhBWsORPPg6qw==
dependencies:
- "@jest/fake-timers" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/fake-timers" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
- jest-mock "^27.3.0"
+ jest-mock "^27.4.2"
-"@jest/fake-timers@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.3.1.tgz#1fad860ee9b13034762cdb94266e95609dfce641"
- integrity sha512-M3ZFgwwlqJtWZ+QkBG5NmC23A9w+A6ZxNsO5nJxJsKYt4yguBd3i8TpjQz5NfCX91nEve1KqD9RA2Q+Q1uWqoA==
+"@jest/fake-timers@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.4.2.tgz#d217f86c3ba2027bf29e0b731fd0cb761a72d093"
+ integrity sha512-f/Xpzn5YQk5adtqBgvw1V6bF8Nx3hY0OIRRpCvWcfPl0EAjdqWPdhH3t/3XpiWZqtjIEHDyMKP9ajpva1l4Zmg==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
"@sinonjs/fake-timers" "^8.0.1"
"@types/node" "*"
- jest-message-util "^27.3.1"
- jest-mock "^27.3.0"
- jest-util "^27.3.1"
+ jest-message-util "^27.4.2"
+ jest-mock "^27.4.2"
+ jest-util "^27.4.2"
-"@jest/globals@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.3.1.tgz#ce1dfb03d379237a9da6c1b99ecfaca1922a5f9e"
- integrity sha512-Q651FWiWQAIFiN+zS51xqhdZ8g9b88nGCobC87argAxA7nMfNQq0Q0i9zTfQYgLa6qFXk2cGANEqfK051CZ8Pg==
+"@jest/globals@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.4.2.tgz#56a402c5ebf22eba1d34e900772147f5126ea2d8"
+ integrity sha512-KkfaHEttlGpXYAQTZHgrESiEPx2q/DKAFLGLFda1uGVrqc17snd3YVPhOxlXOHIzVPs+lQ/SDB2EIvxyGzb3Ew==
dependencies:
- "@jest/environment" "^27.3.1"
- "@jest/types" "^27.2.5"
- expect "^27.3.1"
+ "@jest/environment" "^27.4.2"
+ "@jest/types" "^27.4.2"
+ expect "^27.4.2"
-"@jest/reporters@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.3.1.tgz#28b5c1f5789481e23788048fa822ed15486430b9"
- integrity sha512-m2YxPmL9Qn1emFVgZGEiMwDntDxRRQ2D58tiDQlwYTg5GvbFOKseYCcHtn0WsI8CG4vzPglo3nqbOiT8ySBT/w==
+"@jest/reporters@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.2.tgz#d3860c5d3f668fa1326ab2bf5989f774e5c03f04"
+ integrity sha512-sp4aqmdBJtjKetEakzDPcZggPcVIF6w9QLkYBbaWDV6e/SIsHnF1S4KtIH91eEc2fp7ep6V/e1xvdfEoho1d2w==
dependencies:
"@bcoe/v8-coverage" "^0.2.3"
- "@jest/console" "^27.3.1"
- "@jest/test-result" "^27.3.1"
- "@jest/transform" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/console" "^27.4.2"
+ "@jest/test-result" "^27.4.2"
+ "@jest/transform" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
chalk "^4.0.0"
collect-v8-coverage "^1.0.0"
@@ -1287,60 +1273,81 @@
istanbul-lib-report "^3.0.0"
istanbul-lib-source-maps "^4.0.0"
istanbul-reports "^3.0.2"
- jest-haste-map "^27.3.1"
- jest-resolve "^27.3.1"
- jest-util "^27.3.1"
- jest-worker "^27.3.1"
+ jest-haste-map "^27.4.2"
+ jest-resolve "^27.4.2"
+ jest-util "^27.4.2"
+ jest-worker "^27.4.2"
slash "^3.0.0"
source-map "^0.6.0"
string-length "^4.0.1"
terminal-link "^2.0.0"
v8-to-istanbul "^8.1.0"
-"@jest/source-map@^27.0.6":
- version "27.0.6"
- resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.0.6.tgz#be9e9b93565d49b0548b86e232092491fb60551f"
- integrity sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g==
+"@jest/source-map@^27.4.0":
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6"
+ integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==
dependencies:
callsites "^3.0.0"
graceful-fs "^4.2.4"
source-map "^0.6.0"
-"@jest/test-result@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.3.1.tgz#89adee8b771877c69b3b8d59f52f29dccc300194"
- integrity sha512-mLn6Thm+w2yl0opM8J/QnPTqrfS4FoXsXF2WIWJb2O/GBSyResL71BRuMYbYRsGt7ELwS5JGcEcGb52BNrumgg==
+"@jest/test-result@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.4.2.tgz#05fd4a5466ec502f3eae0b39dff2b93ea4d5d9ec"
+ integrity sha512-kr+bCrra9jfTgxHXHa2UwoQjxvQk3Am6QbpAiJ5x/50LW8llOYrxILkqY0lZRW/hu8FXesnudbql263+EW9iNA==
dependencies:
- "@jest/console" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/console" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/istanbul-lib-coverage" "^2.0.0"
collect-v8-coverage "^1.0.0"
-"@jest/test-sequencer@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.3.1.tgz#4b3bde2dbb05ee74afdae608cf0768e3354683b1"
- integrity sha512-siySLo07IMEdSjA4fqEnxfIX8lB/lWYsBPwNFtkOvsFQvmBrL3yj3k3uFNZv/JDyApTakRpxbKLJ3CT8UGVCrA==
+"@jest/test-sequencer@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.2.tgz#94bb7e5412d59ae2a8a4b8f9925bb16b6dc82b4c"
+ integrity sha512-HmHp5mlh9f9GyNej5yCS1JZIFfUGnP9+jEOH5zoq5EmsuZeYD+dGULqyvGDPtuzzbyAFJ6R4+z4SS0VvnFwwGQ==
dependencies:
- "@jest/test-result" "^27.3.1"
+ "@jest/test-result" "^27.4.2"
graceful-fs "^4.2.4"
- jest-haste-map "^27.3.1"
- jest-runtime "^27.3.1"
+ jest-haste-map "^27.4.2"
+ jest-runtime "^27.4.2"
-"@jest/transform@^27.3.1":
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.3.1.tgz#ff80eafbeabe811e9025e4b6f452126718455220"
- integrity sha512-3fSvQ02kuvjOI1C1ssqMVBKJpZf6nwoCiSu00zAKh5nrp3SptNtZy/8s5deayHnqxhjD9CWDJ+yqQwuQ0ZafXQ==
+"@jest/transform@^27.4.0":
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.0.tgz#060bf842d3ac162c50c684e8422f3bfce6c824c1"
+ integrity sha512-/8Cb8kEoCtXN/Co5lvv+jG0zv4Uj3ruIvffYUzxNGRGmM7qqaHtOBZ3WbH0T1Nvjya5utTA4YtwbInZVS6Zt9A==
dependencies:
"@babel/core" "^7.1.0"
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.0"
babel-plugin-istanbul "^6.0.0"
chalk "^4.0.0"
convert-source-map "^1.4.0"
fast-json-stable-stringify "^2.0.0"
graceful-fs "^4.2.4"
- jest-haste-map "^27.3.1"
- jest-regex-util "^27.0.6"
- jest-util "^27.3.1"
+ jest-haste-map "^27.4.0"
+ jest-regex-util "^27.4.0"
+ jest-util "^27.4.0"
+ micromatch "^4.0.4"
+ pirates "^4.0.1"
+ slash "^3.0.0"
+ source-map "^0.6.1"
+ write-file-atomic "^3.0.0"
+
+"@jest/transform@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.2.tgz#459885e96de2e21fc68b8b371e90aa653966dd0d"
+ integrity sha512-RTKcPZllfcmLfnlxBya7aypofhdz05+E6QITe55Ex0rxyerkgjmmpMlvVn11V0cP719Ps6WcDYCnDzxnnJUwKg==
+ dependencies:
+ "@babel/core" "^7.1.0"
+ "@jest/types" "^27.4.2"
+ babel-plugin-istanbul "^6.0.0"
+ chalk "^4.0.0"
+ convert-source-map "^1.4.0"
+ fast-json-stable-stringify "^2.0.0"
+ graceful-fs "^4.2.4"
+ jest-haste-map "^27.4.2"
+ jest-regex-util "^27.4.0"
+ jest-util "^27.4.2"
micromatch "^4.0.4"
pirates "^4.0.1"
slash "^3.0.0"
@@ -1357,10 +1364,21 @@
"@types/yargs" "^15.0.0"
chalk "^3.0.0"
-"@jest/types@^27.0.2", "@jest/types@^27.2.5":
- version "27.2.5"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.5.tgz#420765c052605e75686982d24b061b4cbba22132"
- integrity sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==
+"@jest/types@^27.0.2", "@jest/types@^27.4.0":
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.0.tgz#ac5c04d29ce47e0b96439dfd44ec3cd930fc9f86"
+ integrity sha512-jIsLdASXMf8GS7P7oGFGwobNse/6Ewq3GBPHoo0i6XRmja+NrUoDqJm4a1ffF2bHGleKJizxokcp1sCqSktP3g==
+ dependencies:
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^3.0.0"
+ "@types/node" "*"
+ "@types/yargs" "^16.0.0"
+ chalk "^4.0.0"
+
+"@jest/types@^27.4.2":
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5"
+ integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
@@ -1413,14 +1431,14 @@
lz-string "^1.4.4"
pretty-format "^27.0.2"
-"@testing-library/jest-dom@^5.15.0":
- version "5.15.0"
- resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.15.0.tgz#4f5295dbc476a14aec3b07176434b3d51aae5da7"
- integrity sha512-lOMuQidnL1tWHLEWIhL6UvSZC1Qt3OkNe1khvi2h6xFiqpe5O8arYs46OU0qyUGq0cSTbroQyMktYNXu3a7sAA==
+"@testing-library/jest-dom@^5.16.0":
+ version "5.16.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.0.tgz#de1a7c5fedfeb80eb2be9fc81f61473973b302b3"
+ integrity sha512-ECygvCL6ufPfHna4fsk7o24+3PVNhRbioDpFbfSVEZaglT6EjuRP+w8I5tzigFz1fobpvCrVRoKyR4qx2QUCxw==
dependencies:
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
- aria-query "^4.2.2"
+ aria-query "^5.0.0"
chalk "^3.0.0"
css "^3.0.0"
css.escape "^1.5.1"
@@ -2079,6 +2097,11 @@ aria-query@^4.2.2:
"@babel/runtime" "^7.10.2"
"@babel/runtime-corejs3" "^7.10.2"
+aria-query@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c"
+ integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==
+
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -2262,16 +2285,30 @@ babel-eslint@^10.1.0:
eslint-visitor-keys "^1.0.0"
resolve "^1.12.0"
-babel-jest@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.3.1.tgz#0636a3404c68e07001e434ac4956d82da8a80022"
- integrity sha512-SjIF8hh/ir0peae2D6S6ZKRhUy7q/DnpH7k/V6fT4Bgs/LXXUztOpX4G2tCgq8mLo5HA9mN6NmlFMeYtKmIsTQ==
+babel-jest@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.0.tgz#ba78a2e19260a0009206f4e717ee2b78ee759781"
+ integrity sha512-4855S+YT4Hx0OiXFDBOWhrMj1Y9zYE7StlchuZtr1vbo1LEDBIkt8U6+7cse8jkpJSV98w3nBVDrPgol5Ab/cQ==
dependencies:
- "@jest/transform" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/transform" "^27.4.0"
+ "@jest/types" "^27.4.0"
"@types/babel__core" "^7.1.14"
babel-plugin-istanbul "^6.0.0"
- babel-preset-jest "^27.2.0"
+ babel-preset-jest "^27.4.0"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.4"
+ slash "^3.0.0"
+
+babel-jest@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.2.tgz#6edf80971045cfd44f3f10b6eda6007d95f62742"
+ integrity sha512-MADrjb3KBO2eyZCAc6QaJg6RT5u+6oEdDyHO5HEalnpwQ6LrhTsQF2Kj1Wnz2t6UPXIXPk18dSXXOT0wF5yTxA==
+ dependencies:
+ "@jest/transform" "^27.4.2"
+ "@jest/types" "^27.4.2"
+ "@types/babel__core" "^7.1.14"
+ babel-plugin-istanbul "^6.0.0"
+ babel-preset-jest "^27.4.0"
chalk "^4.0.0"
graceful-fs "^4.2.4"
slash "^3.0.0"
@@ -2304,10 +2341,10 @@ babel-plugin-istanbul@^6.0.0:
istanbul-lib-instrument "^4.0.0"
test-exclude "^6.0.0"
-babel-plugin-jest-hoist@^27.2.0:
- version "27.2.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.2.0.tgz#79f37d43f7e5c4fdc4b2ca3e10cc6cf545626277"
- integrity sha512-TOux9khNKdi64mW+0OIhcmbAn75tTlzKhxmiNXevQaPbrBYK7YKjP1jl6NHTJ6XR5UgUrJbCnWlKVnJn29dfjw==
+babel-plugin-jest-hoist@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6"
+ integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==
dependencies:
"@babel/template" "^7.3.3"
"@babel/types" "^7.3.3"
@@ -2334,15 +2371,6 @@ babel-plugin-macros@^2.8.0:
cosmiconfig "^6.0.0"
resolve "^1.12.0"
-babel-plugin-polyfill-corejs2@^0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.3.tgz#6ed8e30981b062f8fe6aca8873a37ebcc8cc1c0f"
- integrity sha512-NDZ0auNRzmAfE1oDDPW2JhzIMXUk+FFe2ICejmt5T4ocKgiQx3e0VCRx9NCAidcMtL2RUZaWtXnmjTCkx0tcbA==
- dependencies:
- "@babel/compat-data" "^7.13.11"
- "@babel/helper-define-polyfill-provider" "^0.2.4"
- semver "^6.1.1"
-
babel-plugin-polyfill-corejs2@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz#407082d0d355ba565af24126fb6cb8e9115251fd"
@@ -2352,14 +2380,6 @@ babel-plugin-polyfill-corejs2@^0.3.0:
"@babel/helper-define-polyfill-provider" "^0.3.0"
semver "^6.1.1"
-babel-plugin-polyfill-corejs3@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.3.0.tgz#fa7ca3d1ee9ddc6193600ffb632c9785d54918af"
- integrity sha512-JLwi9vloVdXLjzACL80j24bG6/T1gYxwowG44dg6HN/7aTPdyPbJJidf6ajoA3RPHHtW0j9KMrSOLpIZpAnPpg==
- dependencies:
- "@babel/helper-define-polyfill-provider" "^0.2.4"
- core-js-compat "^3.18.0"
-
babel-plugin-polyfill-corejs3@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz#0b571f4cf3d67f911512f5c04842a7b8e8263087"
@@ -2368,13 +2388,6 @@ babel-plugin-polyfill-corejs3@^0.4.0:
"@babel/helper-define-polyfill-provider" "^0.3.0"
core-js-compat "^3.18.0"
-babel-plugin-polyfill-regenerator@^0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.3.tgz#2e9808f5027c4336c994992b48a4262580cb8d6d"
- integrity sha512-JVE78oRZPKFIeUqFGrSORNzQnrDwZR16oiWeGM8ZyjBn2XAT5OjP+wXx5ESuo33nUsFUEJYjtklnsKbxW5L+7g==
- dependencies:
- "@babel/helper-define-polyfill-provider" "^0.2.4"
-
babel-plugin-polyfill-regenerator@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz#9ebbcd7186e1a33e21c5e20cae4e7983949533be"
@@ -2427,12 +2440,12 @@ babel-preset-current-node-syntax@^1.0.0:
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
"@babel/plugin-syntax-top-level-await" "^7.8.3"
-babel-preset-jest@^27.2.0:
- version "27.2.0"
- resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.2.0.tgz#556bbbf340608fed5670ab0ea0c8ef2449fba885"
- integrity sha512-z7MgQ3peBwN5L5aCqBKnF6iqdlvZvFUQynEhu0J+X9nHLU72jO3iY331lcYrg+AssJ8q7xsv5/3AICzVmJ/wvg==
+babel-preset-jest@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca"
+ integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==
dependencies:
- babel-plugin-jest-hoist "^27.2.0"
+ babel-plugin-jest-hoist "^27.4.0"
babel-preset-current-node-syntax "^1.0.0"
babel-runtime@^6.26.0:
@@ -3851,10 +3864,10 @@ diff-sequences@^25.2.6:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd"
integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==
-diff-sequences@^27.0.6:
- version "27.0.6"
- resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723"
- integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==
+diff-sequences@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5"
+ integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==
diffie-hellman@^5.0.0:
version "5.0.3"
@@ -4621,17 +4634,17 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
dependencies:
homedir-polyfill "^1.0.1"
-expect@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/expect/-/expect-27.3.1.tgz#d0f170b1f5c8a2009bab0beffd4bb94f043e38e7"
- integrity sha512-MrNXV2sL9iDRebWPGOGFdPQRl2eDQNu/uhxIMShjjx74T6kC6jFIkmQ6OqXDtevjGUkyB2IT56RzDBqXf/QPCg==
+expect@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.2.tgz#4429b0f7e307771d176de9bdf23229b101db6ef6"
+ integrity sha512-BjAXIDC6ZOW+WBFNg96J22D27Nq5ohn+oGcuP2rtOtcjuxNoV9McpQ60PcQWhdFOSBIQdR72e+4HdnbZTFSTyg==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
ansi-styles "^5.0.0"
- jest-get-type "^27.3.1"
- jest-matcher-utils "^27.3.1"
- jest-message-util "^27.3.1"
- jest-regex-util "^27.0.6"
+ jest-get-type "^27.4.0"
+ jest-matcher-utils "^27.4.2"
+ jest-message-util "^27.4.2"
+ jest-regex-util "^27.4.0"
express@^4.17.1:
version "4.17.1"
@@ -6211,84 +6224,85 @@ istanbul-reports@^3.0.2:
html-escaper "^2.0.0"
istanbul-lib-report "^3.0.0"
-jest-changed-files@^27.3.0:
- version "27.3.0"
- resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.3.0.tgz#22a02cc2b34583fc66e443171dc271c0529d263c"
- integrity sha512-9DJs9garMHv4RhylUMZgbdCJ3+jHSkpL9aaVKp13xtXAD80qLTLrqcDZL1PHA9dYA0bCI86Nv2BhkLpLhrBcPg==
+jest-changed-files@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5"
+ integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
execa "^5.0.0"
throat "^6.0.1"
-jest-circus@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.3.1.tgz#1679e74387cbbf0c6a8b42de963250a6469e0797"
- integrity sha512-v1dsM9II6gvXokgqq6Yh2jHCpfg7ZqV4jWY66u7npz24JnhP3NHxI0sKT7+ZMQ7IrOWHYAaeEllOySbDbWsiXw==
+jest-circus@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.4.2.tgz#466f482207ca9f323b78416c28f4d1fa7588159a"
+ integrity sha512-2ePUSru1BGMyzxsMvRfu+tNb+PW60rUyMLJBfw1Nrh5zC8RoTPfF+zbE0JToU31a6ZVe4nnrNKWYRzlghAbL0A==
dependencies:
- "@jest/environment" "^27.3.1"
- "@jest/test-result" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/environment" "^27.4.2"
+ "@jest/test-result" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
chalk "^4.0.0"
co "^4.6.0"
dedent "^0.7.0"
- expect "^27.3.1"
+ expect "^27.4.2"
is-generator-fn "^2.0.0"
- jest-each "^27.3.1"
- jest-matcher-utils "^27.3.1"
- jest-message-util "^27.3.1"
- jest-runtime "^27.3.1"
- jest-snapshot "^27.3.1"
- jest-util "^27.3.1"
- pretty-format "^27.3.1"
+ jest-each "^27.4.2"
+ jest-matcher-utils "^27.4.2"
+ jest-message-util "^27.4.2"
+ jest-runtime "^27.4.2"
+ jest-snapshot "^27.4.2"
+ jest-util "^27.4.2"
+ pretty-format "^27.4.2"
slash "^3.0.0"
stack-utils "^2.0.3"
throat "^6.0.1"
-jest-cli@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.3.1.tgz#b576f9d146ba6643ce0a162d782b40152b6b1d16"
- integrity sha512-WHnCqpfK+6EvT62me6WVs8NhtbjAS4/6vZJnk7/2+oOr50cwAzG4Wxt6RXX0hu6m1169ZGMlhYYUNeKBXCph/Q==
+jest-cli@^27.4.3:
+ version "27.4.3"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.3.tgz#89acba683b9f91c7a5e342e2ea13aa5414836a0d"
+ integrity sha512-zZSJBXNC/i8UnJPwcKWsqnhGgIF3uoTYP7th32Zej7KNQJdxzOMj+wCfy2Ox3kU7nXErJ36DtYyXDhfiqaiDRw==
dependencies:
- "@jest/core" "^27.3.1"
- "@jest/test-result" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/core" "^27.4.3"
+ "@jest/test-result" "^27.4.2"
+ "@jest/types" "^27.4.2"
chalk "^4.0.0"
exit "^0.1.2"
graceful-fs "^4.2.4"
import-local "^3.0.2"
- jest-config "^27.3.1"
- jest-util "^27.3.1"
- jest-validate "^27.3.1"
+ jest-config "^27.4.3"
+ jest-util "^27.4.2"
+ jest-validate "^27.4.2"
prompts "^2.0.1"
yargs "^16.2.0"
-jest-config@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.3.1.tgz#cb3b7f6aaa8c0a7daad4f2b9573899ca7e09bbad"
- integrity sha512-KY8xOIbIACZ/vdYCKSopL44I0xboxC751IX+DXL2+Wx6DKNycyEfV3rryC3BPm5Uq/BBqDoMrKuqLEUNJmMKKg==
+jest-config@^27.4.3:
+ version "27.4.3"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.3.tgz#7820e08f7526fa3f725423e2f0fa7888ee0ef9c9"
+ integrity sha512-DQ10HTSqYtC2pO7s9j2jw+li4xUnm2wLYWH2o7K1ftB8NyvToHsXoLlXxtsGh3AW9gUQR6KY/4B7G+T/NswJBw==
dependencies:
"@babel/core" "^7.1.0"
- "@jest/test-sequencer" "^27.3.1"
- "@jest/types" "^27.2.5"
- babel-jest "^27.3.1"
+ "@jest/test-sequencer" "^27.4.2"
+ "@jest/types" "^27.4.2"
+ babel-jest "^27.4.2"
chalk "^4.0.0"
ci-info "^3.2.0"
deepmerge "^4.2.2"
glob "^7.1.1"
graceful-fs "^4.2.4"
- jest-circus "^27.3.1"
- jest-environment-jsdom "^27.3.1"
- jest-environment-node "^27.3.1"
- jest-get-type "^27.3.1"
- jest-jasmine2 "^27.3.1"
- jest-regex-util "^27.0.6"
- jest-resolve "^27.3.1"
- jest-runner "^27.3.1"
- jest-util "^27.3.1"
- jest-validate "^27.3.1"
+ jest-circus "^27.4.2"
+ jest-environment-jsdom "^27.4.3"
+ jest-environment-node "^27.4.2"
+ jest-get-type "^27.4.0"
+ jest-jasmine2 "^27.4.2"
+ jest-regex-util "^27.4.0"
+ jest-resolve "^27.4.2"
+ jest-runner "^27.4.3"
+ jest-util "^27.4.2"
+ jest-validate "^27.4.2"
micromatch "^4.0.4"
- pretty-format "^27.3.1"
+ pretty-format "^27.4.2"
+ slash "^3.0.0"
jest-diff@^25.2.1:
version "25.5.0"
@@ -6300,152 +6314,172 @@ jest-diff@^25.2.1:
jest-get-type "^25.2.6"
pretty-format "^25.5.0"
-jest-diff@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.3.1.tgz#d2775fea15411f5f5aeda2a5e02c2f36440f6d55"
- integrity sha512-PCeuAH4AWUo2O5+ksW4pL9v5xJAcIKPUPfIhZBcG1RKv/0+dvaWTQK1Nrau8d67dp65fOqbeMdoil+6PedyEPQ==
+jest-diff@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.2.tgz#786b2a5211d854f848e2dcc1e324448e9481f36f"
+ integrity sha512-ujc9ToyUZDh9KcqvQDkk/gkbf6zSaeEg9AiBxtttXW59H/AcqEYp1ciXAtJp+jXWva5nAf/ePtSsgWwE5mqp4Q==
dependencies:
chalk "^4.0.0"
- diff-sequences "^27.0.6"
- jest-get-type "^27.3.1"
- pretty-format "^27.3.1"
+ diff-sequences "^27.4.0"
+ jest-get-type "^27.4.0"
+ pretty-format "^27.4.2"
-jest-docblock@^27.0.6:
- version "27.0.6"
- resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3"
- integrity sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA==
+jest-docblock@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f"
+ integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==
dependencies:
detect-newline "^3.0.0"
-jest-each@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.3.1.tgz#14c56bb4f18dd18dc6bdd853919b5f16a17761ff"
- integrity sha512-E4SwfzKJWYcvOYCjOxhZcxwL+AY0uFMvdCOwvzgutJiaiodFjkxQQDxHm8FQBeTqDnSmKsQWn7ldMRzTn2zJaQ==
+jest-each@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.2.tgz#19364c82a692d0d26557642098d1f4619c9ee7d3"
+ integrity sha512-53V2MNyW28CTruB3lXaHNk6PkiIFuzdOC9gR3C6j8YE/ACfrPnz+slB0s17AgU1TtxNzLuHyvNlLJ+8QYw9nBg==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
chalk "^4.0.0"
- jest-get-type "^27.3.1"
- jest-util "^27.3.1"
- pretty-format "^27.3.1"
+ jest-get-type "^27.4.0"
+ jest-util "^27.4.2"
+ pretty-format "^27.4.2"
-jest-environment-jsdom@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.3.1.tgz#63ac36d68f7a9303494df783494856222b57f73e"
- integrity sha512-3MOy8qMzIkQlfb3W1TfrD7uZHj+xx8Olix5vMENkj5djPmRqndMaXtpnaZkxmxM+Qc3lo+yVzJjzuXbCcZjAlg==
+jest-environment-jsdom@^27.4.3:
+ version "27.4.3"
+ resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.3.tgz#74198285f6284888ca9c7486c4e5e67add75aa53"
+ integrity sha512-x1AUVz3G14LpEJs7KIFUaTINT2n0unOUmvdAby3s/sldUpJJetOJifHo1O/EUQC5fNBowggwJbVulko18y6OWw==
dependencies:
- "@jest/environment" "^27.3.1"
- "@jest/fake-timers" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/environment" "^27.4.2"
+ "@jest/fake-timers" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
- jest-mock "^27.3.0"
- jest-util "^27.3.1"
+ jest-mock "^27.4.2"
+ jest-util "^27.4.2"
jsdom "^16.6.0"
-jest-environment-node@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.3.1.tgz#af7d0eed04edafb740311b303f3fe7c8c27014bb"
- integrity sha512-T89F/FgkE8waqrTSA7/ydMkcc52uYPgZZ6q8OaZgyiZkJb5QNNCF6oPZjH9IfPFfcc9uBWh1574N0kY0pSvTXw==
+jest-environment-node@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.2.tgz#bf5586a0924a8d21c13838121ac0941638c7d15e"
+ integrity sha512-nzTZ5nJ+FabuZPH2YVci7SZIHpvtNRHPt8+vipLkCnAgXGjVzHm7XJWdnNqXbAkExIgiKeVEkVMNZOZE/LeiIg==
dependencies:
- "@jest/environment" "^27.3.1"
- "@jest/fake-timers" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/environment" "^27.4.2"
+ "@jest/fake-timers" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
- jest-mock "^27.3.0"
- jest-util "^27.3.1"
+ jest-mock "^27.4.2"
+ jest-util "^27.4.2"
jest-get-type@^25.2.6:
version "25.2.6"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877"
integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==
-jest-get-type@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.3.1.tgz#a8a2b0a12b50169773099eee60a0e6dd11423eff"
- integrity sha512-+Ilqi8hgHSAdhlQ3s12CAVNd8H96ZkQBfYoXmArzZnOfAtVAJEiPDBirjByEblvG/4LPJmkL+nBqPO3A1YJAEg==
+jest-get-type@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5"
+ integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==
-jest-haste-map@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.3.1.tgz#7656fbd64bf48bda904e759fc9d93e2c807353ee"
- integrity sha512-lYfNZIzwPccDJZIyk9Iz5iQMM/MH56NIIcGj7AFU1YyA4ewWFBl8z+YPJuSCRML/ee2cCt2y3W4K3VXPT6Nhzg==
+jest-haste-map@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.0.tgz#d07e0db356bbaa2996f922facf23e85f53e6c3b7"
+ integrity sha512-xTXw1/JBJvdvTEsnTlRj9u9AAg2t23r5GHbtc5eC6AuEIRPfGWV02Y67U0p4K1KpEWLsk9Pb3b6Kfde/5a3C5A==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.0"
"@types/graceful-fs" "^4.1.2"
"@types/node" "*"
anymatch "^3.0.3"
fb-watchman "^2.0.0"
graceful-fs "^4.2.4"
- jest-regex-util "^27.0.6"
- jest-serializer "^27.0.6"
- jest-util "^27.3.1"
- jest-worker "^27.3.1"
+ jest-regex-util "^27.4.0"
+ jest-serializer "^27.4.0"
+ jest-util "^27.4.0"
+ jest-worker "^27.4.0"
micromatch "^4.0.4"
walker "^1.0.7"
optionalDependencies:
fsevents "^2.3.2"
-jest-jasmine2@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.3.1.tgz#df6d3d07c7dafc344feb43a0072a6f09458d32b0"
- integrity sha512-WK11ZUetDQaC09w4/j7o4FZDUIp+4iYWH/Lik34Pv7ukL+DuXFGdnmmi7dT58J2ZYKFB5r13GyE0z3NPeyJmsg==
+jest-haste-map@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.2.tgz#7fc7d5e568cca704284f4850885b74a0b8b87587"
+ integrity sha512-foiyAEePORUN2eeJnOtcM1y8qW0ShEd9kTjWVL4sVaMcuCJM6gtHegvYPBRT0mpI/bs4ueThM90+Eoj2ncoNsA==
+ dependencies:
+ "@jest/types" "^27.4.2"
+ "@types/graceful-fs" "^4.1.2"
+ "@types/node" "*"
+ anymatch "^3.0.3"
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.2.4"
+ jest-regex-util "^27.4.0"
+ jest-serializer "^27.4.0"
+ jest-util "^27.4.2"
+ jest-worker "^27.4.2"
+ micromatch "^4.0.4"
+ walker "^1.0.7"
+ optionalDependencies:
+ fsevents "^2.3.2"
+
+jest-jasmine2@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.2.tgz#c956c88b9c05ca22afdc779deebc2890cb891797"
+ integrity sha512-VO/fyAJSH9u0THjbteFiL8qc93ufU+yW+bdieDc8tzTCWwlWzO53UHS5nFK1qmE8izb5Smkn+XHlVt6/l06MKQ==
dependencies:
"@babel/traverse" "^7.1.0"
- "@jest/environment" "^27.3.1"
- "@jest/source-map" "^27.0.6"
- "@jest/test-result" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/environment" "^27.4.2"
+ "@jest/source-map" "^27.4.0"
+ "@jest/test-result" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
chalk "^4.0.0"
co "^4.6.0"
- expect "^27.3.1"
+ expect "^27.4.2"
is-generator-fn "^2.0.0"
- jest-each "^27.3.1"
- jest-matcher-utils "^27.3.1"
- jest-message-util "^27.3.1"
- jest-runtime "^27.3.1"
- jest-snapshot "^27.3.1"
- jest-util "^27.3.1"
- pretty-format "^27.3.1"
+ jest-each "^27.4.2"
+ jest-matcher-utils "^27.4.2"
+ jest-message-util "^27.4.2"
+ jest-runtime "^27.4.2"
+ jest-snapshot "^27.4.2"
+ jest-util "^27.4.2"
+ pretty-format "^27.4.2"
throat "^6.0.1"
-jest-leak-detector@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.3.1.tgz#7fb632c2992ef707a1e73286e1e704f9cc1772b2"
- integrity sha512-78QstU9tXbaHzwlRlKmTpjP9k4Pvre5l0r8Spo4SbFFVy/4Abg9I6ZjHwjg2QyKEAMg020XcjP+UgLZIY50yEg==
+jest-leak-detector@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.2.tgz#7fc3120893a7a911c553f3f2bdff9faa4454abbb"
+ integrity sha512-ml0KvFYZllzPBJWDei3mDzUhyp/M4ubKebX++fPaudpe8OsxUE+m+P6ciVLboQsrzOCWDjE20/eXew9QMx/VGw==
dependencies:
- jest-get-type "^27.3.1"
- pretty-format "^27.3.1"
+ jest-get-type "^27.4.0"
+ pretty-format "^27.4.2"
-jest-matcher-utils@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.3.1.tgz#257ad61e54a6d4044e080d85dbdc4a08811e9c1c"
- integrity sha512-hX8N7zXS4k+8bC1Aj0OWpGb7D3gIXxYvPNK1inP5xvE4ztbz3rc4AkI6jGVaerepBnfWB17FL5lWFJT3s7qo8w==
+jest-matcher-utils@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.2.tgz#d17c5038607978a255e0a9a5c32c24e984b6c60b"
+ integrity sha512-jyP28er3RRtMv+fmYC/PKG8wvAmfGcSNproVTW2Y0P/OY7/hWUOmsPfxN1jOhM+0u2xU984u2yEagGivz9OBGQ==
dependencies:
chalk "^4.0.0"
- jest-diff "^27.3.1"
- jest-get-type "^27.3.1"
- pretty-format "^27.3.1"
+ jest-diff "^27.4.2"
+ jest-get-type "^27.4.0"
+ pretty-format "^27.4.2"
-jest-message-util@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.3.1.tgz#f7c25688ad3410ab10bcb862bcfe3152345c6436"
- integrity sha512-bh3JEmxsTZ/9rTm0jQrPElbY2+y48Rw2t47uMfByNyUVR+OfPh4anuyKsGqsNkXk/TI4JbLRZx+7p7Hdt6q1yg==
+jest-message-util@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.4.2.tgz#07f3f1bf207d69cf798ce830cc57f1a849f99388"
+ integrity sha512-OMRqRNd9E0DkBLZpFtZkAGYOXl6ZpoMtQJWTAREJKDOFa0M6ptB7L67tp+cszMBkvSgKOhNtQp2Vbcz3ZZKo/w==
dependencies:
"@babel/code-frame" "^7.12.13"
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
"@types/stack-utils" "^2.0.0"
chalk "^4.0.0"
graceful-fs "^4.2.4"
micromatch "^4.0.4"
- pretty-format "^27.3.1"
+ pretty-format "^27.4.2"
slash "^3.0.0"
stack-utils "^2.0.3"
-jest-mock@^27.3.0:
- version "27.3.0"
- resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.3.0.tgz#ddf0ec3cc3e68c8ccd489bef4d1f525571a1b867"
- integrity sha512-ziZiLk0elZOQjD08bLkegBzv5hCABu/c8Ytx45nJKkysQwGaonvmTxwjLqEA4qGdasq9o2I8/HtdGMNnVsMTGw==
+jest-mock@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.4.2.tgz#184ff197a25491bfe4570c286daa5d62eb760b88"
+ integrity sha512-PDDPuyhoukk20JrQKeofK12hqtSka7mWH0QQuxSNgrdiPsrnYYLS6wbzu/HDlxZRzji5ylLRULeuI/vmZZDrYA==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
jest-pnp-resolver@^1.2.2:
@@ -6453,76 +6487,76 @@ jest-pnp-resolver@^1.2.2:
resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
-jest-regex-util@^27.0.6:
- version "27.0.6"
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5"
- integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ==
+jest-regex-util@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca"
+ integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==
-jest-resolve-dependencies@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.3.1.tgz#85b99bdbdfa46e2c81c6228fc4c91076f624f6e2"
- integrity sha512-X7iLzY8pCiYOnvYo2YrK3P9oSE8/3N2f4pUZMJ8IUcZnT81vlSonya1KTO9ZfKGuC+svE6FHK/XOb8SsoRUV1A==
+jest-resolve-dependencies@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.2.tgz#2f4f363cca26f75a22aefd496f9c7ae65b3de37f"
+ integrity sha512-hb++cTpqvOWfU49MCP/JQkxmnrhKoAVqXWFjgYXswRSVGk8Q6bDTSvhbCeYXDtXaymY0y7WrrSIlKogClcKJuw==
dependencies:
- "@jest/types" "^27.2.5"
- jest-regex-util "^27.0.6"
- jest-snapshot "^27.3.1"
+ "@jest/types" "^27.4.2"
+ jest-regex-util "^27.4.0"
+ jest-snapshot "^27.4.2"
-jest-resolve@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.3.1.tgz#0e5542172a1aa0270be6f66a65888647bdd74a3e"
- integrity sha512-Dfzt25CFSPo3Y3GCbxynRBZzxq9AdyNN+x/v2IqYx6KVT5Z6me2Z/PsSGFSv3cOSUZqJ9pHxilao/I/m9FouLw==
+jest-resolve@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.2.tgz#d3e4cbee7acb4a4f8c8bfc270767bec34d2aefaf"
+ integrity sha512-d/zqPjxCzMqHlOdRTg8cTpO9jY+1/T74KazT8Ws/LwmwxV5sRMWOkiLjmzUCDj/5IqA5XHNK4Hkmlq9Kdpb9Sg==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
chalk "^4.0.0"
graceful-fs "^4.2.4"
- jest-haste-map "^27.3.1"
+ jest-haste-map "^27.4.2"
jest-pnp-resolver "^1.2.2"
- jest-util "^27.3.1"
- jest-validate "^27.3.1"
+ jest-util "^27.4.2"
+ jest-validate "^27.4.2"
resolve "^1.20.0"
resolve.exports "^1.1.0"
slash "^3.0.0"
-jest-runner@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.3.1.tgz#1d594dcbf3bd8600a7e839e790384559eaf96e3e"
- integrity sha512-r4W6kBn6sPr3TBwQNmqE94mPlYVn7fLBseeJfo4E2uCTmAyDFm2O5DYAQAFP7Q3YfiA/bMwg8TVsciP7k0xOww==
+jest-runner@^27.4.3:
+ version "27.4.3"
+ resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.3.tgz#9f05d4733829787778e8a143ade913834d0828dc"
+ integrity sha512-JgR6Om/j22Fd6ZUUIGTWNcCtuZVYbNrecb4k89W4UyFJoRtHpo2zMKWkmFFFJoqwWGrfrcPLnVBIgkJiTV3cyA==
dependencies:
- "@jest/console" "^27.3.1"
- "@jest/environment" "^27.3.1"
- "@jest/test-result" "^27.3.1"
- "@jest/transform" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/console" "^27.4.2"
+ "@jest/environment" "^27.4.2"
+ "@jest/test-result" "^27.4.2"
+ "@jest/transform" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
chalk "^4.0.0"
emittery "^0.8.1"
exit "^0.1.2"
graceful-fs "^4.2.4"
- jest-docblock "^27.0.6"
- jest-environment-jsdom "^27.3.1"
- jest-environment-node "^27.3.1"
- jest-haste-map "^27.3.1"
- jest-leak-detector "^27.3.1"
- jest-message-util "^27.3.1"
- jest-resolve "^27.3.1"
- jest-runtime "^27.3.1"
- jest-util "^27.3.1"
- jest-worker "^27.3.1"
+ jest-docblock "^27.4.0"
+ jest-environment-jsdom "^27.4.3"
+ jest-environment-node "^27.4.2"
+ jest-haste-map "^27.4.2"
+ jest-leak-detector "^27.4.2"
+ jest-message-util "^27.4.2"
+ jest-resolve "^27.4.2"
+ jest-runtime "^27.4.2"
+ jest-util "^27.4.2"
+ jest-worker "^27.4.2"
source-map-support "^0.5.6"
throat "^6.0.1"
-jest-runtime@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.3.1.tgz#80fa32eb85fe5af575865ddf379874777ee993d7"
- integrity sha512-qtO6VxPbS8umqhEDpjA4pqTkKQ1Hy4ZSi9mDVeE9Za7LKBo2LdW2jmT+Iod3XFaJqINikZQsn2wEi0j9wPRbLg==
+jest-runtime@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.2.tgz#d72da8a0e97366c16ad515a2c437191a72600d38"
+ integrity sha512-eqPgcBaUNaw6j8T5M+dnfAEh6MIrh2YmtskCr9sl50QYpD22Sg+QqHw3J3nmaLzVMbBtOMHFFxLF0Qx8MsZVFQ==
dependencies:
- "@jest/console" "^27.3.1"
- "@jest/environment" "^27.3.1"
- "@jest/globals" "^27.3.1"
- "@jest/source-map" "^27.0.6"
- "@jest/test-result" "^27.3.1"
- "@jest/transform" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/console" "^27.4.2"
+ "@jest/environment" "^27.4.2"
+ "@jest/globals" "^27.4.2"
+ "@jest/source-map" "^27.4.0"
+ "@jest/test-result" "^27.4.2"
+ "@jest/transform" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
cjs-module-lexer "^1.0.0"
@@ -6531,30 +6565,30 @@ jest-runtime@^27.3.1:
exit "^0.1.2"
glob "^7.1.3"
graceful-fs "^4.2.4"
- jest-haste-map "^27.3.1"
- jest-message-util "^27.3.1"
- jest-mock "^27.3.0"
- jest-regex-util "^27.0.6"
- jest-resolve "^27.3.1"
- jest-snapshot "^27.3.1"
- jest-util "^27.3.1"
- jest-validate "^27.3.1"
+ jest-haste-map "^27.4.2"
+ jest-message-util "^27.4.2"
+ jest-mock "^27.4.2"
+ jest-regex-util "^27.4.0"
+ jest-resolve "^27.4.2"
+ jest-snapshot "^27.4.2"
+ jest-util "^27.4.2"
+ jest-validate "^27.4.2"
slash "^3.0.0"
strip-bom "^4.0.0"
yargs "^16.2.0"
-jest-serializer@^27.0.6:
- version "27.0.6"
- resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.0.6.tgz#93a6c74e0132b81a2d54623251c46c498bb5bec1"
- integrity sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA==
+jest-serializer@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a"
+ integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==
dependencies:
"@types/node" "*"
graceful-fs "^4.2.4"
-jest-snapshot@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.3.1.tgz#1da5c0712a252d70917d46c037054f5918c49ee4"
- integrity sha512-APZyBvSgQgOT0XumwfFu7X3G5elj6TGhCBLbBdn3R1IzYustPGPE38F51dBWMQ8hRXa9je0vAdeVDtqHLvB6lg==
+jest-snapshot@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.2.tgz#bd1ea04a8fab402e5ab18b788809fa597ddff532"
+ integrity sha512-DI7lJlNIu6WSQ+esqhnJzEzU70+dV+cNjoF1c+j5FagWEd3KtOyZvVliAH0RWNQ6KSnAAnKSU0qxJ8UXOOhuUQ==
dependencies:
"@babel/core" "^7.7.2"
"@babel/generator" "^7.7.2"
@@ -6562,60 +6596,72 @@ jest-snapshot@^27.3.1:
"@babel/plugin-syntax-typescript" "^7.7.2"
"@babel/traverse" "^7.7.2"
"@babel/types" "^7.0.0"
- "@jest/transform" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/transform" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/babel__traverse" "^7.0.4"
"@types/prettier" "^2.1.5"
babel-preset-current-node-syntax "^1.0.0"
chalk "^4.0.0"
- expect "^27.3.1"
+ expect "^27.4.2"
graceful-fs "^4.2.4"
- jest-diff "^27.3.1"
- jest-get-type "^27.3.1"
- jest-haste-map "^27.3.1"
- jest-matcher-utils "^27.3.1"
- jest-message-util "^27.3.1"
- jest-resolve "^27.3.1"
- jest-util "^27.3.1"
+ jest-diff "^27.4.2"
+ jest-get-type "^27.4.0"
+ jest-haste-map "^27.4.2"
+ jest-matcher-utils "^27.4.2"
+ jest-message-util "^27.4.2"
+ jest-resolve "^27.4.2"
+ jest-util "^27.4.2"
natural-compare "^1.4.0"
- pretty-format "^27.3.1"
+ pretty-format "^27.4.2"
semver "^7.3.2"
-jest-util@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.3.1.tgz#a58cdc7b6c8a560caac9ed6bdfc4e4ff23f80429"
- integrity sha512-8fg+ifEH3GDryLQf/eKZck1DEs2YuVPBCMOaHQxVVLmQwl/CDhWzrvChTX4efLZxGrw+AA0mSXv78cyytBt/uw==
+jest-util@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.0.tgz#7b803e8a7da99728c7b1a7af74c33cb225df94d5"
+ integrity sha512-9HL5h/IWeg2u2dt0UIiseVRCnadh7CMPD4B9AeoEO23/NofaEfcPzIfl8dw45CpGHjP+xenw1viQYMd25DWquA==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.0"
"@types/node" "*"
chalk "^4.0.0"
ci-info "^3.2.0"
graceful-fs "^4.2.4"
picomatch "^2.2.3"
-jest-validate@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.3.1.tgz#3a395d61a19cd13ae9054af8cdaf299116ef8a24"
- integrity sha512-3H0XCHDFLA9uDII67Bwi1Vy7AqwA5HqEEjyy934lgVhtJ3eisw6ShOF1MDmRPspyikef5MyExvIm0/TuLzZ86Q==
+jest-util@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621"
+ integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ graceful-fs "^4.2.4"
+ picomatch "^2.2.3"
+
+jest-validate@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.4.2.tgz#eecfcc1b1c9429aa007da08a2bae4e32a81bbbc3"
+ integrity sha512-hWYsSUej+Fs8ZhOm5vhWzwSLmVaPAxRy+Mr+z5MzeaHm9AxUpXdoVMEW4R86y5gOobVfBsMFLk4Rb+QkiEpx1A==
+ dependencies:
+ "@jest/types" "^27.4.2"
camelcase "^6.2.0"
chalk "^4.0.0"
- jest-get-type "^27.3.1"
+ jest-get-type "^27.4.0"
leven "^3.1.0"
- pretty-format "^27.3.1"
+ pretty-format "^27.4.2"
-jest-watcher@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.3.1.tgz#ba5e0bc6aa843612b54ddb7f009d1cbff7e05f3e"
- integrity sha512-9/xbV6chABsGHWh9yPaAGYVVKurWoP3ZMCv6h+O1v9/+pkOroigs6WzZ0e9gLP/njokUwM7yQhr01LKJVMkaZA==
+jest-watcher@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.4.2.tgz#c9037edfd80354c9fe90de4b6f8b6e2b8e736744"
+ integrity sha512-NJvMVyyBeXfDezhWzUOCOYZrUmkSCiatpjpm+nFUid74OZEHk6aMLrZAukIiFDwdbqp6mTM6Ui1w4oc+8EobQg==
dependencies:
- "@jest/test-result" "^27.3.1"
- "@jest/types" "^27.2.5"
+ "@jest/test-result" "^27.4.2"
+ "@jest/types" "^27.4.2"
"@types/node" "*"
ansi-escapes "^4.2.1"
chalk "^4.0.0"
- jest-util "^27.3.1"
+ jest-util "^27.4.2"
string-length "^4.0.1"
jest-worker@^26.5.0:
@@ -6627,23 +6673,32 @@ jest-worker@^26.5.0:
merge-stream "^2.0.0"
supports-color "^7.0.0"
-jest-worker@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.3.1.tgz#0def7feae5b8042be38479799aeb7b5facac24b2"
- integrity sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==
+jest-worker@^27.4.0:
+ version "27.4.0"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.0.tgz#fa10dddc611cbb47a4153543dd16a0c7e7fd745c"
+ integrity sha512-4WuKcUxtzxBoKOUFbt1MtTY9fJwPVD4aN/4Cgxee7OLetPZn5as2bjfZz98XSf2Zq1JFfhqPZpS+43BmWXKgCA==
dependencies:
"@types/node" "*"
merge-stream "^2.0.0"
supports-color "^8.0.0"
-jest@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/jest/-/jest-27.3.1.tgz#b5bab64e8f56b6f7e275ba1836898b0d9f1e5c8a"
- integrity sha512-U2AX0AgQGd5EzMsiZpYt8HyZ+nSVIh5ujQ9CPp9EQZJMjXIiSZpJNweZl0swatKRoqHWgGKM3zaSwm4Zaz87ng==
+jest-worker@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.2.tgz#0fb123d50955af1a450267787f340a1bf7e12bc4"
+ integrity sha512-0QMy/zPovLfUPyHuOuuU4E+kGACXXE84nRnq6lBVI9GJg5DCBiA97SATi+ZP8CpiJwEQy1oCPjRBf8AnLjN+Ag==
dependencies:
- "@jest/core" "^27.3.1"
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
+jest@^27.4.3:
+ version "27.4.3"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.3.tgz#cf7d1876a84c70efece2e01e4f9dfc2e464d9cbb"
+ integrity sha512-jwsfVABBzuN3Atm+6h6vIEpTs9+VApODLt4dk2qv1WMOpb1weI1IIZfuwpMiWZ62qvWj78MvdvMHIYdUfqrFaA==
+ dependencies:
+ "@jest/core" "^27.4.3"
import-local "^3.0.2"
- jest-cli "^27.3.1"
+ jest-cli "^27.4.3"
js-base64@^2.1.9:
version "2.6.4"
@@ -8603,12 +8658,12 @@ pretty-format@^27.0.2:
ansi-styles "^5.0.0"
react-is "^17.0.1"
-pretty-format@^27.3.1:
- version "27.3.1"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.3.1.tgz#7e9486365ccdd4a502061fa761d3ab9ca1b78df5"
- integrity sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA==
+pretty-format@^27.4.2:
+ version "27.4.2"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.2.tgz#e4ce92ad66c3888423d332b40477c87d1dac1fb8"
+ integrity sha512-p0wNtJ9oLuvgOQDEIZ9zQjZffK7KtyR6Si0jnXULIDwrlNF8Cuir3AZP0hHv0jmKuNN/edOnbMjnzd4uTcmWiw==
dependencies:
- "@jest/types" "^27.2.5"
+ "@jest/types" "^27.4.2"
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
react-is "^17.0.1"
@@ -9177,10 +9232,10 @@ redux-immutable@^4.0.0:
resolved "https://registry.yarnpkg.com/redux-immutable/-/redux-immutable-4.0.0.tgz#3a1a32df66366462b63691f0e1dc35e472bbc9f3"
integrity sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=
-redux-thunk@^2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.0.tgz#ac89e1d6b9bdb9ee49ce69a69071be41bbd82d67"
- integrity sha512-/y6ZKQNU/0u8Bm7ROLq9Pt/7lU93cT0IucYMrubo89ENjxPa7i8pqLKu6V4X7/TvYovQ6x01unTeyeZ9lgXiTA==
+redux-thunk@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
+ integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==
redux@^4.0.0, redux@^4.1.2:
version "4.1.2"
@@ -9334,10 +9389,10 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
-reselect@^4.1.4:
- version "4.1.4"
- resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.4.tgz#66df0aff41b6ee0f51e2cc17cfaf2c1995916f32"
- integrity sha512-i1LgXw8DKSU5qz1EV0ZIKz4yIUHJ7L3bODh+Da6HmVSm9vdL/hG7IpbgzQ3k2XSirzf8/eI7OMEs81gb1VV2fQ==
+reselect@^4.1.5:
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6"
+ integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==
resolve-cwd@^2.0.0:
version "2.0.0"
@@ -9546,10 +9601,10 @@ sass-loader@^10.2.0:
schema-utils "^3.0.0"
semver "^7.3.2"
-sass@^1.43.4:
- version "1.43.4"
- resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.4.tgz#68c7d6a1b004bef49af0d9caf750e9b252105d1f"
- integrity sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==
+sass@^1.43.5:
+ version "1.43.5"
+ resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.5.tgz#25a9d91dd098793ef7229d7b04dd3daae2fc4a65"
+ integrity sha512-WuNm+eAryMgQluL7Mbq9M4EruyGGMyal7Lu58FfnRMVWxgUzIvI7aSn60iNt3kn5yZBMR7G84fAGDcwqOF5JOg==
dependencies:
chokidar ">=3.0.0 <4.0.0"
@@ -11353,10 +11408,10 @@ ws@^7.3.1, ws@^7.4.5:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
-ws@^8.2.3:
- version "8.2.3"
- resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
- integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
+ws@^8.3.0:
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d"
+ integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==
xml-name-validator@^3.0.0:
version "3.0.0"