1
0
Fork 0
forked from gitea/nas

Merge commit '14bb6bb29a' into kb_migration

This commit is contained in:
KMY 2023-05-31 12:34:11 +09:00
commit 5b8ca44e3d
20 changed files with 726 additions and 169 deletions

View file

@ -896,7 +896,6 @@ Rails/WhereExists:
- 'app/validators/vote_validator.rb'
- 'app/workers/move_worker.rb'
- 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
- 'lib/mastodon/cli/email_domain_blocks.rb'
- 'lib/tasks/tests.rake'
- 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
- 'spec/controllers/api/v1/tags_controller_spec.rb'
@ -958,7 +957,6 @@ Style/FormatStringToken:
Exclude:
- 'app/models/privacy_policy.rb'
- 'config/initializers/devise.rb'
- 'lib/mastodon/cli/maintenance.rb'
- 'lib/paperclip/color_extractor.rb'
# This cop supports unsafe autocorrection (--autocorrect-all).

View file

@ -59,7 +59,7 @@ gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.14'
gem 'nokogiri', '~> 1.15'
gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14'

View file

@ -439,8 +439,8 @@ GEM
net-protocol
net-ssh (7.1.0)
nio4r (2.5.9)
nokogiri (1.14.3)
mini_portile2 (~> 2.8.0)
nokogiri (1.15.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nsa (0.2.8)
activesupport (>= 4.2, < 7)
@ -642,7 +642,7 @@ GEM
activerecord (>= 4.0.0)
railties (>= 4.0.0)
semantic_range (3.0.0)
sidekiq (6.5.8)
sidekiq (6.5.9)
connection_pool (>= 2.2.5, < 3)
rack (~> 2.0)
redis (>= 4.5.0, < 5)
@ -829,7 +829,7 @@ DEPENDENCIES
mime-types (~> 3.4.1)
net-http (~> 0.3.2)
net-ldap (~> 0.18)
nokogiri (~> 1.14)
nokogiri (~> 1.15)
nsa (~> 0.2)
oj (~> 3.14)
omniauth (~> 1.9)

View file

@ -13,7 +13,7 @@ import { registrationsOpen } from 'mastodon/initial_state';
const mapStateToProps = (state, { accountId }) => ({
displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']),
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up') || '/auth/sign_up',
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
});
const mapDispatchToProps = (dispatch) => ({

View file

@ -168,8 +168,9 @@ const makeMapStateToProps = () => {
};
const truncate = (str, num) => {
if (str.length > num) {
return str.slice(0, num) + '…';
const arr = Array.from(str);
if (arr.length > num) {
return arr.slice(0, num).join('') + '…';
} else {
return str;
}

View file

@ -17,7 +17,7 @@ const SignInBanner = () => {
let signupButton;
const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up') || '/auth/sign_up');
const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up');
if (registrationsOpen) {
signupButton = (

View file

@ -19,7 +19,7 @@ class FetchResourceService < BaseService
private
def process(url, terminal = false)
def process(url, terminal: false)
@url = url
perform_request { |response| process_response(response, terminal) }

View file

@ -113,12 +113,7 @@ module Mastodon::CLI
say('OK', :green)
say("New password: #{password}")
else
user.errors.each do |error|
say('Failure/Error: ', :red)
say(error.attribute)
say(" #{error.type}", :red)
end
report_errors(user.errors)
exit(1)
end
end
@ -189,12 +184,7 @@ module Mastodon::CLI
say('OK', :green)
say("New password: #{password}") if options[:reset_password]
else
user.errors.each do |error|
say('Failure/Error: ', :red)
say(error.attribute)
say(" #{error.type}", :red)
end
report_errors(user.errors)
exit(1)
end
end
@ -217,7 +207,6 @@ module Mastodon::CLI
exit(1)
end
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
account = nil
if username.present?
@ -234,9 +223,9 @@ module Mastodon::CLI
end
end
say("Deleting user with #{account.statuses_count} statuses, this might take a while...#{dry_run}")
DeleteAccountService.new.call(account, reserve_email: false) unless options[:dry_run]
say("OK#{dry_run}", :green)
say("Deleting user with #{account.statuses_count} statuses, this might take a while...#{dry_run_mode_suffix}")
DeleteAccountService.new.call(account, reserve_email: false) unless dry_run?
say("OK#{dry_run_mode_suffix}", :green)
end
option :force, type: :boolean, aliases: [:f], description: 'Override public key check'
@ -291,7 +280,7 @@ module Mastodon::CLI
Account.remote.select(:uri, 'count(*)').group(:uri).having('count(*) > 1').pluck(:uri).each do |uri|
say("Duplicates found for #{uri}")
begin
ActivityPub::FetchRemoteAccountService.new.call(uri) unless options[:dry_run]
ActivityPub::FetchRemoteAccountService.new.call(uri) unless dry_run?
rescue => e
say("Error processing #{uri}: #{e}", :red)
end
@ -332,7 +321,6 @@ module Mastodon::CLI
LONG_DESC
def cull(*domains)
skip_threshold = 7.days.ago
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
skip_domains = Concurrent::Set.new
query = Account.remote.where(protocol: :activitypub)
@ -350,7 +338,7 @@ module Mastodon::CLI
end
if [404, 410].include?(code)
DeleteAccountService.new.call(account, reserve_username: false) unless options[:dry_run]
DeleteAccountService.new.call(account, reserve_username: false) unless dry_run?
1
else
# Touch account even during dry run to avoid getting the account into the window again
@ -358,7 +346,7 @@ module Mastodon::CLI
end
end
say("Visited #{processed} accounts, removed #{culled}#{dry_run}", :green)
say("Visited #{processed} accounts, removed #{culled}#{dry_run_mode_suffix}", :green)
unless skip_domains.empty?
say('The following domains were not available during the check:', :yellow)
@ -381,21 +369,19 @@ module Mastodon::CLI
specified with space-separated USERNAMES.
LONG_DESC
def refresh(*usernames)
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
if options[:domain] || options[:all]
scope = Account.remote
scope = scope.where(domain: options[:domain]) if options[:domain]
processed, = parallelize_with_progress(scope) do |account|
next if options[:dry_run]
next if dry_run?
account.reset_avatar!
account.reset_header!
account.save
end
say("Refreshed #{processed} accounts#{dry_run}", :green, true)
say("Refreshed #{processed} accounts#{dry_run_mode_suffix}", :green, true)
elsif !usernames.empty?
usernames.each do |user|
user, domain = user.split('@')
@ -406,7 +392,7 @@ module Mastodon::CLI
exit(1)
end
next if options[:dry_run]
next if dry_run?
begin
account.reset_avatar!
@ -417,7 +403,7 @@ module Mastodon::CLI
end
end
say("OK#{dry_run}", :green)
say("OK#{dry_run_mode_suffix}", :green)
else
say('No account(s) given', :red)
exit(1)
@ -568,8 +554,6 @@ module Mastodon::CLI
- not muted/blocked by us
LONG_DESC
def prune
dry_run = options[:dry_run] ? ' (dry run)' : ''
query = Account.remote.where.not(actor_type: %i(Application Service))
query = query.where('NOT EXISTS (SELECT 1 FROM mentions WHERE account_id = accounts.id)')
query = query.where('NOT EXISTS (SELECT 1 FROM favourites WHERE account_id = accounts.id)')
@ -585,11 +569,11 @@ module Mastodon::CLI
next if account.suspended?
next if account.silenced?
account.destroy unless options[:dry_run]
account.destroy unless dry_run?
1
end
say("OK, pruned #{deleted} accounts#{dry_run}", :green)
say("OK, pruned #{deleted} accounts#{dry_run_mode_suffix}", :green)
end
option :force, type: :boolean
@ -667,6 +651,14 @@ module Mastodon::CLI
private
def report_errors(errors)
errors.each do |error|
say('Failure/Error: ', :red)
say(error.attribute)
say(" #{error.type}", :red)
end
end
def rotate_keys_for_account(account, delay = 0)
if account.nil?
say('No such account', :red)

View file

@ -34,7 +34,6 @@ module Mastodon::CLI
When the --purge-domain-blocks option is given, also purge matching domain blocks.
LONG_DESC
def purge(*domains)
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
domains = domains.map { |domain| TagManager.instance.normalize_domain(domain) }
account_scope = Account.none
domain_block_scope = DomainBlock.none
@ -79,23 +78,23 @@ module Mastodon::CLI
# Actually perform the deletions
processed, = parallelize_with_progress(account_scope) do |account|
DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) unless options[:dry_run]
DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) unless dry_run?
end
say("Removed #{processed} accounts#{dry_run}", :green)
say("Removed #{processed} accounts#{dry_run_mode_suffix}", :green)
if options[:purge_domain_blocks]
domain_block_count = domain_block_scope.count
domain_block_scope.in_batches.destroy_all unless options[:dry_run]
say("Removed #{domain_block_count} domain blocks#{dry_run}", :green)
domain_block_scope.in_batches.destroy_all unless dry_run?
say("Removed #{domain_block_count} domain blocks#{dry_run_mode_suffix}", :green)
end
custom_emojis_count = emoji_scope.count
emoji_scope.in_batches.destroy_all unless options[:dry_run]
emoji_scope.in_batches.destroy_all unless dry_run?
Instance.refresh unless options[:dry_run]
Instance.refresh unless dry_run?
say("Removed #{custom_emojis_count} custom emojis#{dry_run}", :green)
say("Removed #{custom_emojis_count} custom emojis#{dry_run_mode_suffix}", :green)
end
option :concurrency, type: :numeric, default: 50, aliases: [:c]

View file

@ -39,7 +39,7 @@ module Mastodon::CLI
processed = 0
domains.each do |domain|
if EmailDomainBlock.where(domain: domain).exists?
if EmailDomainBlock.exists?(domain: domain)
say("#{domain} is already blocked.", :yellow)
skipped += 1
next
@ -60,7 +60,7 @@ module Mastodon::CLI
(email_domain_block.other_domains || []).uniq.each do |hostname|
another_email_domain_block = EmailDomainBlock.new(domain: hostname, parent: email_domain_block)
if EmailDomainBlock.where(domain: hostname).exists?
if EmailDomainBlock.exists?(domain: hostname)
say("#{hostname} is already blocked.", :yellow)
skipped += 1
next

View file

@ -18,14 +18,12 @@ module Mastodon::CLI
Otherwise, a single user specified by USERNAME.
LONG_DESC
def build(username = nil)
dry_run = options[:dry_run] ? '(DRY RUN)' : ''
if options[:all] || username.nil?
processed, = parallelize_with_progress(Account.joins(:user).merge(User.active)) do |account|
PrecomputeFeedService.new.call(account) unless options[:dry_run]
PrecomputeFeedService.new.call(account) unless dry_run?
end
say("Regenerated feeds for #{processed} accounts #{dry_run}", :green, true)
say("Regenerated feeds for #{processed} accounts #{dry_run_mode_suffix}", :green, true)
elsif username.present?
account = Account.find_local(username)
@ -34,9 +32,9 @@ module Mastodon::CLI
exit(1)
end
PrecomputeFeedService.new.call(account) unless options[:dry_run]
PrecomputeFeedService.new.call(account) unless dry_run?
say("OK #{dry_run}", :green, true)
say("OK #{dry_run_mode_suffix}", :green, true)
else
say('No account(s) given', :red)
exit(1)

View file

@ -15,6 +15,10 @@ module Mastodon::CLI
options[:dry_run]
end
def dry_run_mode_suffix
dry_run? ? ' (DRY RUN)' : ''
end
def create_progress_bar(total = nil)
ProgressBar.create(total: total, format: '%c/%u |%b%i| %e')
end

View file

@ -94,7 +94,7 @@ module Mastodon::CLI
exit(1) unless prompt.ask('Type in the domain of the server to confirm:', required: true) == Rails.configuration.x.local_domain
unless options[:dry_run]
unless dry_run?
prompt.warn('This operation WILL NOT be reversible. It can also take a long time.')
prompt.warn('While the data won\'t be erased locally, the server will be in a BROKEN STATE afterwards.')
prompt.warn('A running Sidekiq process is required. Do not shut it down until queues clear.')
@ -104,12 +104,11 @@ module Mastodon::CLI
inboxes = Account.inboxes
processed = 0
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
Setting.registrations_mode = 'none' unless options[:dry_run]
Setting.registrations_mode = 'none' unless dry_run?
if inboxes.empty?
Account.local.without_suspended.in_batches.update_all(suspended_at: Time.now.utc, suspension_origin: :local) unless options[:dry_run]
Account.local.without_suspended.in_batches.update_all(suspended_at: Time.now.utc, suspension_origin: :local) unless dry_run?
prompt.ok('It seems like your server has not federated with anything')
prompt.ok('You can shut it down and delete it any time')
return
@ -126,7 +125,7 @@ module Mastodon::CLI
json = Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(account))
unless options[:dry_run]
unless dry_run?
ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
[json, account.id, inbox_url]
end
@ -140,7 +139,7 @@ module Mastodon::CLI
Account.local.without_suspended.find_each { |account| delete_account.call(account) }
Account.local.suspended.joins(:deletion_request).find_each { |account| delete_account.call(account) }
prompt.ok("Queued #{inboxes.size * processed} items into Sidekiq for #{processed} accounts#{dry_run}")
prompt.ok("Queued #{inboxes.size * processed} items into Sidekiq for #{processed} accounts#{dry_run_mode_suffix}")
prompt.ok('Wait until Sidekiq processes all items, then you can shut everything down and delete the data')
rescue TTY::Reader::InputInterrupt
exit(1)

View file

@ -225,9 +225,8 @@ module Mastodon::CLI
@prompt.warn "e-mail will be disabled for the following accounts: #{user.map(&:account).map(&:acct).join(', ')}"
@prompt.warn 'Please reach out to them and set another address with `tootctl account modify` or delete them.'
i = 0
users.each do |user|
user.update!(email: "#{i} " + user.email)
users.each_with_index do |user, index|
user.update!(email: "#{index} " + user.email)
end
end
@ -552,7 +551,16 @@ module Mastodon::CLI
@prompt.warn 'All those accounts are distinct accounts but only the most recently-created one is fully-functional.'
accounts.each_with_index do |account, idx|
@prompt.say format('%2d. %s: created at: %s; updated at: %s; last logged in at: %s; statuses: %5d; last status at: %s', idx, account.username, account.created_at, account.updated_at, account.user&.last_sign_in_at&.to_s || 'N/A', account.account_stat&.statuses_count || 0, account.account_stat&.last_status_at || 'N/A')
@prompt.say format(
'%<index>2d. %<username>s: created at: %<created_at>s; updated at: %<updated_at>s; last logged in at: %<last_log_in_at>s; statuses: %<status_count>5d; last status at: %<last_status_at>s',
index: idx,
username: account.username,
created_at: account.created_at,
updated_at: account.updated_at,
last_log_in_at: account.user&.last_sign_in_at&.to_s || 'N/A',
status_count: account.account_stat&.statuses_count || 0,
last_status_at: account.account_stat&.last_status_at || 'N/A'
)
end
@prompt.say 'Please chose the one to keep unchanged, other ones will be automatically renamed.'

View file

@ -35,12 +35,12 @@ module Mastodon::CLI
say('--prune-profiles and --remove-headers should not be specified simultaneously', :red, true)
exit(1)
end
if options[:include_follows] && !(options[:prune_profiles] || options[:remove_headers])
say('--include-follows can only be used with --prune-profiles or --remove-headers', :red, true)
exit(1)
end
time_ago = options[:days].days.ago
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
if options[:prune_profiles] || options[:remove_headers]
processed, aggregate = parallelize_with_progress(Account.remote.where({ last_webfingered_at: ..time_ago, updated_at: ..time_ago })) do |account|
@ -51,7 +51,7 @@ module Mastodon::CLI
size = (account.header_file_size || 0)
size += (account.avatar_file_size || 0) if options[:prune_profiles]
unless options[:dry_run]
unless dry_run?
account.header.destroy
account.avatar.destroy if options[:prune_profiles]
account.save!
@ -60,7 +60,7 @@ module Mastodon::CLI
size
end
say("Visited #{processed} accounts and removed profile media totaling #{number_to_human_size(aggregate)}#{dry_run}", :green, true)
say("Visited #{processed} accounts and removed profile media totaling #{number_to_human_size(aggregate)}#{dry_run_mode_suffix}", :green, true)
end
unless options[:prune_profiles] || options[:remove_headers]
@ -69,7 +69,7 @@ module Mastodon::CLI
size = (media_attachment.file_file_size || 0) + (media_attachment.thumbnail_file_size || 0)
unless options[:dry_run]
unless dry_run?
media_attachment.file.destroy
media_attachment.thumbnail.destroy
media_attachment.save
@ -78,7 +78,7 @@ module Mastodon::CLI
size
end
say("Removed #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
say("Removed #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run_mode_suffix}", :green, true)
end
end
@ -97,7 +97,6 @@ module Mastodon::CLI
progress = create_progress_bar(nil)
reclaimed_bytes = 0
removed = 0
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
prefix = options[:prefix]
case Paperclip::Attachment.default_options[:storage]
@ -123,7 +122,7 @@ module Mastodon::CLI
record_map = preload_records_from_mixed_objects(objects)
objects.each do |object|
object.acl.put(acl: s3_permissions) if options[:fix_permissions] && !options[:dry_run]
object.acl.put(acl: s3_permissions) if options[:fix_permissions] && !dry_run?
path_segments = object.key.split('/')
path_segments.delete('cache')
@ -145,7 +144,7 @@ module Mastodon::CLI
next unless attachment.blank? || !attachment.variant?(file_name)
begin
object.delete unless options[:dry_run]
object.delete unless dry_run?
reclaimed_bytes += object.size
removed += 1
@ -194,7 +193,7 @@ module Mastodon::CLI
begin
size = File.size(path)
unless options[:dry_run]
unless dry_run?
File.delete(path)
begin
FileUtils.rmdir(File.dirname(path), parents: true)
@ -216,7 +215,7 @@ module Mastodon::CLI
progress.total = progress.progress
progress.finish
say("Removed #{removed} orphans (approx. #{number_to_human_size(reclaimed_bytes)})#{dry_run}", :green, true)
say("Removed #{removed} orphans (approx. #{number_to_human_size(reclaimed_bytes)})#{dry_run_mode_suffix}", :green, true)
end
option :account, type: :string
@ -246,8 +245,6 @@ module Mastodon::CLI
not be re-downloaded. To force re-download of every URL, use --force.
DESC
def refresh
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
if options[:status]
scope = MediaAttachment.where(status_id: options[:status])
elsif options[:account]
@ -274,7 +271,7 @@ module Mastodon::CLI
next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?)
next if DomainBlock.reject_media?(media_attachment.account.domain)
unless options[:dry_run]
unless dry_run?
media_attachment.reset_file!
media_attachment.reset_thumbnail!
media_attachment.save
@ -283,7 +280,7 @@ module Mastodon::CLI
media_attachment.file_file_size + (media_attachment.thumbnail_file_size || 0)
end
say("Downloaded #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
say("Downloaded #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run_mode_suffix}", :green, true)
end
desc 'usage', 'Calculate disk space consumed by Mastodon'

View file

@ -27,7 +27,6 @@ module Mastodon::CLI
DESC
def remove
time_ago = options[:days].days.ago
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
link = options[:link] ? 'link-type ' : ''
scope = PreviewCard.cached
scope = scope.where(type: :link) if options[:link]
@ -38,7 +37,7 @@ module Mastodon::CLI
size = preview_card.image_file_size
unless options[:dry_run]
unless dry_run?
preview_card.image.destroy
preview_card.save
end
@ -46,7 +45,7 @@ module Mastodon::CLI
size
end
say("Removed #{processed} #{link}preview cards (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
say("Removed #{processed} #{link}preview cards (approx. #{number_to_human_size(aggregate)})#{dry_run_mode_suffix}", :green, true)
end
end
end

View file

@ -17,7 +17,6 @@ module Mastodon::CLI
LONG_DESC
def storage_schema
progress = create_progress_bar(nil)
dry_run = dry_run? ? ' (DRY RUN)' : ''
records = 0
klasses = [
@ -69,7 +68,7 @@ module Mastodon::CLI
progress.total = progress.progress
progress.finish
say("Upgraded storage schema of #{records} records#{dry_run}", :green, true)
say("Upgraded storage schema of #{records} records#{dry_run_mode_suffix}", :green, true)
end
private

View file

@ -73,7 +73,7 @@
"intl-messageformat": "^2.2.0",
"intl-relativeformat": "^6.4.3",
"js-yaml": "^4.1.0",
"jsdom": "^22.0.0",
"jsdom": "^22.1.0",
"lodash": "^4.17.21",
"mark-loader": "^0.1.6",
"marky": "^1.2.5",
@ -83,7 +83,7 @@
"path-complete-extname": "^1.0.0",
"pg": "^8.5.0",
"pg-connection-string": "^2.6.0",
"postcss": "^8.4.23",
"postcss": "^8.4.24",
"postcss-loader": "^4.3.0",
"prop-types": "^15.8.1",
"punycode": "^2.3.0",
@ -150,18 +150,18 @@
"@types/intl": "^1.2.0",
"@types/jest": "^29.5.1",
"@types/js-yaml": "^4.0.5",
"@types/lodash": "^4.14.194",
"@types/lodash": "^4.14.195",
"@types/npmlog": "^4.1.4",
"@types/object-assign": "^4.0.30",
"@types/pg": "^8.6.6",
"@types/prop-types": "^15.7.5",
"@types/punycode": "^2.1.0",
"@types/react": "^18.0.26",
"@types/react": "^18.2.7",
"@types/react-dom": "^18.2.4",
"@types/react-helmet": "^6.1.6",
"@types/react-immutable-proptypes": "^2.1.0",
"@types/react-intl": "2.3.18",
"@types/react-motion": "^0.0.33",
"@types/react-motion": "^0.0.34",
"@types/react-overlays": "^3.1.0",
"@types/react-router-dom": "^5.3.3",
"@types/react-select": "^5.0.1",
@ -175,8 +175,8 @@
"@types/uuid": "^9.0.0",
"@types/webpack": "^4.41.33",
"@types/yargs": "^17.0.24",
"@typescript-eslint/eslint-plugin": "^5.59.7",
"@typescript-eslint/parser": "^5.59.7",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
"babel-jest": "^29.5.0",
"eslint": "^8.41.0",
"eslint-config-prettier": "^8.8.0",

View file

@ -4,9 +4,577 @@ require 'rails_helper'
require 'mastodon/cli/accounts'
describe Mastodon::CLI::Accounts do
let(:cli) { described_class.new }
describe '.exit_on_failure?' do
it 'returns true' do
expect(described_class.exit_on_failure?).to be true
end
end
describe '#create' do
shared_examples 'a new user with given email address and username' do
it 'creates a new user with the specified email address' do
cli.invoke(:create, arguments, options)
expect(User.find_by(email: options[:email])).to be_present
end
it 'creates a new local account with the specified username' do
cli.invoke(:create, arguments, options)
expect(Account.find_local('tootctl_username')).to be_present
end
it 'returns "OK" and newly generated password' do
allow(SecureRandom).to receive(:hex).and_return('test_password')
expect { cli.invoke(:create, arguments, options) }.to output(
a_string_including("OK\nNew password: test_password")
).to_stdout
end
end
context 'when required USERNAME and --email are provided' do
let(:arguments) { ['tootctl_username'] }
context 'with USERNAME and --email only' do
let(:options) { { email: 'tootctl@example.com' } }
it_behaves_like 'a new user with given email address and username'
context 'with invalid --email value' do
let(:options) { { email: 'invalid' } }
it 'exits with an error message' do
expect { cli.invoke(:create, arguments, options) }.to output(
a_string_including('Failure/Error: email')
).to_stdout
.and raise_error(SystemExit)
end
end
end
context 'with --confirmed option' do
let(:options) { { email: 'tootctl@example.com', confirmed: true } }
it_behaves_like 'a new user with given email address and username'
it 'creates a new user with confirmed status' do
cli.invoke(:create, arguments, options)
user = User.find_by(email: options[:email])
expect(user.confirmed?).to be(true)
end
end
context 'with --approve option' do
let(:options) { { email: 'tootctl@example.com', approve: true } }
before do
Form::AdminSettings.new(registrations_mode: 'approved').save
end
it_behaves_like 'a new user with given email address and username'
it 'creates a new user with approved status' do
cli.invoke(:create, arguments, options)
user = User.find_by(email: options[:email])
expect(user.approved?).to be(true)
end
end
context 'with --role option' do
context 'when role exists' do
let(:default_role) { Fabricate(:user_role) }
let(:options) { { email: 'tootctl@example.com', role: default_role.name } }
it_behaves_like 'a new user with given email address and username'
it 'creates a new user and assigns the specified role' do
cli.invoke(:create, arguments, options)
role = User.find_by(email: options[:email])&.role
expect(role.name).to eq(default_role.name)
end
end
context 'when role does not exist' do
let(:options) { { email: 'tootctl@example.com', role: '404' } }
it 'exits with an error message indicating the role name was not found' do
expect { cli.invoke(:create, arguments, options) }.to output(
a_string_including('Cannot find user role with that name')
).to_stdout
.and raise_error(SystemExit)
end
end
end
context 'with --reattach option' do
context "when account's user is present" do
let(:options) { { email: 'tootctl_new@example.com', reattach: true } }
let(:user) { Fabricate.build(:user, email: 'tootctl@example.com') }
before do
Fabricate(:account, username: 'tootctl_username', user: user)
end
it 'returns an error message indicating the username is already taken' do
expect { cli.invoke(:create, arguments, options) }.to output(
a_string_including("The chosen username is currently in use\nUse --force to reattach it anyway and delete the other user")
).to_stdout
end
context 'with --force option' do
let(:options) { { email: 'tootctl_new@example.com', reattach: true, force: true } }
it 'reattaches the account to the new user and deletes the previous user' do
cli.invoke(:create, arguments, options)
user = Account.find_local('tootctl_username')&.user
expect(user.email).to eq(options[:email])
end
end
end
context "when account's user is not present" do
let(:options) { { email: 'tootctl@example.com', reattach: true } }
before do
Fabricate(:account, username: 'tootctl_username', user: nil)
end
it_behaves_like 'a new user with given email address and username'
end
end
end
context 'when required --email option is not provided' do
let(:arguments) { ['tootctl_username'] }
it 'raises a required argument missing error (Thor::RequiredArgumentMissingError)' do
expect { cli.invoke(:create, arguments) }
.to raise_error(Thor::RequiredArgumentMissingError)
end
end
end
describe '#modify' do
context 'when the given username is not found' do
let(:arguments) { ['non_existent_username'] }
it 'exits with an error message indicating the user was not found' do
expect { cli.invoke(:modify, arguments) }.to output(
a_string_including('No user with such username')
).to_stdout
.and raise_error(SystemExit)
end
end
context 'when the given username is found' do
let(:user) { Fabricate(:user) }
let(:arguments) { [user.account.username] }
context 'when no option is provided' do
it 'returns a successful message' do
expect { cli.invoke(:modify, arguments) }.to output(
a_string_including('OK')
).to_stdout
end
it 'does not modify the user' do
cli.invoke(:modify, arguments)
expect(user).to eq(user.reload)
end
end
context 'with --role option' do
context 'when the given role is not found' do
let(:options) { { role: '404' } }
it 'exits with an error message indicating the role was not found' do
expect { cli.invoke(:modify, arguments, options) }.to output(
a_string_including('Cannot find user role with that name')
).to_stdout
.and raise_error(SystemExit)
end
end
context 'when the given role is found' do
let(:default_role) { Fabricate(:user_role) }
let(:options) { { role: default_role.name } }
it "updates the user's role to the specified role" do
cli.invoke(:modify, arguments, options)
role = user.reload.role
expect(role.name).to eq(default_role.name)
end
end
end
context 'with --remove-role option' do
let(:options) { { remove_role: true } }
let(:role) { Fabricate(:user_role) }
let(:user) { Fabricate(:user, role: role) }
it "removes the user's role successfully" do
cli.invoke(:modify, arguments, options)
role = user.reload.role
expect(role.name).to be_empty
end
end
context 'with --email option' do
let(:user) { Fabricate(:user, email: 'old_email@email.com') }
let(:options) { { email: 'new_email@email.com' } }
it "sets the user's unconfirmed email to the provided email address" do
cli.invoke(:modify, arguments, options)
expect(user.reload.unconfirmed_email).to eq(options[:email])
end
it "does not update the user's original email address" do
cli.invoke(:modify, arguments, options)
expect(user.reload.email).to eq('old_email@email.com')
end
context 'with --confirm option' do
let(:user) { Fabricate(:user, email: 'old_email@email.com', confirmed_at: nil) }
let(:options) { { email: 'new_email@email.com', confirm: true } }
it "updates the user's email address to the provided email" do
cli.invoke(:modify, arguments, options)
expect(user.reload.email).to eq(options[:email])
end
it "sets the user's email address as confirmed" do
cli.invoke(:modify, arguments, options)
expect(user.reload.confirmed?).to be(true)
end
end
end
context 'with --confirm option' do
let(:user) { Fabricate(:user, confirmed_at: nil) }
let(:options) { { confirm: true } }
it "confirms the user's email address" do
cli.invoke(:modify, arguments, options)
expect(user.reload.confirmed?).to be(true)
end
end
context 'with --approve option' do
let(:user) { Fabricate(:user, approved: false) }
let(:options) { { approve: true } }
before do
Form::AdminSettings.new(registrations_mode: 'approved').save
end
it 'approves the user' do
expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.approved }.from(false).to(true)
end
end
context 'with --disable option' do
let(:user) { Fabricate(:user, disabled: false) }
let(:options) { { disable: true } }
it 'disables the user' do
expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.disabled }.from(false).to(true)
end
end
context 'with --enable option' do
let(:user) { Fabricate(:user, disabled: true) }
let(:options) { { enable: true } }
it 'enables the user' do
expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.disabled }.from(true).to(false)
end
end
context 'with --reset-password option' do
let(:options) { { reset_password: true } }
it 'returns a new password for the user' do
allow(SecureRandom).to receive(:hex).and_return('new_password')
expect { cli.invoke(:modify, arguments, options) }.to output(
a_string_including('new_password')
).to_stdout
end
end
context 'with --disable-2fa option' do
let(:user) { Fabricate(:user, otp_required_for_login: true) }
let(:options) { { disable_2fa: true } }
it 'disables the two-factor authentication for the user' do
expect { cli.invoke(:modify, arguments, options) }.to change { user.reload.otp_required_for_login }.from(true).to(false)
end
end
context 'when provided data is invalid' do
let(:user) { Fabricate(:user) }
let(:options) { { email: 'invalid' } }
it 'exits with an error message' do
expect { cli.invoke(:modify, arguments, options) }.to output(
a_string_including('Failure/Error: email')
).to_stdout
.and raise_error(SystemExit)
end
end
end
end
describe '#delete' do
let(:account) { Fabricate(:account) }
let(:arguments) { [account.username] }
let(:options) { { email: account.user.email } }
let(:delete_account_service) { instance_double(DeleteAccountService) }
before do
allow(DeleteAccountService).to receive(:new).and_return(delete_account_service)
allow(delete_account_service).to receive(:call)
end
context 'when both username and --email are provided' do
it 'exits with an error message indicating that only one should be used' do
expect { cli.invoke(:delete, arguments, options) }.to output(
a_string_including('Use username or --email, not both')
).to_stdout
.and raise_error(SystemExit)
end
end
context 'when neither username nor --email are provided' do
it 'exits with an error message indicating that no username was provided' do
expect { cli.invoke(:delete) }.to output(
a_string_including('No username provided')
).to_stdout
.and raise_error(SystemExit)
end
end
context 'when username is provided' do
it 'deletes the specified user successfully' do
cli.invoke(:delete, arguments)
expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
end
context 'with --dry-run option' do
let(:options) { { dry_run: true } }
it 'does not delete the specified user' do
cli.invoke(:delete, arguments, options)
expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false)
end
it 'outputs a successful message in dry run mode' do
expect { cli.invoke(:delete, arguments, options) }.to output(
a_string_including('OK (DRY RUN)')
).to_stdout
end
end
context 'when the given username is not found' do
let(:arguments) { ['non_existent_username'] }
it 'exits with an error message indicating that no user was found' do
expect { cli.invoke(:delete, arguments) }.to output(
a_string_including('No user with such username')
).to_stdout
.and raise_error(SystemExit)
end
end
end
context 'when --email is provided' do
it 'deletes the specified user successfully' do
cli.invoke(:delete, nil, options)
expect(delete_account_service).to have_received(:call).with(account, reserve_email: false).once
end
context 'with --dry-run option' do
let(:options) { { email: account.user.email, dry_run: true } }
it 'does not delete the user' do
cli.invoke(:delete, nil, options)
expect(delete_account_service).to_not have_received(:call).with(account, reserve_email: false)
end
it 'outputs a successful message in dry run mode' do
expect { cli.invoke(:delete, nil, options) }.to output(
a_string_including('OK (DRY RUN)')
).to_stdout
end
end
context 'when the given email address is not found' do
let(:options) { { email: '404@example.com' } }
it 'exits with an error message indicating that no user was found' do
expect { cli.invoke(:delete, nil, options) }.to output(
a_string_including('No user with such email')
).to_stdout
.and raise_error(SystemExit)
end
end
end
end
describe '#approve' do
let(:total_users) { 10 }
before do
Form::AdminSettings.new(registrations_mode: 'approved').save
Fabricate.times(total_users, :user)
end
context 'with --all option' do
it 'approves all pending registrations' do
cli.invoke(:approve, nil, all: true)
expect(User.pluck(:approved).all?(true)).to be(true)
end
end
context 'with --number option' do
context 'when the number is positive' do
let(:options) { { number: 3 } }
it 'approves the earliest n pending registrations' do
cli.invoke(:approve, nil, options)
n_earliest_pending_registrations = User.order(created_at: :asc).first(options[:number])
expect(n_earliest_pending_registrations.all?(&:approved?)).to be(true)
end
it 'does not approve the remaining pending registrations' do
cli.invoke(:approve, nil, options)
pending_registrations = User.order(created_at: :asc).last(total_users - options[:number])
expect(pending_registrations.all?(&:approved?)).to be(false)
end
end
context 'when the number is negative' do
it 'exits with an error message indicating that the number must be positive' do
expect { cli.invoke(:approve, nil, number: -1) }.to output(
a_string_including('Number must be positive')
).to_stdout
.and raise_error(SystemExit)
end
end
context 'when the given number is greater than the number of users' do
let(:options) { { number: total_users * 2 } }
it 'approves all users' do
cli.invoke(:approve, nil, options)
expect(User.pluck(:approved).all?(true)).to be(true)
end
it 'does not raise any error' do
expect { cli.invoke(:approve, nil, options) }
.to_not raise_error
end
end
end
context 'with username argument' do
context 'when the given username is found' do
let(:user) { User.last }
let(:arguments) { [user.account.username] }
it 'approves the specified user successfully' do
cli.invoke(:approve, arguments)
expect(user.reload.approved?).to be(true)
end
end
context 'when the given username is not found' do
let(:arguments) { ['non_existent_username'] }
it 'exits with an error message indicating that no such account was found' do
expect { cli.invoke(:approve, arguments) }.to output(
a_string_including('No such account')
).to_stdout
.and raise_error(SystemExit)
end
end
end
end
describe '#follow' do
context 'when the given username is not found' do
let(:arguments) { ['non_existent_username'] }
it 'exits with an error message indicating that no account with the given username was found' do
expect { cli.invoke(:follow, arguments) }.to output(
a_string_including('No such account')
).to_stdout
.and raise_error(SystemExit)
end
end
context 'when the given username is found' do
let!(:target_account) { Fabricate(:account) }
let!(:follower_bob) { Fabricate(:account, username: 'bob') }
let!(:follower_rony) { Fabricate(:account, username: 'rony') }
let!(:follower_charles) { Fabricate(:account, username: 'charles') }
let(:follow_service) { instance_double(FollowService, call: nil) }
let(:scope) { Account.local.without_suspended }
before do
allow(cli).to receive(:parallelize_with_progress).and_yield(follower_bob)
.and_yield(follower_rony)
.and_yield(follower_charles)
.and_return([3, nil])
allow(FollowService).to receive(:new).and_return(follow_service)
end
it 'makes all local accounts follow the target account' do
cli.follow(target_account.username)
expect(cli).to have_received(:parallelize_with_progress).with(scope).once
expect(follow_service).to have_received(:call).with(follower_bob, target_account, any_args).once
expect(follow_service).to have_received(:call).with(follower_rony, target_account, any_args).once
expect(follow_service).to have_received(:call).with(follower_charles, target_account, any_args).once
end
it 'displays a successful message' do
expect { cli.follow(target_account.username) }.to output(
a_string_including('OK, followed target from 3 accounts')
).to_stdout
end
end
end
end

143
yarn.lock
View file

@ -2087,10 +2087,10 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/lodash@^4.14.194":
version "4.14.194"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76"
integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==
"@types/lodash@^4.14.195":
version "4.14.195"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632"
integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==
"@types/mime@*":
version "3.0.1"
@ -2156,12 +2156,7 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0"
integrity sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==
"@types/prop-types@*":
version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/prop-types@^15.7.5":
"@types/prop-types@*", "@types/prop-types@^15.7.5":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
@ -2208,10 +2203,10 @@
resolved "https://registry.yarnpkg.com/@types/react-intl/-/react-intl-2.3.18.tgz#fd2d8b7f4d0a1dd05b5f1784ab0d7fe1786a690d"
integrity sha512-DVNJs49zUxKRZng8VuILE886Yihdsf3yLr5vHk9zJrmF8SyRSK3sxNSvikAKxNkv9hX55XBTJShz6CkJnbNjgg==
"@types/react-motion@^0.0.33":
version "0.0.33"
resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.33.tgz#c156c400ace995584990344cc0239e41f411f425"
integrity sha512-R9grd4EwdDBcKKq7Zhszd8ukyy2BLKN6ooNI0V39nUl/sui+m7VI94cdebYemBteoPHmO7J7BZk+cIf+Xnk4TA==
"@types/react-motion@^0.0.34":
version "0.0.34"
resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.34.tgz#789ff2063e2f7fbb6085d291135c442e8b35291a"
integrity sha512-/rFI22Vg4Xzb47hXtS06WkzUGRu+Vb3yDleuxiqzGj0JbXYXQUCgwSa2ZU12K7ubKi4C8xsdIN3xt4Z4fjSdPw==
dependencies:
"@types/react" "*"
@ -2298,10 +2293,10 @@
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@>=16.9.11", "@types/react@^18.0.26":
version "18.2.6"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571"
integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==
"@types/react@*", "@types/react@>=16.9.11", "@types/react@^18.0.26", "@types/react@^18.2.7":
version "18.2.7"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.7.tgz#dfb4518042a3117a045b8c222316f83414a783b3"
integrity sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
@ -2444,15 +2439,15 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.7.tgz#e470af414f05ecfdc05a23e9ce6ec8f91db56fe2"
integrity sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==
"@typescript-eslint/eslint-plugin@^5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz#1e7a3e5318ece22251dfbc5c9c6feeb4793cc509"
integrity sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==
dependencies:
"@eslint-community/regexpp" "^4.4.0"
"@typescript-eslint/scope-manager" "5.59.7"
"@typescript-eslint/type-utils" "5.59.7"
"@typescript-eslint/utils" "5.59.7"
"@typescript-eslint/scope-manager" "5.59.8"
"@typescript-eslint/type-utils" "5.59.8"
"@typescript-eslint/utils" "5.59.8"
debug "^4.3.4"
grapheme-splitter "^1.0.4"
ignore "^5.2.0"
@ -2460,31 +2455,31 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/parser@^5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.7.tgz#02682554d7c1028b89aa44a48bf598db33048caa"
integrity sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==
"@typescript-eslint/parser@^5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.8.tgz#60cbb00671d86cf746044ab797900b1448188567"
integrity sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==
dependencies:
"@typescript-eslint/scope-manager" "5.59.7"
"@typescript-eslint/types" "5.59.7"
"@typescript-eslint/typescript-estree" "5.59.7"
"@typescript-eslint/scope-manager" "5.59.8"
"@typescript-eslint/types" "5.59.8"
"@typescript-eslint/typescript-estree" "5.59.8"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz#0243f41f9066f3339d2f06d7f72d6c16a16769e2"
integrity sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==
"@typescript-eslint/scope-manager@5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz#ff4ad4fec6433647b817c4a7d4b4165d18ea2fa8"
integrity sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==
dependencies:
"@typescript-eslint/types" "5.59.7"
"@typescript-eslint/visitor-keys" "5.59.7"
"@typescript-eslint/types" "5.59.8"
"@typescript-eslint/visitor-keys" "5.59.8"
"@typescript-eslint/type-utils@5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.7.tgz#89c97291371b59eb18a68039857c829776f1426d"
integrity sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==
"@typescript-eslint/type-utils@5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz#aa6c029a9d7706d26bbd25eb4666398781df6ea2"
integrity sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==
dependencies:
"@typescript-eslint/typescript-estree" "5.59.7"
"@typescript-eslint/utils" "5.59.7"
"@typescript-eslint/typescript-estree" "5.59.8"
"@typescript-eslint/utils" "5.59.8"
debug "^4.3.4"
tsutils "^3.21.0"
@ -2493,10 +2488,10 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32"
integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==
"@typescript-eslint/types@5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.7.tgz#6f4857203fceee91d0034ccc30512d2939000742"
integrity sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==
"@typescript-eslint/types@5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.8.tgz#212e54414733618f5d0fd50b2da2717f630aebf8"
integrity sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==
"@typescript-eslint/typescript-estree@5.59.0":
version "5.59.0"
@ -2511,30 +2506,30 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz#b887acbd4b58e654829c94860dbff4ac55c5cff8"
integrity sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==
"@typescript-eslint/typescript-estree@5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz#801a7b1766481629481b3b0878148bd7a1f345d7"
integrity sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==
dependencies:
"@typescript-eslint/types" "5.59.7"
"@typescript-eslint/visitor-keys" "5.59.7"
"@typescript-eslint/types" "5.59.8"
"@typescript-eslint/visitor-keys" "5.59.8"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/utils@5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.7.tgz#7adf068b136deae54abd9a66ba5a8780d2d0f898"
integrity sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==
"@typescript-eslint/utils@5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.8.tgz#34d129f35a2134c67fdaf024941e8f96050dca2b"
integrity sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@types/json-schema" "^7.0.9"
"@types/semver" "^7.3.12"
"@typescript-eslint/scope-manager" "5.59.7"
"@typescript-eslint/types" "5.59.7"
"@typescript-eslint/typescript-estree" "5.59.7"
"@typescript-eslint/scope-manager" "5.59.8"
"@typescript-eslint/types" "5.59.8"
"@typescript-eslint/typescript-estree" "5.59.8"
eslint-scope "^5.1.1"
semver "^7.3.7"
@ -2546,12 +2541,12 @@
"@typescript-eslint/types" "5.59.0"
eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@5.59.7":
version "5.59.7"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz#09c36eaf268086b4fbb5eb9dc5199391b6485fc5"
integrity sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==
"@typescript-eslint/visitor-keys@5.59.8":
version "5.59.8"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz#aa6a7ef862add919401470c09e1609392ef3cc40"
integrity sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==
dependencies:
"@typescript-eslint/types" "5.59.7"
"@typescript-eslint/types" "5.59.8"
eslint-visitor-keys "^3.3.0"
"@webassemblyjs/ast@1.9.0":
@ -7477,10 +7472,10 @@ jsdom@^20.0.0:
ws "^8.11.0"
xml-name-validator "^4.0.0"
jsdom@^22.0.0:
version "22.0.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.0.0.tgz#3295c6992c70089c4b8f5cf060489fddf7ee9816"
integrity sha512-p5ZTEb5h+O+iU02t0GfEjAnkdYPrQSkfuTSMkMYyIoMvUNEHsbG0bHHbfXIcfTqD2UfvjQX7mmgiFsyRwGscVw==
jsdom@^22.1.0:
version "22.1.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8"
integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==
dependencies:
abab "^2.0.6"
cssstyle "^3.0.0"
@ -9263,10 +9258,10 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.2.15, postcss@^8.4.23:
version "8.4.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
postcss@^8.2.15, postcss@^8.4.23, postcss@^8.4.24:
version "8.4.24"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df"
integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==
dependencies:
nanoid "^3.3.6"
picocolors "^1.0.0"