diff --git a/Gemfile b/Gemfile
index 51308c24d..930c5352c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,7 +5,7 @@ ruby '>= 3.0.0'
gem 'pkg-config', '~> 1.5'
-gem 'puma', '~> 6.2'
+gem 'puma', '~> 6.3'
gem 'rails', '~> 6.1.7'
gem 'sprockets', '~> 3.7.2'
gem 'thor', '~> 1.2'
@@ -17,7 +17,7 @@ gem 'makara', '~> 0.5'
gem 'pghero'
gem 'dotenv-rails', '~> 2.8'
-gem 'aws-sdk-s3', '~> 1.122', require: false
+gem 'aws-sdk-s3', '~> 1.123', require: false
gem 'fog-core', '<= 2.4.0'
gem 'fog-openstack', '~> 0.3', require: false
gem 'kt-paperclip', '~> 7.1', github: 'kreeti/kt-paperclip', ref: '11abf222dc31bff71160a1d138b445214f434b2b'
diff --git a/Gemfile.lock b/Gemfile.lock
index 36f7d7201..7d04d875c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -109,17 +109,17 @@ GEM
attr_required (1.0.1)
awrence (1.2.1)
aws-eventstream (1.2.0)
- aws-partitions (1.761.0)
- aws-sdk-core (3.172.0)
+ aws-partitions (1.772.0)
+ aws-sdk-core (3.174.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.64.0)
- aws-sdk-core (~> 3, >= 3.165.0)
+ aws-sdk-kms (1.65.0)
+ aws-sdk-core (~> 3, >= 3.174.0)
aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.122.0)
- aws-sdk-core (~> 3, >= 3.165.0)
+ aws-sdk-s3 (1.123.0)
+ aws-sdk-core (~> 3, >= 3.174.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.2)
@@ -501,7 +501,7 @@ GEM
premailer (~> 1.7, >= 1.7.9)
private_address_check (0.5.0)
public_suffix (5.0.1)
- puma (6.2.2)
+ puma (6.3.0)
nio4r (~> 2.0)
pundit (2.3.0)
activesupport (>= 3.0.0)
@@ -544,8 +544,9 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
- rails-html-sanitizer (1.5.0)
- loofah (~> 2.19, >= 2.19.1)
+ rails-html-sanitizer (1.6.0)
+ loofah (~> 2.21)
+ nokogiri (~> 1.14)
rails-i18n (6.0.0)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7)
@@ -588,7 +589,7 @@ GEM
rspec-mocks (3.12.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
- rspec-rails (6.0.2)
+ rspec-rails (6.0.3)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
@@ -648,7 +649,7 @@ GEM
redis (>= 4.5.0, < 5)
sidekiq-bulk (0.2.0)
sidekiq
- sidekiq-scheduler (5.0.2)
+ sidekiq-scheduler (5.0.3)
rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8)
tilt (>= 1.4.0)
@@ -770,7 +771,7 @@ DEPENDENCIES
active_model_serializers (~> 0.10)
addressable (~> 2.8)
annotate (~> 3.2)
- aws-sdk-s3 (~> 1.122)
+ aws-sdk-s3 (~> 1.123)
better_errors (~> 2.9)
binding_of_caller (~> 1.0)
blurhash (~> 0.1)
@@ -846,7 +847,7 @@ DEPENDENCIES
premailer-rails
private_address_check (~> 0.5)
public_suffix (~> 5.0)
- puma (~> 6.2)
+ puma (~> 6.3)
pundit (~> 2.3)
rack (~> 2.2.7)
rack-attack (~> 6.6)
diff --git a/app/controllers/settings/imports_controller.rb b/app/controllers/settings/imports_controller.rb
index bdbf8796f..983caf22f 100644
--- a/app/controllers/settings/imports_controller.rb
+++ b/app/controllers/settings/imports_controller.rb
@@ -12,6 +12,7 @@ class Settings::ImportsController < Settings::BaseController
muting: 'muted_accounts_failures.csv',
domain_blocking: 'blocked_domains_failures.csv',
bookmarks: 'bookmarks_failures.csv',
+ lists: 'lists_failures.csv',
}.freeze
TYPE_TO_HEADERS_MAP = {
@@ -20,6 +21,7 @@ class Settings::ImportsController < Settings::BaseController
muting: ['Account address', 'Hide notifications'],
domain_blocking: false,
bookmarks: false,
+ lists: false,
}.freeze
def index
@@ -49,6 +51,8 @@ class Settings::ImportsController < Settings::BaseController
csv << [row.data['domain']]
when :bookmarks
csv << [row.data['uri']]
+ when :lists
+ csv << [row.data['list_name'], row.data['acct']]
end
end
end
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index 3d5592867..ae89cec78 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -5,10 +5,6 @@ module SettingsHelper
LanguagesHelper::SUPPORTED_LOCALES.keys
end
- def hash_to_object(hash)
- HashObject.new(hash)
- end
-
def session_device_icon(session)
device = session.detection.device
diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx
index 68a456c1b..ea863f5d1 100644
--- a/app/javascript/mastodon/components/account.jsx
+++ b/app/javascript/mastodon/components/account.jsx
@@ -143,7 +143,7 @@ class Account extends ImmutablePureComponent {
const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
if (firstVerifiedField) {
- verification = <>· >;
+ verification = ;
}
return (
@@ -154,9 +154,13 @@ class Account extends ImmutablePureComponent {
-
+
- {!minimal && <>
{verification} {muteTimeRemaining}>}
+ {!minimal && (
+
+ {verification} {muteTimeRemaining}
+
+ )}
diff --git a/app/javascript/mastodon/components/poll.jsx b/app/javascript/mastodon/components/poll.jsx
index fd2efd59c..dfc4034fa 100644
--- a/app/javascript/mastodon/components/poll.jsx
+++ b/app/javascript/mastodon/components/poll.jsx
@@ -57,9 +57,9 @@ class Poll extends ImmutablePureComponent {
};
static getDerivedStateFromProps (props, state) {
- const { poll, intl } = props;
+ const { poll } = props;
const expires_at = poll.get('expires_at');
- const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < intl.now();
+ const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < Date.now();
return (expired === state.expired) ? null : { expired };
}
@@ -76,10 +76,10 @@ class Poll extends ImmutablePureComponent {
}
_setupTimer () {
- const { poll, intl } = this.props;
+ const { poll } = this.props;
clearTimeout(this._timer);
if (!this.state.expired) {
- const delay = (new Date(poll.get('expires_at'))).getTime() - intl.now();
+ const delay = (new Date(poll.get('expires_at'))).getTime() - Date.now();
this._timer = setTimeout(() => {
this.setState({ expired: true });
}, delay);
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index fff9cb7ea..def4058e6 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -7814,13 +7814,28 @@ noscript {
}
}
+.account__contents {
+ overflow: hidden;
+}
+
+.account__details {
+ display: flex;
+ flex-wrap: wrap;
+ column-gap: 1em;
+}
+
.verified-badge {
display: inline-flex;
align-items: center;
color: $valid-value-color;
gap: 4px;
overflow: hidden;
- text-overflow: ellipsis;
+ white-space: nowrap;
+
+ > span {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
a {
color: inherit;
diff --git a/app/lib/hash_object.rb b/app/lib/hash_object.rb
deleted file mode 100644
index 274c020ad..000000000
--- a/app/lib/hash_object.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-class HashObject
- def initialize(hash)
- hash.each do |k, v|
- instance_variable_set("@#{k}", v)
- self.class.send(:define_method, k, proc { instance_variable_get("@#{k}") })
- end
- end
-end
diff --git a/app/models/bulk_import.rb b/app/models/bulk_import.rb
index af9a9670b..810e47184 100644
--- a/app/models/bulk_import.rb
+++ b/app/models/bulk_import.rb
@@ -30,6 +30,7 @@ class BulkImport < ApplicationRecord
muting: 2,
domain_blocking: 3,
bookmarks: 4,
+ lists: 5,
}
enum state: {
diff --git a/app/models/form/import.rb b/app/models/form/import.rb
index 750ef84be..2fc74715b 100644
--- a/app/models/form/import.rb
+++ b/app/models/form/import.rb
@@ -18,6 +18,7 @@ class Form::Import
muting: ['Account address', 'Hide notifications'],
domain_blocking: ['#domain'],
bookmarks: ['#uri'],
+ lists: ['List name', 'Account address'],
}.freeze
KNOWN_FIRST_HEADERS = EXPECTED_HEADERS_BY_TYPE.values.map(&:first).uniq.freeze
@@ -30,6 +31,7 @@ class Form::Import
'Hide notifications' => 'hide_notifications',
'#domain' => 'domain',
'#uri' => 'uri',
+ 'List name' => 'list_name',
}.freeze
class EmptyFileError < StandardError; end
@@ -48,6 +50,7 @@ class Form::Import
return :muting if data.original_filename&.start_with?('mutes') || data.original_filename&.start_with?('muted_accounts')
return :domain_blocking if data.original_filename&.start_with?('domain_blocks') || data.original_filename&.start_with?('blocked_domains')
return :bookmarks if data.original_filename&.start_with?('bookmarks')
+ return :lists if data.original_filename&.start_with?('lists')
end
# Whether the uploaded CSV file seems to correspond to a different import type than the one selected
@@ -76,14 +79,16 @@ class Form::Import
private
- def default_csv_header
+ def default_csv_headers
case type.to_sym
when :following, :blocking, :muting
- 'Account address'
+ ['Account address']
when :domain_blocking
- '#domain'
+ ['#domain']
when :bookmarks
- '#uri'
+ ['#uri']
+ when :lists
+ ['List name', 'Account address']
end
end
@@ -98,7 +103,7 @@ class Form::Import
field&.split(',')&.map(&:strip)&.presence
when 'Account address'
field.strip.gsub(/\A@/, '')
- when '#domain', '#uri'
+ when '#domain', '#uri', 'List name'
field.strip
else
field
@@ -109,7 +114,7 @@ class Form::Import
@csv_data.take(1) # Ensure the headers are read
raise EmptyFileError if @csv_data.headers == true
- @csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: [default_csv_header], converters: csv_converter) unless KNOWN_FIRST_HEADERS.include?(@csv_data.headers&.first)
+ @csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: default_csv_headers, converters: csv_converter) unless KNOWN_FIRST_HEADERS.include?(@csv_data.headers&.first)
@csv_data
end
@@ -133,7 +138,7 @@ class Form::Import
def validate_data
return if data.nil?
return errors.add(:data, I18n.t('imports.errors.too_large')) if data.size > FILE_SIZE_LIMIT
- return errors.add(:data, I18n.t('imports.errors.incompatible_type')) unless csv_data.headers.include?(default_csv_header)
+ return errors.add(:data, I18n.t('imports.errors.incompatible_type')) unless default_csv_headers.all? { |header| csv_data.headers.include?(header) }
errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ROWS_PROCESSING_LIMIT)) if csv_row_count > ROWS_PROCESSING_LIMIT
diff --git a/app/services/bulk_import_row_service.rb b/app/services/bulk_import_row_service.rb
index 4046ef4ee..ef4c18e78 100644
--- a/app/services/bulk_import_row_service.rb
+++ b/app/services/bulk_import_row_service.rb
@@ -7,7 +7,7 @@ class BulkImportRowService
@type = row.bulk_import.type.to_sym
case @type
- when :following, :blocking, :muting
+ when :following, :blocking, :muting, :lists
target_acct = @data['acct']
target_domain = domain(target_acct)
@target_account = stoplight_wrap_request(target_domain) { ResolveAccountService.new.call(target_acct, { check_delivery_availability: true }) }
@@ -33,6 +33,12 @@ class BulkImportRowService
return false unless StatusPolicy.new(@account, @target_status).show?
@account.bookmarks.find_or_create_by!(status: @target_status)
+ when :lists
+ list = @account.owned_lists.find_or_create_by!(title: @data['list_name'])
+
+ FollowService.new.call(@account, @target_account) unless @account.id == @target_account.id
+
+ list.accounts << @target_account
end
true
diff --git a/app/services/bulk_import_service.rb b/app/services/bulk_import_service.rb
index 2701b0c7e..5c14adc49 100644
--- a/app/services/bulk_import_service.rb
+++ b/app/services/bulk_import_service.rb
@@ -16,6 +16,8 @@ class BulkImportService < BaseService
import_domain_blocks!
when :bookmarks
import_bookmarks!
+ when :lists
+ import_lists!
end
@import.update!(state: :finished, finished_at: Time.now.utc) if @import.processed_items == @import.total_items
@@ -157,4 +159,24 @@ class BulkImportService < BaseService
[row.id]
end
end
+
+ def import_lists!
+ rows = @import.rows.to_a
+
+ if @import.overwrite?
+ included_lists = rows.map { |row| row.data['list_name'] }.uniq
+
+ @account.owned_lists.where.not(title: included_lists).destroy_all
+
+ # As list membership changes do not retroactively change timeline
+ # contents, simplify things by just clearing everything
+ @account.owned_lists.find_each do |list|
+ list.list_accounts.destroy_all
+ end
+ end
+
+ Import::RowWorker.push_bulk(rows) do |row|
+ [row.id]
+ end
+ end
end
diff --git a/app/views/settings/imports/index.html.haml b/app/views/settings/imports/index.html.haml
index 02c3f4eb3..5f7950b59 100644
--- a/app/views/settings/imports/index.html.haml
+++ b/app/views/settings/imports/index.html.haml
@@ -3,7 +3,7 @@
= simple_form_for @import, url: settings_imports_path do |f|
.field-group
- = f.input :type, as: :grouped_select, collection: { constructive: %i(following bookmarks), destructive: %i(muting blocking domain_blocking) }, wrapper: :with_block_label, include_blank: false, label_method: ->(type) { I18n.t("imports.types.#{type}") }, group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") }, group_method: :last, hint: t('imports.preface')
+ = f.input :type, as: :grouped_select, collection: { constructive: %i(following bookmarks lists), destructive: %i(muting blocking domain_blocking) }, wrapper: :with_block_label, include_blank: false, label_method: ->(type) { I18n.t("imports.types.#{type}") }, group_label_method: ->(group) { I18n.t("imports.type_groups.#{group.first}") }, group_method: :last, hint: t('imports.preface')
.fields-row
.fields-group.fields-row__column.fields-row__column-6
diff --git a/config/initializers/twitter_regex.rb b/config/initializers/twitter_regex.rb
index 6a7723fd2..e65b05dfd 100644
--- a/config/initializers/twitter_regex.rb
+++ b/config/initializers/twitter_regex.rb
@@ -25,7 +25,7 @@ module Twitter::TwitterText
\)
/iox
UCHARS = '\u{A0}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}\u{E000}-\u{F8FF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}'
- REGEXEN[:valid_url_query_chars] = /[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@#{UCHARS}]/iou
+ REGEXEN[:valid_url_query_chars] = /[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@\^#{UCHARS}]/iou
REGEXEN[:valid_url_query_ending_chars] = /[a-z0-9_&=#\/\-#{UCHARS}]/iou
REGEXEN[:valid_url_path] = /(?:
(?:
diff --git a/db/migrate/20230531153942_add_primary_key_to_accounts_tags_join_table.rb b/db/migrate/20230531153942_add_primary_key_to_accounts_tags_join_table.rb
new file mode 100644
index 000000000..0c3c17a36
--- /dev/null
+++ b/db/migrate/20230531153942_add_primary_key_to_accounts_tags_join_table.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddPrimaryKeyToAccountsTagsJoinTable < ActiveRecord::Migration[6.1]
+ disable_ddl_transaction!
+
+ def up
+ ActiveRecord::Base.transaction do
+ safety_assured do
+ execute 'ALTER TABLE accounts_tags ADD PRIMARY KEY USING INDEX index_accounts_tags_on_tag_id_and_account_id'
+
+ # Rename for consistency as the primary key's name is not represented in db/schema.rb
+ execute 'ALTER INDEX index_accounts_tags_on_tag_id_and_account_id RENAME TO accounts_tags_pkey'
+ end
+ end
+ end
+
+ def down
+ safety_assured do
+ # I have found no way to demote the primary key to an index, instead, re-create the index
+ execute 'CREATE UNIQUE INDEX CONCURRENTLY index_accounts_tags_on_tag_id_and_account_id ON accounts_tags (tag_id, account_id)'
+ execute 'ALTER TABLE accounts_tags DROP CONSTRAINT accounts_tags_pkey'
+ end
+ end
+end
diff --git a/db/migrate/20230531154811_add_primary_key_to_statuses_tags_join_table.rb b/db/migrate/20230531154811_add_primary_key_to_statuses_tags_join_table.rb
new file mode 100644
index 000000000..6581943da
--- /dev/null
+++ b/db/migrate/20230531154811_add_primary_key_to_statuses_tags_join_table.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddPrimaryKeyToStatusesTagsJoinTable < ActiveRecord::Migration[6.1]
+ disable_ddl_transaction!
+
+ def up
+ ActiveRecord::Base.transaction do
+ safety_assured do
+ execute 'ALTER TABLE statuses_tags ADD PRIMARY KEY USING INDEX index_statuses_tags_on_tag_id_and_status_id'
+
+ # Rename for consistency as the primary key's name is not represented in db/schema.rb
+ execute 'ALTER INDEX index_statuses_tags_on_tag_id_and_status_id RENAME TO statuses_tags_pkey'
+ end
+ end
+ end
+
+ def down
+ safety_assured do
+ # I have found no way to demote the primary key to an index, instead, re-create the index
+ execute 'CREATE UNIQUE INDEX CONCURRENTLY index_statuses_tags_on_tag_id_and_status_id ON statuses_tags (tag_id, status_id)'
+ execute 'ALTER TABLE statuses_tags DROP CONSTRAINT statuses_tags_pkey'
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b1fbaecab..11b5fb8d6 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: 2023_05_24_194155) do
+ActiveRecord::Schema.define(version: 2023_05_31_154811) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -194,11 +194,10 @@ ActiveRecord::Schema.define(version: 2023_05_24_194155) do
t.index ["url"], name: "index_accounts_on_url", opclass: :text_pattern_ops, where: "(url IS NOT NULL)"
end
- create_table "accounts_tags", id: false, force: :cascade do |t|
+ create_table "accounts_tags", primary_key: ["tag_id", "account_id"], force: :cascade do |t|
t.bigint "account_id", null: false
t.bigint "tag_id", null: false
t.index ["account_id", "tag_id"], name: "index_accounts_tags_on_account_id_and_tag_id"
- t.index ["tag_id", "account_id"], name: "index_accounts_tags_on_tag_id_and_account_id", unique: true
end
create_table "admin_action_logs", force: :cascade do |t|
@@ -982,11 +981,10 @@ ActiveRecord::Schema.define(version: 2023_05_24_194155) do
t.index ["uri"], name: "index_statuses_on_uri", unique: true, opclass: :text_pattern_ops, where: "(uri IS NOT NULL)"
end
- create_table "statuses_tags", id: false, force: :cascade do |t|
+ create_table "statuses_tags", primary_key: ["tag_id", "status_id"], force: :cascade do |t|
t.bigint "status_id", null: false
t.bigint "tag_id", null: false
t.index ["status_id"], name: "index_statuses_tags_on_status_id"
- t.index ["tag_id", "status_id"], name: "index_statuses_tags_on_tag_id_and_status_id", unique: true
end
create_table "system_keys", force: :cascade do |t|
diff --git a/lib/mastodon/cli/base.rb b/lib/mastodon/cli/base.rb
index f3c9fea92..32aff2fcc 100644
--- a/lib/mastodon/cli/base.rb
+++ b/lib/mastodon/cli/base.rb
@@ -4,16 +4,39 @@ require_relative '../../../config/boot'
require_relative '../../../config/environment'
require 'thor'
-require_relative 'helper'
+require_relative 'progress_helper'
module Mastodon
module CLI
class Base < Thor
- include CLI::Helper
+ include ProgressHelper
def self.exit_on_failure?
true
end
+
+ private
+
+ def pastel
+ @pastel ||= Pastel.new
+ end
+
+ def dry_run?
+ options[:dry_run]
+ end
+
+ def dry_run_mode_suffix
+ dry_run? ? ' (DRY RUN)' : ''
+ end
+
+ def reset_connection_pools!
+ ActiveRecord::Base.establish_connection(
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first.configuration_hash
+ .dup
+ .tap { |config| config['pool'] = options[:concurrency] + 1 }
+ )
+ RedisConfiguration.establish_pool(options[:concurrency])
+ end
end
end
end
diff --git a/lib/mastodon/cli/helper.rb b/lib/mastodon/cli/progress_helper.rb
similarity index 80%
rename from lib/mastodon/cli/helper.rb
rename to lib/mastodon/cli/progress_helper.rb
index 78931b9a2..1fa2745c1 100644
--- a/lib/mastodon/cli/helper.rb
+++ b/lib/mastodon/cli/progress_helper.rb
@@ -9,23 +9,19 @@ HttpLog.configuration.logger = dev_null
Paperclip.options[:log] = false
Chewy.logger = dev_null
-module Mastodon::CLI
- module Helper
- def dry_run?
- options[:dry_run]
- end
+require 'ruby-progressbar/outputs/null'
- def dry_run_mode_suffix
- dry_run? ? ' (DRY RUN)' : ''
- end
+module Mastodon::CLI
+ module ProgressHelper
+ PROGRESS_FORMAT = '%c/%u |%b%i| %e'
def create_progress_bar(total = nil)
- ProgressBar.create(total: total, format: '%c/%u |%b%i| %e')
- end
-
- def reset_connection_pools!
- ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Rails.env].dup.tap { |config| config['pool'] = options[:concurrency] + 1 })
- RedisConfiguration.establish_pool(options[:concurrency])
+ ProgressBar.create(
+ {
+ total: total,
+ format: PROGRESS_FORMAT,
+ }.merge(progress_output_options)
+ )
end
def parallelize_with_progress(scope)
@@ -82,8 +78,10 @@ module Mastodon::CLI
[total.value, aggregate.value]
end
- def pastel
- @pastel ||= Pastel.new
+ private
+
+ def progress_output_options
+ Rails.env.test? ? { output: ProgressBar::Outputs::Null } : {}
end
end
end
diff --git a/lib/mastodon/cli/search.rb b/lib/mastodon/cli/search.rb
index 6f48dcb09..8d7b7202f 100644
--- a/lib/mastodon/cli/search.rb
+++ b/lib/mastodon/cli/search.rb
@@ -29,15 +29,7 @@ module Mastodon::CLI
database will be imported into the indices, unless overridden with --no-import.
LONG_DESC
def deploy
- if options[:concurrency] < 1
- say('Cannot run with this concurrency setting, must be at least 1', :red)
- exit(1)
- end
-
- if options[:batch_size] < 1
- say('Cannot run with this batch_size setting, must be at least 1', :red)
- exit(1)
- end
+ verify_deploy_options!
indices = if options[:only]
options[:only].map { |str| "#{str.camelize}Index".constantize }
@@ -98,5 +90,26 @@ module Mastodon::CLI
say("Indexed #{added} records, de-indexed #{removed}", :green, true)
end
+
+ private
+
+ def verify_deploy_options!
+ verify_deploy_concurrency!
+ verify_deploy_batch_size!
+ end
+
+ def verify_deploy_concurrency!
+ return unless options[:concurrency] < 1
+
+ say('Cannot run with this concurrency setting, must be at least 1', :red)
+ exit(1)
+ end
+
+ def verify_deploy_batch_size!
+ return unless options[:batch_size] < 1
+
+ say('Cannot run with this batch_size setting, must be at least 1', :red)
+ exit(1)
+ end
end
end
diff --git a/package.json b/package.json
index 7fffd8a5c..f42a66cb0 100644
--- a/package.json
+++ b/package.json
@@ -140,12 +140,12 @@
"webpack-cli": "^3.3.12",
"webpack-merge": "^5.9.0",
"wicg-inert": "^3.1.2",
- "workbox-expiration": "^6.6.0",
- "workbox-precaching": "^6.6.0",
- "workbox-routing": "^6.6.0",
- "workbox-strategies": "^6.6.0",
- "workbox-webpack-plugin": "^6.6.0",
- "workbox-window": "^6.6.0",
+ "workbox-expiration": "^7.0.0",
+ "workbox-precaching": "^7.0.0",
+ "workbox-routing": "^7.0.0",
+ "workbox-strategies": "^7.0.0",
+ "workbox-webpack-plugin": "^7.0.0",
+ "workbox-window": "^7.0.0",
"ws": "^8.12.1"
},
"devDependencies": {
@@ -158,7 +158,7 @@
"@types/express": "^4.17.17",
"@types/http-link-header": "^1.0.3",
"@types/intl": "^1.2.0",
- "@types/jest": "^29.5.1",
+ "@types/jest": "^29.5.2",
"@types/js-yaml": "^4.0.5",
"@types/lodash": "^4.14.195",
"@types/npmlog": "^4.1.4",
@@ -192,7 +192,7 @@
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-formatjs": "^4.10.1",
"eslint-plugin-import": "~2.27.5",
- "eslint-plugin-jsdoc": "^45.0.0",
+ "eslint-plugin-jsdoc": "^46.1.0",
"eslint-plugin-jsx-a11y": "~6.7.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "~6.1.1",
diff --git a/spec/fixtures/files/lists.csv b/spec/fixtures/files/lists.csv
new file mode 100644
index 000000000..3155ed6d5
--- /dev/null
+++ b/spec/fixtures/files/lists.csv
@@ -0,0 +1,3 @@
+Mastodon project,gargron@example.com
+Mastodon project,mastodon@example.com
+test,foo@example.com
diff --git a/spec/lib/hash_object_spec.rb b/spec/lib/hash_object_spec.rb
deleted file mode 100644
index ce1806520..000000000
--- a/spec/lib/hash_object_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe HashObject do
- it 'has methods corresponding to hash properties' do
- expect(HashObject.new(key: 'value').key).to eq 'value'
- end
-end
diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb
index 8eee6f538..ba49e480a 100644
--- a/spec/lib/mastodon/cli/accounts_spec.rb
+++ b/spec/lib/mastodon/cli/accounts_spec.rb
@@ -662,4 +662,340 @@ describe Mastodon::CLI::Accounts do
end
end
end
+
+ describe '#refresh' do
+ context 'with --all option' do
+ let!(:local_account) { Fabricate(:account, domain: nil) }
+ let!(:remote_account_example_com) { Fabricate(:account, domain: 'example.com') }
+ let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
+ let(:scope) { Account.remote }
+
+ before do
+ allow(cli).to receive(:parallelize_with_progress).and_yield(remote_account_example_com)
+ .and_yield(account_example_net)
+ .and_return([2, nil])
+ cli.options = { all: true }
+ end
+
+ it 'refreshes the avatar for all remote accounts' do
+ allow(remote_account_example_com).to receive(:reset_avatar!)
+ allow(account_example_net).to receive(:reset_avatar!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(remote_account_example_com).to have_received(:reset_avatar!).once
+ expect(account_example_net).to have_received(:reset_avatar!).once
+ end
+
+ it 'does not refresh avatar for local accounts' do
+ allow(local_account).to receive(:reset_avatar!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(local_account).to_not have_received(:reset_avatar!)
+ end
+
+ it 'refreshes the header for all remote accounts' do
+ allow(remote_account_example_com).to receive(:reset_header!)
+ allow(account_example_net).to receive(:reset_header!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(remote_account_example_com).to have_received(:reset_header!).once
+ expect(account_example_net).to have_received(:reset_header!).once
+ end
+
+ it 'does not refresh the header for local accounts' do
+ allow(local_account).to receive(:reset_header!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(local_account).to_not have_received(:reset_header!)
+ end
+
+ it 'displays a successful message' do
+ expect { cli.refresh }.to output(
+ a_string_including('Refreshed 2 accounts')
+ ).to_stdout
+ end
+
+ context 'with --dry-run option' do
+ before do
+ cli.options = { all: true, dry_run: true }
+ end
+
+ it 'does not refresh the avatar for any account' do
+ allow(local_account).to receive(:reset_avatar!)
+ allow(remote_account_example_com).to receive(:reset_avatar!)
+ allow(account_example_net).to receive(:reset_avatar!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(local_account).to_not have_received(:reset_avatar!)
+ expect(remote_account_example_com).to_not have_received(:reset_avatar!)
+ expect(account_example_net).to_not have_received(:reset_avatar!)
+ end
+
+ it 'does not refresh the header for any account' do
+ allow(local_account).to receive(:reset_header!)
+ allow(remote_account_example_com).to receive(:reset_header!)
+ allow(account_example_net).to receive(:reset_header!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(local_account).to_not have_received(:reset_header!)
+ expect(remote_account_example_com).to_not have_received(:reset_header!)
+ expect(account_example_net).to_not have_received(:reset_header!)
+ end
+
+ it 'displays a successful message with (DRY RUN)' do
+ expect { cli.refresh }.to output(
+ a_string_including('Refreshed 2 accounts (DRY RUN)')
+ ).to_stdout
+ end
+ end
+ end
+
+ context 'with a list of accts' do
+ let!(:account_example_com_a) { Fabricate(:account, domain: 'example.com') }
+ let!(:account_example_com_b) { Fabricate(:account, domain: 'example.com') }
+ let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
+ let(:arguments) { [account_example_com_a.acct, account_example_com_b.acct] }
+
+ before do
+ allow(Account).to receive(:find_remote).with(account_example_com_a.username, account_example_com_a.domain).and_return(account_example_com_a)
+ allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(account_example_com_b)
+ allow(Account).to receive(:find_remote).with(account_example_net.username, account_example_net.domain).and_return(account_example_net)
+ end
+
+ it 'resets the avatar for the specified accounts' do
+ allow(account_example_com_a).to receive(:reset_avatar!)
+ allow(account_example_com_b).to receive(:reset_avatar!)
+
+ cli.refresh(*arguments)
+
+ expect(account_example_com_a).to have_received(:reset_avatar!).once
+ expect(account_example_com_b).to have_received(:reset_avatar!).once
+ end
+
+ it 'does not reset the avatar for unspecified accounts' do
+ allow(account_example_net).to receive(:reset_avatar!)
+
+ cli.refresh(*arguments)
+
+ expect(account_example_net).to_not have_received(:reset_avatar!)
+ end
+
+ it 'resets the header for the specified accounts' do
+ allow(account_example_com_a).to receive(:reset_header!)
+ allow(account_example_com_b).to receive(:reset_header!)
+
+ cli.refresh(*arguments)
+
+ expect(account_example_com_a).to have_received(:reset_header!).once
+ expect(account_example_com_b).to have_received(:reset_header!).once
+ end
+
+ it 'does not reset the header for unspecified accounts' do
+ allow(account_example_net).to receive(:reset_header!)
+
+ cli.refresh(*arguments)
+
+ expect(account_example_net).to_not have_received(:reset_header!)
+ end
+
+ context 'when an UnexpectedResponseError is raised' do
+ it 'displays a failure message' do
+ allow(account_example_com_a).to receive(:reset_avatar!).and_raise(Mastodon::UnexpectedResponseError)
+
+ expect { cli.refresh(*arguments) }
+ .to output(
+ a_string_including("Account failed: #{account_example_com_a.username}@#{account_example_com_a.domain}")
+ ).to_stdout
+ end
+ end
+
+ context 'when a specified account is not found' do
+ it 'exits with an error message' do
+ allow(Account).to receive(:find_remote).with(account_example_com_b.username, account_example_com_b.domain).and_return(nil)
+
+ expect { cli.refresh(*arguments) }.to output(
+ a_string_including('No such account')
+ ).to_stdout
+ .and raise_error(SystemExit)
+ end
+ end
+
+ context 'with --dry-run option' do
+ before do
+ cli.options = { dry_run: true }
+ end
+
+ it 'does not refresh the avatar for any account' do
+ allow(account_example_com_a).to receive(:reset_avatar!)
+ allow(account_example_com_b).to receive(:reset_avatar!)
+
+ cli.refresh(*arguments)
+
+ expect(account_example_com_a).to_not have_received(:reset_avatar!)
+ expect(account_example_com_b).to_not have_received(:reset_avatar!)
+ end
+
+ it 'does not refresh the header for any account' do
+ allow(account_example_com_a).to receive(:reset_header!)
+ allow(account_example_com_b).to receive(:reset_header!)
+
+ cli.refresh(*arguments)
+
+ expect(account_example_com_a).to_not have_received(:reset_header!)
+ expect(account_example_com_b).to_not have_received(:reset_header!)
+ end
+ end
+ end
+
+ context 'with --domain option' do
+ let!(:account_example_com_a) { Fabricate(:account, domain: 'example.com') }
+ let!(:account_example_com_b) { Fabricate(:account, domain: 'example.com') }
+ let!(:account_example_net) { Fabricate(:account, domain: 'example.net') }
+ let(:domain) { 'example.com' }
+ let(:scope) { Account.remote.where(domain: domain) }
+
+ before do
+ allow(cli).to receive(:parallelize_with_progress).and_yield(account_example_com_a)
+ .and_yield(account_example_com_b)
+ .and_return([2, nil])
+
+ cli.options = { domain: domain }
+ end
+
+ it 'refreshes the avatar for all accounts on specified domain' do
+ allow(account_example_com_a).to receive(:reset_avatar!)
+ allow(account_example_com_b).to receive(:reset_avatar!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(account_example_com_a).to have_received(:reset_avatar!).once
+ expect(account_example_com_b).to have_received(:reset_avatar!).once
+ end
+
+ it 'does not refresh the avatar for accounts outside specified domain' do
+ allow(account_example_net).to receive(:reset_avatar!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(account_example_net).to_not have_received(:reset_avatar!)
+ end
+
+ it 'refreshes the header for all accounts on specified domain' do
+ allow(account_example_com_a).to receive(:reset_header!)
+ allow(account_example_com_b).to receive(:reset_header!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope)
+ expect(account_example_com_a).to have_received(:reset_header!).once
+ expect(account_example_com_b).to have_received(:reset_header!).once
+ end
+
+ it 'does not refresh the header for accounts outside specified domain' do
+ allow(account_example_net).to receive(:reset_header!)
+
+ cli.refresh
+
+ expect(cli).to have_received(:parallelize_with_progress).with(scope).once
+ expect(account_example_net).to_not have_received(:reset_header!)
+ end
+ end
+
+ context 'when neither a list of accts nor options are provided' do
+ it 'exits with an error message' do
+ expect { cli.refresh }.to output(
+ a_string_including('No account(s) given')
+ ).to_stdout
+ .and raise_error(SystemExit)
+ end
+ end
+ end
+
+ describe '#rotate' do
+ context 'when neither username nor --all option are given' do
+ it 'exits with an error message' do
+ expect { cli.rotate }.to output(
+ a_string_including('No account(s) given')
+ ).to_stdout
+ .and raise_error(SystemExit)
+ end
+ end
+
+ context 'when a username is given' do
+ let(:account) { Fabricate(:account) }
+
+ it 'correctly rotates keys for the specified account' do
+ old_private_key = account.private_key
+ old_public_key = account.public_key
+
+ cli.rotate(account.username)
+ account.reload
+
+ expect(account.private_key).to_not eq(old_private_key)
+ expect(account.public_key).to_not eq(old_public_key)
+ end
+
+ it 'broadcasts the new keys for the specified account' do
+ allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
+
+ cli.rotate(account.username)
+
+ expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
+ end
+
+ context 'when the given username is not found' do
+ it 'exits with an error message when the specified username is not found' do
+ expect { cli.rotate('non_existent_username') }.to output(
+ a_string_including('No such account')
+ ).to_stdout
+ .and raise_error(SystemExit)
+ end
+ end
+ end
+
+ context 'when --all option is provided' do
+ let(:accounts) { Fabricate.times(3, :account) }
+ let(:options) { { all: true } }
+
+ before do
+ allow(Account).to receive(:local).and_return(Account.where(id: accounts.map(&:id)))
+ cli.options = { all: true }
+ end
+
+ it 'correctly rotates keys for all local accounts' do
+ old_private_keys = accounts.map(&:private_key)
+ old_public_keys = accounts.map(&:public_key)
+
+ cli.rotate
+ accounts.each(&:reload)
+
+ expect(accounts.map(&:private_key)).to_not eq(old_private_keys)
+ expect(accounts.map(&:public_key)).to_not eq(old_public_keys)
+ end
+
+ it 'broadcasts the new keys for each account' do
+ allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_in)
+
+ cli.rotate
+
+ accounts.each do |account|
+ expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_in).with(anything, account.id, anything).once
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/form/import_spec.rb b/spec/models/form/import_spec.rb
index e1fea4205..52cf1c96e 100644
--- a/spec/models/form/import_spec.rb
+++ b/spec/models/form/import_spec.rb
@@ -86,6 +86,7 @@ RSpec.describe Form::Import do
it_behaves_like 'too many CSV rows', 'muting', 'imports.txt', 1
it_behaves_like 'too many CSV rows', 'domain_blocking', 'domain_blocks.csv', 2
it_behaves_like 'too many CSV rows', 'bookmarks', 'bookmark-imports.txt', 3
+ it_behaves_like 'too many CSV rows', 'lists', 'lists.csv', 2
# Importing list of addresses with no headers into various types
it_behaves_like 'valid import', 'following', 'imports.txt'
@@ -98,6 +99,9 @@ RSpec.describe Form::Import do
# Importing bookmarks list with no headers into expected type
it_behaves_like 'valid import', 'bookmarks', 'bookmark-imports.txt'
+ # Importing lists with no headers into expected type
+ it_behaves_like 'valid import', 'lists', 'lists.csv'
+
# Importing followed accounts with headers into various compatible types
it_behaves_like 'valid import', 'following', 'following_accounts.csv'
it_behaves_like 'valid import', 'blocking', 'following_accounts.csv'
@@ -273,6 +277,12 @@ RSpec.describe Form::Import do
{ 'acct' => 'user@test.com', 'hide_notifications' => false },
]
+ it_behaves_like 'on successful import', 'lists', 'merge', 'lists.csv', [
+ { 'acct' => 'gargron@example.com', 'list_name' => 'Mastodon project' },
+ { 'acct' => 'mastodon@example.com', 'list_name' => 'Mastodon project' },
+ { 'acct' => 'foo@example.com', 'list_name' => 'test' },
+ ]
+
# Based on the bug report 20571 where UTF-8 encoded domains were rejecting import of their users
#
# https://github.com/mastodon/mastodon/issues/20571
diff --git a/spec/services/bulk_import_row_service_spec.rb b/spec/services/bulk_import_row_service_spec.rb
index 5bbe6b004..5e09845b5 100644
--- a/spec/services/bulk_import_row_service_spec.rb
+++ b/spec/services/bulk_import_row_service_spec.rb
@@ -91,5 +91,77 @@ RSpec.describe BulkImportRowService do
end
end
end
+
+ context 'when importing a list row' do
+ let(:import_type) { 'lists' }
+ let(:target_account) { Fabricate(:account) }
+ let(:data) do
+ { 'acct' => target_account.acct, 'list_name' => 'my list' }
+ end
+
+ shared_examples 'common behavior' do
+ context 'when the target account is already followed' do
+ before do
+ account.follow!(target_account)
+ end
+
+ it 'returns true' do
+ expect(subject.call(import_row)).to be true
+ end
+
+ it 'adds the target account to the list' do
+ expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
+ end
+ end
+
+ context 'when the user already requested to follow the target account' do
+ before do
+ account.request_follow!(target_account)
+ end
+
+ it 'returns true' do
+ expect(subject.call(import_row)).to be true
+ end
+
+ it 'adds the target account to the list' do
+ expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
+ end
+ end
+
+ context 'when the target account is neither followed nor requested' do
+ it 'returns true' do
+ expect(subject.call(import_row)).to be true
+ end
+
+ it 'adds the target account to the list' do
+ expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
+ end
+ end
+
+ context 'when the target account is the user themself' do
+ let(:target_account) { account }
+
+ it 'returns true' do
+ expect(subject.call(import_row)).to be true
+ end
+
+ it 'adds the target account to the list' do
+ expect { subject.call(import_row) }.to change { ListAccount.joins(:list).exists?(account_id: target_account.id, list: { title: 'my list' }) }.from(false).to(true)
+ end
+ end
+ end
+
+ context 'when the list does not exist yet' do
+ include_examples 'common behavior'
+ end
+
+ context 'when the list exists' do
+ before do
+ Fabricate(:list, account: account, title: 'my list')
+ end
+
+ include_examples 'common behavior'
+ end
+ end
end
end
diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb
index 7016ecd3f..6495b323c 100644
--- a/spec/services/fetch_link_card_service_spec.rb
+++ b/spec/services/fetch_link_card_service_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe FetchLinkCardService, type: :service do
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
stub_request(:get, 'http://example.com/日本語').to_return(request_fixture('sjis.txt'))
stub_request(:get, 'https://github.com/qbi/WannaCry').to_return(status: 404)
+ stub_request(:get, 'http://example.com/test?data=file.gpx%5E1').to_return(status: 200)
stub_request(:get, 'http://example.com/test-').to_return(request_fixture('idn.txt'))
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
@@ -87,6 +88,15 @@ RSpec.describe FetchLinkCardService, type: :service do
expect(a_request(:get, 'http://example.com/sjis')).to_not have_been_made
end
end
+
+ context do
+ let(:status) { Fabricate(:status, text: 'test http://example.com/test?data=file.gpx^1') }
+
+ it 'does fetch URLs with a caret in search params' do
+ expect(a_request(:get, 'http://example.com/test?data=file.gpx')).to_not have_been_made
+ expect(a_request(:get, 'http://example.com/test?data=file.gpx%5E1')).to have_been_made.once
+ end
+ end
end
context 'with a remote status' do
diff --git a/yarn.lock b/yarn.lock
index 959fa8bd6..0cdfceccf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1743,6 +1743,15 @@
"@jridgewell/set-array" "^1.0.0"
"@jridgewell/sourcemap-codec" "^1.4.10"
+"@jridgewell/gen-mapping@^0.3.0":
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
+ integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
"@jridgewell/gen-mapping@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
@@ -1767,6 +1776,14 @@
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+"@jridgewell/source-map@^0.3.2":
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda"
+ integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
"@jridgewell/sourcemap-codec@1.4.14":
version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
@@ -2231,10 +2248,10 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/jest@*", "@types/jest@^29.5.1":
- version "29.5.1"
- resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47"
- integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==
+"@types/jest@*", "@types/jest@^29.5.2":
+ version "29.5.2"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b"
+ integrity sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==
dependencies:
expect "^29.0.0"
pretty-format "^29.0.0"
@@ -3007,9 +3024,9 @@ ansi-regex@^2.0.0:
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
ansi-regex@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
- integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
+ integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
ansi-regex@^5.0.0, ansi-regex@^5.0.1:
version "5.0.1"
@@ -5264,10 +5281,10 @@ eslint-plugin-import@~2.27.5:
semver "^6.3.0"
tsconfig-paths "^3.14.1"
-eslint-plugin-jsdoc@^45.0.0:
- version "45.0.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-45.0.0.tgz#6be84e4842a7138cc571a907ea9c31c42eaac5c0"
- integrity sha512-l2+Jcs/Ps7oFA+SWY+0sweU/e5LgricnEl6EsDlyRTF5y0+NWL1y9Qwz9PHwHAxtdJq6lxPjEQWmYLMkvhzD4g==
+eslint-plugin-jsdoc@^46.1.0:
+ version "46.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.1.0.tgz#3ff932b70bc25f3745049f525a789faed7c948da"
+ integrity sha512-NpjpSuWR+Wwxzmssji7AVty1Vu0JvI7v+cTj+Rw1nKVjGv2eMvLGM/SI4VpgTXp82JbLtFOsA2QYLHT3YSmASA==
dependencies:
"@es-joy/jsdoccomment" "~0.39.4"
are-docs-informative "^0.0.2"
@@ -7860,9 +7877,9 @@ loader-utils@^1.2.3, loader-utils@^1.4.0:
json5 "^1.0.1"
loader-utils@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
- integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
+ integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
@@ -10782,7 +10799,7 @@ source-map@^0.7.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
-source-map@^0.8.0-beta.0, source-map@~0.8.0-beta.0:
+source-map@^0.8.0-beta.0:
version "0.8.0-beta.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
@@ -10863,9 +10880,9 @@ sprintf-js@~1.0.2:
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
ssri@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.0.tgz#79ca74e21f8ceaeddfcb4b90143c458b8d988808"
- integrity sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"
+ integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==
dependencies:
minipass "^3.1.1"
@@ -11359,13 +11376,13 @@ terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^4.2.3:
webpack-sources "^1.4.3"
terser@^5.0.0, terser@^5.3.4:
- version "5.13.1"
- resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.1.tgz#66332cdc5a01b04a224c9fad449fc1a18eaa1799"
- integrity sha512-hn4WKOfwnwbYfe48NgrQjqNOH9jzLqRcIfbYytOXCOv46LBfWr9bDS17MQqOi+BWGD0sJK3Sj5NC/gJjiojaoA==
+ version "5.17.6"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.6.tgz#d810e75e1bb3350c799cd90ebefe19c9412c12de"
+ integrity sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==
dependencies:
+ "@jridgewell/source-map" "^0.3.2"
acorn "^8.5.0"
commander "^2.20.0"
- source-map "~0.8.0-beta.0"
source-map-support "~0.5.20"
tesseract.js-core@^2.2.0:
@@ -12267,25 +12284,25 @@ word-wrap@^1.2.3, word-wrap@~1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
-workbox-background-sync@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f"
- integrity sha512-trJd3ovpWCvzu4sW0E8rV3FUyIcC0W8G+AZ+VcqzzA890AsWZlUGOTSxIMmIHVusUw/FDq1HFWfy/kC/WTRqSg==
+workbox-background-sync@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz#2b84b96ca35fec976e3bd2794b70e4acec46b3a5"
+ integrity sha512-S+m1+84gjdueM+jIKZ+I0Lx0BDHkk5Nu6a3kTVxP4fdj3gKouRNmhO8H290ybnJTOPfBDtTMXSQA/QLTvr7PeA==
dependencies:
idb "^7.0.1"
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-broadcast-update@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.6.1.tgz#0fad9454cf8e4ace0c293e5617c64c75d8a8c61e"
- integrity sha512-fBhffRdaANdeQ1V8s692R9l/gzvjjRtydBOvR6WCSB0BNE2BacA29Z4r9/RHd9KaXCPl6JTdI9q0bR25YKP8TQ==
+workbox-broadcast-update@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-7.0.0.tgz#7f611ca1a94ba8ac0aa40fa171c9713e0f937d22"
+ integrity sha512-oUuh4jzZrLySOo0tC0WoKiSg90bVAcnE98uW7F8GFiSOXnhogfNDGZelPJa+6KpGBO5+Qelv04Hqx2UD+BJqNQ==
dependencies:
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-build@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.6.1.tgz#6010e9ce550910156761448f2dbea8cfcf759cb0"
- integrity sha512-INPgDx6aRycAugUixbKgiEQBWD0MPZqU5r0jyr24CehvNuLPSXp/wGOpdRJmts656lNiXwqV7dC2nzyrzWEDnw==
+workbox-build@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-7.0.0.tgz#02ab5ef2991b3369b8b9395703f08912212769b4"
+ integrity sha512-CttE7WCYW9sZC+nUYhQg3WzzGPr4IHmrPnjKiu3AMXsiNQKx+l4hHl63WTrnicLmKEKHScWDH8xsGBdrYgtBzg==
dependencies:
"@apideck/better-ajv-errors" "^0.3.1"
"@babel/core" "^7.11.1"
@@ -12309,132 +12326,132 @@ workbox-build@6.6.1:
strip-comments "^2.0.1"
tempy "^0.6.0"
upath "^1.2.0"
- workbox-background-sync "6.6.1"
- workbox-broadcast-update "6.6.1"
- workbox-cacheable-response "6.6.1"
- workbox-core "6.6.1"
- workbox-expiration "6.6.1"
- workbox-google-analytics "6.6.1"
- workbox-navigation-preload "6.6.1"
- workbox-precaching "6.6.1"
- workbox-range-requests "6.6.1"
- workbox-recipes "6.6.1"
- workbox-routing "6.6.1"
- workbox-strategies "6.6.1"
- workbox-streams "6.6.1"
- workbox-sw "6.6.1"
- workbox-window "6.6.1"
+ workbox-background-sync "7.0.0"
+ workbox-broadcast-update "7.0.0"
+ workbox-cacheable-response "7.0.0"
+ workbox-core "7.0.0"
+ workbox-expiration "7.0.0"
+ workbox-google-analytics "7.0.0"
+ workbox-navigation-preload "7.0.0"
+ workbox-precaching "7.0.0"
+ workbox-range-requests "7.0.0"
+ workbox-recipes "7.0.0"
+ workbox-routing "7.0.0"
+ workbox-strategies "7.0.0"
+ workbox-streams "7.0.0"
+ workbox-sw "7.0.0"
+ workbox-window "7.0.0"
-workbox-cacheable-response@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.6.1.tgz#284c2b86be3f4fd191970ace8c8e99797bcf58e9"
- integrity sha512-85LY4veT2CnTCDxaVG7ft3NKaFbH6i4urZXgLiU4AiwvKqS2ChL6/eILiGRYXfZ6gAwDnh5RkuDbr/GMS4KSag==
+workbox-cacheable-response@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz#ee27c036728189eed69d25a135013053277482d2"
+ integrity sha512-0lrtyGHn/LH8kKAJVOQfSu3/80WDc9Ma8ng0p2i/5HuUndGttH+mGMSvOskjOdFImLs2XZIimErp7tSOPmu/6g==
dependencies:
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-core@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.6.1.tgz#7184776d4134c5ed2f086878c882728fc9084265"
- integrity sha512-ZrGBXjjaJLqzVothoE12qTbVnOAjFrHDXpZe7coCb6q65qI/59rDLwuFMO4PcZ7jcbxY+0+NhUVztzR/CbjEFw==
+workbox-core@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.0.0.tgz#dec114ec923cc2adc967dd9be1b8a0bed50a3545"
+ integrity sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==
-workbox-expiration@6.6.1, workbox-expiration@^6.6.0:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.6.1.tgz#a841fa36676104426dbfb9da1ef6a630b4f93739"
- integrity sha512-qFiNeeINndiOxaCrd2DeL1Xh1RFug3JonzjxUHc5WkvkD2u5abY3gZL1xSUNt3vZKsFFGGORItSjVTVnWAZO4A==
+workbox-expiration@7.0.0, workbox-expiration@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-7.0.0.tgz#3d90bcf2a7577241de950f89784f6546b66c2baa"
+ integrity sha512-MLK+fogW+pC3IWU9SFE+FRStvDVutwJMR5if1g7oBJx3qwmO69BNoJQVaMXq41R0gg3MzxVfwOGKx3i9P6sOLQ==
dependencies:
idb "^7.0.1"
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-google-analytics@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.6.1.tgz#a07a6655ab33d89d1b0b0a935ffa5dea88618c5d"
- integrity sha512-1TjSvbFSLmkpqLcBsF7FuGqqeDsf+uAXO/pjiINQKg3b1GN0nBngnxLcXDYo1n/XxK4N7RaRrpRlkwjY/3ocuA==
+workbox-google-analytics@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-7.0.0.tgz#603b2c4244af1e85de0fb26287d4e17d3293452a"
+ integrity sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==
dependencies:
- workbox-background-sync "6.6.1"
- workbox-core "6.6.1"
- workbox-routing "6.6.1"
- workbox-strategies "6.6.1"
+ workbox-background-sync "7.0.0"
+ workbox-core "7.0.0"
+ workbox-routing "7.0.0"
+ workbox-strategies "7.0.0"
-workbox-navigation-preload@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.6.1.tgz#61a34fe125558dd88cf09237f11bd966504ea059"
- integrity sha512-DQCZowCecO+wRoIxJI2V6bXWK6/53ff+hEXLGlQL4Rp9ZaPDLrgV/32nxwWIP7QpWDkVEtllTAK5h6cnhxNxDA==
+workbox-navigation-preload@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-7.0.0.tgz#4913878dbbd97057181d57baa18d2bbdde085c6c"
+ integrity sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==
dependencies:
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-precaching@6.6.1, workbox-precaching@^6.6.0:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.6.1.tgz#dedeeba10a2d163d990bf99f1c2066ac0d1a19e2"
- integrity sha512-K4znSJ7IKxCnCYEdhNkMr7X1kNh8cz+mFgx9v5jFdz1MfI84pq8C2zG+oAoeE5kFrUf7YkT5x4uLWBNg0DVZ5A==
+workbox-precaching@7.0.0, workbox-precaching@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-7.0.0.tgz#3979ba8033aadf3144b70e9fe631d870d5fbaa03"
+ integrity sha512-EC0vol623LJqTJo1mkhD9DZmMP604vHqni3EohhQVwhJlTgyKyOkMrZNy5/QHfOby+39xqC01gv4LjOm4HSfnA==
dependencies:
- workbox-core "6.6.1"
- workbox-routing "6.6.1"
- workbox-strategies "6.6.1"
+ workbox-core "7.0.0"
+ workbox-routing "7.0.0"
+ workbox-strategies "7.0.0"
-workbox-range-requests@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.6.1.tgz#ddaf7e73af11d362fbb2f136a9063a4c7f507a39"
- integrity sha512-4BDzk28govqzg2ZpX0IFkthdRmCKgAKreontYRC5YsAPB2jDtPNxqx3WtTXgHw1NZalXpcH/E4LqUa9+2xbv1g==
+workbox-range-requests@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-7.0.0.tgz#97511901e043df27c1aa422adcc999a7751f52ed"
+ integrity sha512-SxAzoVl9j/zRU9OT5+IQs7pbJBOUOlriB8Gn9YMvi38BNZRbM+RvkujHMo8FOe9IWrqqwYgDFBfv6sk76I1yaQ==
dependencies:
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-recipes@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.6.1.tgz#ea70d2b2b0b0bce8de0a9d94f274d4a688e69fae"
- integrity sha512-/oy8vCSzromXokDA+X+VgpeZJvtuf8SkQ8KL0xmRivMgJZrjwM3c2tpKTJn6PZA6TsbxGs3Sc7KwMoZVamcV2g==
+workbox-recipes@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-7.0.0.tgz#1a6a01c8c2dfe5a41eef0fed3fe517e8a45c6514"
+ integrity sha512-DntcK9wuG3rYQOONWC0PejxYYIDHyWWZB/ueTbOUDQgefaeIj1kJ7pdP3LZV2lfrj8XXXBWt+JDRSw1lLLOnww==
dependencies:
- workbox-cacheable-response "6.6.1"
- workbox-core "6.6.1"
- workbox-expiration "6.6.1"
- workbox-precaching "6.6.1"
- workbox-routing "6.6.1"
- workbox-strategies "6.6.1"
+ workbox-cacheable-response "7.0.0"
+ workbox-core "7.0.0"
+ workbox-expiration "7.0.0"
+ workbox-precaching "7.0.0"
+ workbox-routing "7.0.0"
+ workbox-strategies "7.0.0"
-workbox-routing@6.6.1, workbox-routing@^6.6.0:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.6.1.tgz#cba9a1c7e0d1ea11e24b6f8c518840efdc94f581"
- integrity sha512-j4ohlQvfpVdoR8vDYxTY9rA9VvxTHogkIDwGdJ+rb2VRZQ5vt1CWwUUZBeD/WGFAni12jD1HlMXvJ8JS7aBWTg==
+workbox-routing@7.0.0, workbox-routing@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-7.0.0.tgz#6668438a06554f60645aedc77244a4fe3a91e302"
+ integrity sha512-8YxLr3xvqidnbVeGyRGkaV4YdlKkn5qZ1LfEePW3dq+ydE73hUUJJuLmGEykW3fMX8x8mNdL0XrWgotcuZjIvA==
dependencies:
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-strategies@6.6.1, workbox-strategies@^6.6.0:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.6.1.tgz#38d0f0fbdddba97bd92e0c6418d0b1a2ccd5b8bf"
- integrity sha512-WQLXkRnsk4L81fVPkkgon1rZNxnpdO5LsO+ws7tYBC6QQQFJVI6v98klrJEjFtZwzw/mB/HT5yVp7CcX0O+mrw==
+workbox-strategies@7.0.0, workbox-strategies@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-7.0.0.tgz#dcba32b3f3074476019049cc490fe1a60ea73382"
+ integrity sha512-dg3qJU7tR/Gcd/XXOOo7x9QoCI9nk74JopaJaYAQ+ugLi57gPsXycVdBnYbayVj34m6Y8ppPwIuecrzkpBVwbA==
dependencies:
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
-workbox-streams@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.6.1.tgz#b2f7ba7b315c27a6e3a96a476593f99c5d227d26"
- integrity sha512-maKG65FUq9e4BLotSKWSTzeF0sgctQdYyTMq529piEN24Dlu9b6WhrAfRpHdCncRS89Zi2QVpW5V33NX8PgH3Q==
+workbox-streams@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-7.0.0.tgz#36722aecd04785f88b6f709e541c094fc658c0f9"
+ integrity sha512-moVsh+5to//l6IERWceYKGiftc+prNnqOp2sgALJJFbnNVpTXzKISlTIsrWY+ogMqt+x1oMazIdHj25kBSq/HQ==
dependencies:
- workbox-core "6.6.1"
- workbox-routing "6.6.1"
+ workbox-core "7.0.0"
+ workbox-routing "7.0.0"
-workbox-sw@6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.6.1.tgz#d4c4ca3125088e8b9fd7a748ed537fa0247bd72c"
- integrity sha512-R7whwjvU2abHH/lR6kQTTXLHDFU2izht9kJOvBRYK65FbwutT4VvnUAJIgHvfWZ/fokrOPhfoWYoPCMpSgUKHQ==
+workbox-sw@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-7.0.0.tgz#7350126411e3de1409f7ec243df8d06bb5b08b86"
+ integrity sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==
-workbox-webpack-plugin@^6.6.0:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.1.tgz#4f81cc1ad4e5d2cd7477a86ba83c84ee2d187531"
- integrity sha512-zpZ+ExFj9NmiI66cFEApyjk7hGsfJ1YMOaLXGXBoZf0v7Iu6hL0ZBe+83mnDq3YYWAfA3fnyFejritjOHkFcrA==
+workbox-webpack-plugin@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-7.0.0.tgz#6c61661a2cacde1239192a5877a041a2943d1a55"
+ integrity sha512-R1ZzCHPfzeJjLK2/TpKUhxSQ3fFDCxlWxgRhhSjMQLz3G2MlBnyw/XeYb34e7SGgSv0qG22zEhMIzjMNqNeKbw==
dependencies:
fast-json-stable-stringify "^2.1.0"
pretty-bytes "^5.4.1"
upath "^1.2.0"
webpack-sources "^1.4.3"
- workbox-build "6.6.1"
+ workbox-build "7.0.0"
-workbox-window@6.6.1, workbox-window@^6.6.0:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.6.1.tgz#f22a394cbac36240d0dadcbdebc35f711bb7b89e"
- integrity sha512-wil4nwOY58nTdCvif/KEZjQ2NP8uk3gGeRNy2jPBbzypU4BT4D9L8xiwbmDBpZlSgJd2xsT9FvSNU0gsxV51JQ==
+workbox-window@7.0.0, workbox-window@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-7.0.0.tgz#a683ab33c896e4f16786794eac7978fc98a25d08"
+ integrity sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==
dependencies:
"@types/trusted-types" "^2.0.2"
- workbox-core "6.6.1"
+ workbox-core "7.0.0"
wrap-ansi@^5.1.0:
version "5.1.0"
@@ -12485,9 +12502,9 @@ write-file-atomic@^5.0.1:
signal-exit "^4.0.1"
ws@^6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
+ integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
dependencies:
async-limiter "~1.0.0"