This is a test status.
', + contentHtml: data.text ?? 'This is a test status.
', ...data, }); diff --git a/app/lib/activitypub/parser/interaction_policy_parser.rb b/app/lib/activitypub/parser/interaction_policy_parser.rb new file mode 100644 index 0000000000..6587b245ee --- /dev/null +++ b/app/lib/activitypub/parser/interaction_policy_parser.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class ActivityPub::Parser::InteractionPolicyParser + def initialize(json, account) + @json = json + @account = account + end + + def bitmap + flags = 0 + return flags if @json.blank? + + flags |= subpolicy(@json['automaticApproval']) + flags <<= 16 + flags |= subpolicy(@json['manualApproval']) + + flags + end + + private + + def subpolicy(partial_json) + flags = 0 + + allowed_actors = Array(partial_json).dup + allowed_actors.uniq! + + flags |= InteractionPolicy::POLICY_FLAGS[:public] if allowed_actors.delete('as:Public') || allowed_actors.delete('Public') || allowed_actors.delete('https://www.w3.org/ns/activitystreams#Public') + flags |= InteractionPolicy::POLICY_FLAGS[:followers] if allowed_actors.delete(@account.followers_url) + flags |= InteractionPolicy::POLICY_FLAGS[:following] if allowed_actors.delete(@account.following_url) + + includes_target_actor = allowed_actors.delete(ActivityPub::TagManager.instance.uri_for(@account)).present? + + # Any unrecognized actor is marked as unsupported + flags |= InteractionPolicy::POLICY_FLAGS[:unsupported_policy] unless allowed_actors.empty? + + flags |= InteractionPolicy::POLICY_FLAGS[:disabled] if flags.zero? && includes_target_actor + + flags + end +end diff --git a/app/models/account.rb b/app/models/account.rb index 3caa30f09b..6735e98ac0 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -5,54 +5,55 @@ # Table name: accounts # # id :bigint(8) not null, primary key -# username :string default(""), not null -# domain :string -# private_key :text -# public_key :text default(""), not null -# created_at :datetime not null -# updated_at :datetime not null -# note :text default(""), not null -# display_name :string default(""), not null -# uri :string default(""), not null -# url :string -# avatar_file_name :string +# actor_type :string +# also_known_as :string is an Array +# attribution_domains :string default([]), is an Array # avatar_content_type :string +# avatar_file_name :string # avatar_file_size :integer -# avatar_updated_at :datetime -# header_file_name :string -# header_content_type :string -# header_file_size :integer -# header_updated_at :datetime # avatar_remote_url :string -# locked :boolean default(FALSE), not null -# header_remote_url :string default(""), not null -# last_webfingered_at :datetime -# inbox_url :string default(""), not null -# outbox_url :string default(""), not null -# shared_inbox_url :string default(""), not null -# followers_url :string default(""), not null -# following_url :string default(""), not null -# protocol :integer default("ostatus"), not null -# memorial :boolean default(FALSE), not null -# moved_to_account_id :bigint(8) +# avatar_storage_schema_version :integer +# avatar_updated_at :datetime +# discoverable :boolean +# display_name :string default(""), not null +# domain :string +# feature_approval_policy :integer default(0), not null # featured_collection_url :string # fields :jsonb -# actor_type :string -# discoverable :boolean -# also_known_as :string is an Array +# followers_url :string default(""), not null +# following_url :string default(""), not null +# header_content_type :string +# header_file_name :string +# header_file_size :integer +# header_remote_url :string default(""), not null +# header_storage_schema_version :integer +# header_updated_at :datetime +# hide_collections :boolean +# id_scheme :integer default("numeric_ap_id") +# inbox_url :string default(""), not null +# indexable :boolean default(FALSE), not null +# last_webfingered_at :datetime +# locked :boolean default(FALSE), not null +# memorial :boolean default(FALSE), not null +# note :text default(""), not null +# outbox_url :string default(""), not null +# private_key :text +# protocol :integer default("ostatus"), not null +# public_key :text default(""), not null +# requested_review_at :datetime +# reviewed_at :datetime +# sensitized_at :datetime +# shared_inbox_url :string default(""), not null # silenced_at :datetime # suspended_at :datetime -# hide_collections :boolean -# avatar_storage_schema_version :integer -# header_storage_schema_version :integer # suspension_origin :integer -# sensitized_at :datetime # trendable :boolean -# reviewed_at :datetime -# requested_review_at :datetime -# indexable :boolean default(FALSE), not null -# attribution_domains :string default([]), is an Array -# id_scheme :integer default("numeric_ap_id") +# uri :string default(""), not null +# url :string +# username :string default(""), not null +# created_at :datetime not null +# updated_at :datetime not null +# moved_to_account_id :bigint(8) # class Account < ApplicationRecord @@ -90,6 +91,7 @@ class Account < ApplicationRecord include Account::FaspConcern include Account::FinderConcern include Account::Header + include Account::InteractionPolicyConcern include Account::Interactions include Account::Mappings include Account::Merging diff --git a/app/models/concerns/account/interaction_policy_concern.rb b/app/models/concerns/account/interaction_policy_concern.rb new file mode 100644 index 0000000000..8fe9eda1ba --- /dev/null +++ b/app/models/concerns/account/interaction_policy_concern.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Account::InteractionPolicyConcern + extend ActiveSupport::Concern + + included do + composed_of :feature_interaction_policy, class_name: 'InteractionPolicy', mapping: { feature_approval_policy: :bitmap } + end + + def feature_policy_as_keys(kind) + raise ArgumentError unless kind.in?(%i(automatic manual)) + return local_feature_policy(kind) if local? + + sub_policy = feature_interaction_policy.send(kind) + sub_policy.as_keys + end + + # Returns `:automatic`, `:manual`, `:unknown`, ':missing` or `:denied` + def feature_policy_for_account(other_account) + return :denied if other_account.nil? || (local? && !discoverable?) + return :automatic if local? + # Post author is always allowed to feature themselves + return :automatic if self == other_account + return :missing if feature_approval_policy.zero? + + automatic_policy = feature_interaction_policy.automatic + following_self = nil + followed_by_self = nil + + return :automatic if automatic_policy.public? + + if automatic_policy.followers? + following_self = followed_by?(other_account) + return :automatic if following_self + end + + if automatic_policy.following? + followed_by_self = following?(other_account) + return :automatic if followed_by_self + end + + # We don't know we are allowed by the automatic policy, considering the manual one + manual_policy = feature_interaction_policy.manual + + return :manual if manual_policy.public? + + if manual_policy.followers? + following_self = followed_by?(other_account) if following_self.nil? + return :manual if following_self + end + + if manual_policy.following? + followed_by_self = following?(other_account) if followed_by_self.nil? + return :manual if followed_by_self + end + + return :unknown if [automatic_policy, manual_policy].any?(&:unsupported_policy?) + + :denied + end + + private + + def local_feature_policy(kind) + return [] if kind == :manual || !discoverable? + + [:public] + end +end diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index ed90fa428f..c19d42bfb4 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -10,12 +10,16 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer :moved_to, :property_value, :discoverable, :suspended, :memorial, :indexable, :attribution_domains + context_extensions :interaction_policies if Mastodon::Feature.collections_enabled? + attributes :id, :type, :following, :followers, :inbox, :outbox, :featured, :featured_tags, :preferred_username, :name, :summary, :url, :manually_approves_followers, :discoverable, :indexable, :published, :memorial + attribute :interaction_policy, if: -> { Mastodon::Feature.collections_enabled? } + has_one :public_key, serializer: ActivityPub::PublicKeySerializer has_many :virtual_tags, key: :tag @@ -163,6 +167,16 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer object.created_at.midnight.iso8601 end + def interaction_policy + uri = object.discoverable? ? ActivityPub::TagManager::COLLECTIONS[:public] : ActivityPub::TagManager.instance.uri_for(object) + + { + canFeature: { + automaticApproval: [uri], + }, + } + end + class CustomEmojiSerializer < ActivityPub::EmojiSerializer end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index dad1527d45..9a6b3fd2ce 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -20,6 +20,8 @@ class REST::AccountSerializer < ActiveModel::Serializer attribute :memorial, if: :memorial? + attribute :feature_approval, if: -> { Mastodon::Feature.collections_enabled? } + class AccountDecorator < SimpleDelegator def self.model_name Account.model_name @@ -161,4 +163,12 @@ class REST::AccountSerializer < ActiveModel::Serializer def moved_and_not_nested? object.moved? end + + def feature_approval + { + automatic: object.feature_policy_as_keys(:automatic), + manual: object.feature_policy_as_keys(:manual), + current_user: object.feature_policy_for_account(current_user&.account), + } + end end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index eb67daf7e8..f133fbc84a 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -107,6 +107,7 @@ class ActivityPub::ProcessAccountService < BaseService @account.uri = @uri @account.actor_type = actor_type @account.created_at = @json['published'] if @json['published'].present? + @account.feature_approval_policy = feature_approval_policy if Mastodon::Feature.collections_enabled? end def valid_collection_uri(uri) @@ -360,4 +361,8 @@ class ActivityPub::ProcessAccountService < BaseService emoji.image_remote_url = image_url emoji.save end + + def feature_approval_policy + ActivityPub::Parser::InteractionPolicyParser.new(@json.dig('interactionPolicy', 'canFeature'), @account).bitmap + end end diff --git a/config/locales/he.yml b/config/locales/he.yml index 98baeb601c..f22d5d9607 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -876,6 +876,7 @@ he: publish_statistics: פרסום הסטטיסטיקות בפומבי title: תגליות trends: נושאים חמים + wrapstodon: סיכומודון domain_blocks: all: לכולם disabled: לאף אחד diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml index cdf1300a4f..5fcaeb4421 100644 --- a/config/locales/simple_form.he.yml +++ b/config/locales/simple_form.he.yml @@ -111,6 +111,7 @@ he: thumbnail: תמונה ביחס 2:1 בערך שתוצג ליד המידע על השרת שלך. trendable_by_default: לדלג על בדיקה ידנית של התכנים החמים. פריטים ספציפיים עדיין ניתנים להסרה לאחר מעשה. trends: נושאים חמים יציגו אילו הודעות, תגיות וידיעות חדשות צוברות חשיפה על השרת שלך. + wrapstodon: אפשר למשתמשיך המקומיים.ות ליצור סיכום חביב של פעילותם במסטודון בשנה האחרונה. התכונה מאופשרת בין 10 ועד 31 בדצמבר כל שנה, ומצעת למשתמשים שיצרו לפחות הודעה ציבורית אחת והשתמשו לפחות בתגית אחת במשך השנה. form_challenge: current_password: את.ה נכנס. ת לאזור מאובטח imports: @@ -314,6 +315,7 @@ he: thumbnail: תמונה ממוזערת מהשרת trendable_by_default: הרשאה לפריטים להופיע בנושאים החמים ללא אישור מוקדם trends: אפשר פריטים חמים (טרנדים) + wrapstodon: הפעלת סיכומודון interactions: must_be_follower: חסימת התראות משאינם עוקבים must_be_following: חסימת התראות משאינם נעקבים diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml index 0e79c05766..35df26dc6e 100644 --- a/config/locales/simple_form.tr.yml +++ b/config/locales/simple_form.tr.yml @@ -111,6 +111,7 @@ tr: thumbnail: Sunucu bilginizin yanında gösterilen yaklaşık 2:1'lik görüntü. trendable_by_default: Öne çıkan içeriğin elle incelenmesini atla. Tekil öğeler sonrada öne çıkanlardan kaldırılabilir. trends: Öne çıkanlar, sunucunuzda ilgi toplayan gönderileri, etiketleri ve haber yazılarını gösterir. + wrapstodon: Yerel kullanıcılara, yıl boyunca Mastodon kullanımlarının eğlenceli bir özetini oluşturma imkanı sunun. Bu özellik, her yıl 10 Aralık ile 31 Aralık tarihleri arasında kullanılabilir ve yıl içinde en az bir adet Halka Açık veya Sessiz Halka Açık gönderi paylaşan ve en az bir hashtag kullanan kullanıcılara sunulur. form_challenge: current_password: Güvenli bir bölgeye giriyorsunuz imports: @@ -312,6 +313,7 @@ tr: thumbnail: Sunucu küçük resmi trendable_by_default: Ön incelemesiz öne çıkanlara izin ver trends: Öne çıkanları etkinleştir + wrapstodon: Wrapstodonu Etkinleştir interactions: must_be_follower: Takipçim olmayan kişilerden gelen bildirimleri engelle must_be_following: Takip etmediğim kişilerden gelen bildirimleri engelle diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml index d5c4525233..0bc0ffa413 100644 --- a/config/locales/simple_form.zh-CN.yml +++ b/config/locales/simple_form.zh-CN.yml @@ -111,6 +111,7 @@ zh-CN: thumbnail: 与服务器信息一并展示的约 2:1 比例的图像。 trendable_by_default: 跳过对热门内容的手工审核。个别项目仍可在之后从趋势中删除。 trends: 热门页中会显示正在你服务器上受到关注的嘟文、标签和新闻故事。 + wrapstodon: 为本站用户提供生成他们过去一年使用 Mastodon 情况的趣味总结的功能。此功能在每年12月10日至12月31日提供给这一年发布过至少1条公开嘟文(无论是否设置为在时间线上显示)及至少使用过1个话题标签的用户。 form_challenge: current_password: 你正在进入安全区域 imports: @@ -311,6 +312,7 @@ zh-CN: thumbnail: 本站缩略图 trendable_by_default: 允许在未审核的情况下将话题置为热门 trends: 启用热门 + wrapstodon: 启用 Wrapstodon 年度回顾 interactions: must_be_follower: 屏蔽来自未关注我的用户的通知 must_be_following: 屏蔽来自我未关注的用户的通知 diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 13f1e6a62f..0eb31119e8 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -848,6 +848,7 @@ tr: publish_statistics: İstatistikleri yayınla title: Keşfet trends: Öne çıkanlar + wrapstodon: Wrapstodon domain_blocks: all: Herkes için disabled: Hiç kimseye diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 9dffdcd249..87356403d2 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -834,6 +834,7 @@ zh-CN: publish_statistics: 发布统计数据 title: 发现 trends: 热门 + wrapstodon: Wrapstodon 年度回顾 domain_blocks: all: 对每个人 disabled: 不对任何人 diff --git a/db/migrate/20251217091936_add_feature_approval_policy_to_accounts.rb b/db/migrate/20251217091936_add_feature_approval_policy_to_accounts.rb new file mode 100644 index 0000000000..6d6e6e8205 --- /dev/null +++ b/db/migrate/20251217091936_add_feature_approval_policy_to_accounts.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddFeatureApprovalPolicyToAccounts < ActiveRecord::Migration[8.0] + def change + add_column :accounts, :feature_approval_policy, :integer, null: false, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 45c4067687..216c1e00cf 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[8.0].define(version: 2025_12_09_093813) do +ActiveRecord::Schema[8.0].define(version: 2025_12_17_091936) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -200,6 +200,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_09_093813) do t.string "attribution_domains", default: [], array: true t.string "following_url", default: "", null: false t.integer "id_scheme", default: 1 + t.integer "feature_approval_policy", default: 0, null: false t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin t.index "lower((username)::text), COALESCE(lower((domain)::text), ''::text)", name: "index_accounts_on_username_and_domain_lower", unique: true t.index ["domain", "id"], name: "index_accounts_on_domain_and_id" diff --git a/spec/lib/activitypub/parser/interaction_policy_parser_spec.rb b/spec/lib/activitypub/parser/interaction_policy_parser_spec.rb new file mode 100644 index 0000000000..0af445eab2 --- /dev/null +++ b/spec/lib/activitypub/parser/interaction_policy_parser_spec.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::Parser::InteractionPolicyParser do + subject { described_class.new(json_policy, account) } + + let(:account) do + Fabricate(:account, + uri: 'https://foo.test', + domain: 'foo.test', + followers_url: 'https://foo.test/followers', + following_url: 'https://foo.test/following') + end + + describe '#bitmap' do + context 'when no policy is given' do + let(:json_policy) { nil } + + it 'returns zero' do + expect(subject.bitmap).to be_zero + end + end + + context 'with special public URI' do + let(:json_policy) do + { + 'manualApproval' => [public_uri], + } + end + + shared_examples 'setting the public bit' do + it 'sets the public bit' do + expect(subject.bitmap).to eq 0b10 + end + end + + context 'when public URI is given in full' do + let(:public_uri) { 'https://www.w3.org/ns/activitystreams#Public' } + + it_behaves_like 'setting the public bit' + end + + context 'when public URI is abbreviated using namespace' do + let(:public_uri) { 'as:Public' } + + it_behaves_like 'setting the public bit' + end + + context 'when public URI is abbreviated without namespace' do + let(:public_uri) { 'Public' } + + it_behaves_like 'setting the public bit' + end + end + + context 'when mixing array and scalar values' do + let(:json_policy) do + { + 'automaticApproval' => 'https://foo.test', + 'manualApproval' => [ + 'https://foo.test/followers', + 'https://foo.test/following', + ], + } + end + + it 'sets the correct flags' do + expect(subject.bitmap).to eq 0b100000000000000001100 + end + end + + context 'when including individual actor URIs' do + let(:json_policy) do + { + 'automaticApproval' => ['https://example.com/actor', 'https://masto.example.com/@user'], + 'manualApproval' => ['https://masto.example.com/@other'], + } + end + + it 'sets the unsupported bit' do + expect(subject.bitmap).to eq 0b10000000000000001 + end + end + + context "when giving the affected actor's URI in addition to other supported URIs" do + let(:json_policy) do + { + 'manualApproval' => [ + 'https://foo.test/followers', + 'https://foo.test/following', + 'https://foo.test', + ], + } + end + + it 'is being ignored' do + expect(subject.bitmap).to eq 0b1100 + end + end + end +end diff --git a/spec/models/concerns/account/interaction_policy_concern_spec.rb b/spec/models/concerns/account/interaction_policy_concern_spec.rb new file mode 100644 index 0000000000..586a33b77f --- /dev/null +++ b/spec/models/concerns/account/interaction_policy_concern_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Account::InteractionPolicyConcern do + describe '#feature_policy_as_keys' do + context 'when account is local' do + context 'when account is discoverable' do + let(:account) { Fabricate(:account) } + + it 'returns public for automtatic and nothing for manual' do + expect(account.feature_policy_as_keys(:automatic)).to eq [:public] + expect(account.feature_policy_as_keys(:manual)).to eq [] + end + end + + context 'when account is not discoverable' do + let(:account) { Fabricate(:account, discoverable: false) } + + it 'returns empty arrays for both inputs' do + expect(account.feature_policy_as_keys(:automatic)).to eq [] + expect(account.feature_policy_as_keys(:manual)).to eq [] + end + end + end + + context 'when account is remote' do + let(:account) { Fabricate(:account, domain: 'example.com', feature_approval_policy: (0b0101 << 16) | 0b0010) } + + it 'returns the expected values' do + expect(account.feature_policy_as_keys(:automatic)).to eq ['unsupported_policy', 'followers'] + expect(account.feature_policy_as_keys(:manual)).to eq ['public'] + end + end + end + + describe '#feature_policy_for_account' do + context 'when account is remote' do + let(:account) { Fabricate(:account, domain: 'example.com', feature_approval_policy:) } + let(:feature_approval_policy) { (0b0101 << 16) | 0b0010 } + let(:other_account) { Fabricate(:account) } + + context 'when no policy is available' do + let(:feature_approval_policy) { 0 } + + context 'when both accounts are the same' do + it 'returns :automatic' do + expect(account.feature_policy_for_account(account)).to eq :automatic + end + end + + context 'with two different accounts' do + it 'returns :missing' do + expect(account.feature_policy_for_account(other_account)).to eq :missing + end + end + end + + context 'when the other account is not following the account' do + it 'returns :manual because of the public entry in the manual policy' do + expect(account.feature_policy_for_account(other_account)).to eq :manual + end + end + + context 'when the other account is following the account' do + before do + other_account.follow!(account) + end + + it 'returns :automatic because of the followers entry in the automatic policy' do + expect(account.feature_policy_for_account(other_account)).to eq :automatic + end + end + + context 'when the account falls into the unknown bucket' do + let(:feature_approval_policy) { (0b0001 << 16) | 0b0100 } + + it 'returns :automatic because of the followers entry in the automatic policy' do + expect(account.feature_policy_for_account(other_account)).to eq :unknown + end + end + end + end +end diff --git a/spec/serializers/activitypub/actor_serializer_spec.rb b/spec/serializers/activitypub/actor_serializer_spec.rb index ad24455953..734da6673c 100644 --- a/spec/serializers/activitypub/actor_serializer_spec.rb +++ b/spec/serializers/activitypub/actor_serializer_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe ActivityPub::ActorSerializer do - subject { serialized_record_json(record, described_class) } + subject { serialized_record_json(record, described_class, adapter: ActivityPub::Adapter) } describe '#type' do context 'with the instance actor' do @@ -36,4 +36,39 @@ RSpec.describe ActivityPub::ActorSerializer do it { is_expected.to include('type' => 'Person') } end end + + describe '#interactionPolicy' do + let(:record) { Fabricate(:account) } + + # TODO: Remove when feature flag is removed + context 'when collections feature is disabled?' do + it 'is not present' do + expect(subject).to_not have_key('interactionPolicy') + end + end + + context 'when collections feature is enabled', feature: :collections do + context 'when actor is discoverable' do + it 'includes an automatic policy allowing everyone' do + expect(subject).to include('interactionPolicy' => { + 'canFeature' => { + 'automaticApproval' => ['https://www.w3.org/ns/activitystreams#Public'], + }, + }) + end + end + + context 'when actor is not discoverable' do + let(:record) { Fabricate(:account, discoverable: false) } + + it 'includes an automatic policy limited to the actor itself' do + expect(subject).to include('interactionPolicy' => { + 'canFeature' => { + 'automaticApproval' => [ActivityPub::TagManager.instance.uri_for(record)], + }, + }) + end + end + end + end end diff --git a/spec/serializers/rest/account_serializer_spec.rb b/spec/serializers/rest/account_serializer_spec.rb index 5fd4f8d706..998de6b0fb 100644 --- a/spec/serializers/rest/account_serializer_spec.rb +++ b/spec/serializers/rest/account_serializer_spec.rb @@ -3,12 +3,18 @@ require 'rails_helper' RSpec.describe REST::AccountSerializer do - subject { serialized_record_json(account, described_class) } + subject do + serialized_record_json(account, described_class, options: { + scope: current_user, + scope_name: :current_user, + }) + end let(:default_datetime) { DateTime.new(2024, 11, 28, 16, 20, 0) } let(:role) { Fabricate(:user_role, name: 'Role', highlighted: true) } let(:user) { Fabricate(:user, role: role) } let(:account) { user.account } + let(:current_user) { Fabricate(:user) } context 'when the account is suspended' do before do @@ -68,4 +74,51 @@ RSpec.describe REST::AccountSerializer do expect(subject['last_status_at']).to eq('2024-11-28') end end + + describe '#feature_approval' do + # TODO: Remove when feature flag is removed + context 'when collections feature is disabled' do + it 'does not include the approval policy' do + expect(subject).to_not have_key('feature_approval') + end + end + + context 'when collections feature is enabled', feature: :collections do + context 'when account is local' do + context 'when account is discoverable' do + it 'includes a policy that allows featuring' do + expect(subject['feature_approval']).to include({ + 'automatic' => ['public'], + 'manual' => [], + 'current_user' => 'automatic', + }) + end + end + + context 'when account is not discoverable' do + let(:account) { Fabricate(:account, discoverable: false) } + + it 'includes a policy that disallows featuring' do + expect(subject['feature_approval']).to include({ + 'automatic' => [], + 'manual' => [], + 'current_user' => 'denied', + }) + end + end + end + + context 'when account is remote' do + let(:account) { Fabricate(:account, domain: 'example.com', feature_approval_policy: 0b11000000000000000010) } + + it 'includes the matching policy' do + expect(subject['feature_approval']).to include({ + 'automatic' => ['followers', 'following'], + 'manual' => ['public'], + 'current_user' => 'manual', + }) + end + end + end + end end diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index eb0ba3524a..b2c6bc17b2 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -272,6 +272,49 @@ RSpec.describe ActivityPub::ProcessAccountService do end end + context 'with interaction policy' do + let(:payload) do + { + id: 'https://foo.test', + type: 'Actor', + inbox: 'https://foo.test/inbox', + followers: 'https://foo.test/followers', + following: 'https://foo.test/following', + interactionPolicy: { + canFeature: { + automaticApproval: 'https://foo.test', + manualApproval: [ + 'https://foo.test/followers', + 'https://foo.test/following', + ], + }, + }, + }.with_indifferent_access + end + + before do + stub_request(:get, %r{^https://foo\.test/follow}) + .to_return(status: 200, body: '', headers: {}) + end + + # TODO: Remove when feature flag is removed + context 'when collections feature is disabled' do + it 'does not set the interaction policy' do + account = subject.call('user1', 'foo.test', payload) + + expect(account.feature_approval_policy).to be_zero + end + end + + context 'when collections feature is enabled', feature: :collections do + it 'sets the interaction policy to the correct value' do + account = subject.call('user1', 'foo.test', payload) + + expect(account.feature_approval_policy).to eq 0b100000000000000001100 + end + end + end + private def create_some_remote_accounts diff --git a/yarn.lock b/yarn.lock index d5758ced16..6226be07a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2079,6 +2079,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/aix-ppc64@npm:0.27.2" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/android-arm64@npm:0.25.5" @@ -2086,6 +2093,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/android-arm64@npm:0.27.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/android-arm@npm:0.25.5" @@ -2093,6 +2107,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/android-arm@npm:0.27.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/android-x64@npm:0.25.5" @@ -2100,6 +2121,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/android-x64@npm:0.27.2" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/darwin-arm64@npm:0.25.5" @@ -2107,6 +2135,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/darwin-arm64@npm:0.27.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/darwin-x64@npm:0.25.5" @@ -2114,6 +2149,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/darwin-x64@npm:0.27.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/freebsd-arm64@npm:0.25.5" @@ -2121,6 +2163,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/freebsd-arm64@npm:0.27.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/freebsd-x64@npm:0.25.5" @@ -2128,6 +2177,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/freebsd-x64@npm:0.27.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-arm64@npm:0.25.5" @@ -2135,6 +2191,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-arm64@npm:0.27.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-arm@npm:0.25.5" @@ -2142,6 +2205,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-arm@npm:0.27.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-ia32@npm:0.25.5" @@ -2149,6 +2219,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-ia32@npm:0.27.2" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-loong64@npm:0.25.5" @@ -2156,6 +2233,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-loong64@npm:0.27.2" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-mips64el@npm:0.25.5" @@ -2163,6 +2247,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-mips64el@npm:0.27.2" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-ppc64@npm:0.25.5" @@ -2170,6 +2261,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-ppc64@npm:0.27.2" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-riscv64@npm:0.25.5" @@ -2177,6 +2275,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-riscv64@npm:0.27.2" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-s390x@npm:0.25.5" @@ -2184,6 +2289,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-s390x@npm:0.27.2" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/linux-x64@npm:0.25.5" @@ -2191,6 +2303,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/linux-x64@npm:0.27.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-arm64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/netbsd-arm64@npm:0.25.5" @@ -2198,6 +2317,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/netbsd-arm64@npm:0.27.2" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/netbsd-x64@npm:0.25.5" @@ -2205,6 +2331,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/netbsd-x64@npm:0.27.2" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-arm64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/openbsd-arm64@npm:0.25.5" @@ -2212,6 +2345,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/openbsd-arm64@npm:0.27.2" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/openbsd-x64@npm:0.25.5" @@ -2219,6 +2359,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/openbsd-x64@npm:0.27.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/openharmony-arm64@npm:0.27.2" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/sunos-x64@npm:0.25.5" @@ -2226,6 +2380,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/sunos-x64@npm:0.27.2" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/win32-arm64@npm:0.25.5" @@ -2233,6 +2394,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/win32-arm64@npm:0.27.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/win32-ia32@npm:0.25.5" @@ -2240,6 +2408,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/win32-ia32@npm:0.27.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/win32-x64@npm:0.25.5" @@ -2247,6 +2422,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.27.2": + version: 0.27.2 + resolution: "@esbuild/win32-x64@npm:0.27.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.7.0, @eslint-community/eslint-utils@npm:^4.8.0": version: 4.9.0 resolution: "@eslint-community/eslint-utils@npm:4.9.0" @@ -3725,6 +3907,16 @@ __metadata: languageName: node linkType: hard +"@storybook/icons@npm:^2.0.0": + version: 2.0.1 + resolution: "@storybook/icons@npm:2.0.1" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/df2bbf1a5b50f12ab1bf78cae6de4dbf7c49df0e3a5f845553b51b20adbe8386a09fd172ea60342379f9284bb528cba2d0e2659cae6eb8d015cf92c8b32f1222 + languageName: node + linkType: hard + "@storybook/react-dom-shim@npm:10.0.6": version: 10.0.6 resolution: "@storybook/react-dom-shim@npm:10.0.6" @@ -4939,25 +5131,6 @@ __metadata: languageName: node linkType: hard -"@vitest/mocker@npm:3.2.4": - version: 3.2.4 - resolution: "@vitest/mocker@npm:3.2.4" - dependencies: - "@vitest/spy": "npm:3.2.4" - estree-walker: "npm:^3.0.3" - magic-string: "npm:^0.30.17" - peerDependencies: - msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - checksum: 10c0/f7a4aea19bbbf8f15905847ee9143b6298b2c110f8b64789224cb0ffdc2e96f9802876aa2ca83f1ec1b6e1ff45e822abb34f0054c24d57b29ab18add06536ccd - languageName: node - linkType: hard - "@vitest/mocker@npm:4.0.15": version: 4.0.15 resolution: "@vitest/mocker@npm:4.0.15" @@ -5695,6 +5868,15 @@ __metadata: languageName: node linkType: hard +"bundle-name@npm:^4.1.0": + version: 4.1.0 + resolution: "bundle-name@npm:4.1.0" + dependencies: + run-applescript: "npm:^7.0.0" + checksum: 10c0/8e575981e79c2bcf14d8b1c027a3775c095d362d1382312f444a7c861b0e21513c0bd8db5bd2b16e50ba0709fa622d4eab6b53192d222120305e68359daece29 + languageName: node + linkType: hard + "bytes@npm:^3.1.2, bytes@npm:~3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -6384,6 +6566,23 @@ __metadata: languageName: node linkType: hard +"default-browser-id@npm:^5.0.0": + version: 5.0.1 + resolution: "default-browser-id@npm:5.0.1" + checksum: 10c0/5288b3094c740ef3a86df9b999b04ff5ba4dee6b64e7b355c0fff5217752c8c86908d67f32f6cba9bb4f9b7b61a1b640c0a4f9e34c57e0ff3493559a625245ee + languageName: node + linkType: hard + +"default-browser@npm:^5.2.1": + version: 5.4.0 + resolution: "default-browser@npm:5.4.0" + dependencies: + bundle-name: "npm:^4.1.0" + default-browser-id: "npm:^5.0.0" + checksum: 10c0/a49ddd0c7b1a319163f64a5fc68ebb45a98548ea23a3155e04518f026173d85cfa2f451b646366c36c8f70b01e4cb773e23d1d22d2c61d8b84e5fbf151b4b609 + languageName: node + linkType: hard + "define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": version: 1.1.4 resolution: "define-data-property@npm:1.1.4" @@ -6402,6 +6601,13 @@ __metadata: languageName: node linkType: hard +"define-lazy-prop@npm:^3.0.0": + version: 3.0.0 + resolution: "define-lazy-prop@npm:3.0.0" + checksum: 10c0/5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49 + languageName: node + linkType: hard + "define-properties@npm:^1.1.3, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" @@ -6887,7 +7093,96 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0, esbuild@npm:^0.25.0": +"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0": + version: 0.27.2 + resolution: "esbuild@npm:0.27.2" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.2" + "@esbuild/android-arm": "npm:0.27.2" + "@esbuild/android-arm64": "npm:0.27.2" + "@esbuild/android-x64": "npm:0.27.2" + "@esbuild/darwin-arm64": "npm:0.27.2" + "@esbuild/darwin-x64": "npm:0.27.2" + "@esbuild/freebsd-arm64": "npm:0.27.2" + "@esbuild/freebsd-x64": "npm:0.27.2" + "@esbuild/linux-arm": "npm:0.27.2" + "@esbuild/linux-arm64": "npm:0.27.2" + "@esbuild/linux-ia32": "npm:0.27.2" + "@esbuild/linux-loong64": "npm:0.27.2" + "@esbuild/linux-mips64el": "npm:0.27.2" + "@esbuild/linux-ppc64": "npm:0.27.2" + "@esbuild/linux-riscv64": "npm:0.27.2" + "@esbuild/linux-s390x": "npm:0.27.2" + "@esbuild/linux-x64": "npm:0.27.2" + "@esbuild/netbsd-arm64": "npm:0.27.2" + "@esbuild/netbsd-x64": "npm:0.27.2" + "@esbuild/openbsd-arm64": "npm:0.27.2" + "@esbuild/openbsd-x64": "npm:0.27.2" + "@esbuild/openharmony-arm64": "npm:0.27.2" + "@esbuild/sunos-x64": "npm:0.27.2" + "@esbuild/win32-arm64": "npm:0.27.2" + "@esbuild/win32-ia32": "npm:0.27.2" + "@esbuild/win32-x64": "npm:0.27.2" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/cf83f626f55500f521d5fe7f4bc5871bec240d3deb2a01fbd379edc43b3664d1167428738a5aad8794b35d1cca985c44c375b1cd38a2ca613c77ced2c83aafcd + languageName: node + linkType: hard + +"esbuild@npm:^0.25.0": version: 0.25.5 resolution: "esbuild@npm:0.25.5" dependencies: @@ -8565,6 +8860,15 @@ __metadata: languageName: node linkType: hard +"is-docker@npm:^3.0.0": + version: 3.0.0 + resolution: "is-docker@npm:3.0.0" + bin: + is-docker: cli.js + checksum: 10c0/d2c4f8e6d3e34df75a5defd44991b6068afad4835bb783b902fa12d13ebdb8f41b2a199dcb0b5ed2cb78bfee9e4c0bbdb69c2d9646f4106464674d3e697a5856 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -8615,6 +8919,17 @@ __metadata: languageName: node linkType: hard +"is-inside-container@npm:^1.0.0": + version: 1.0.0 + resolution: "is-inside-container@npm:1.0.0" + dependencies: + is-docker: "npm:^3.0.0" + bin: + is-inside-container: cli.js + checksum: 10c0/a8efb0e84f6197e6ff5c64c52890fa9acb49b7b74fed4da7c95383965da6f0fa592b4dbd5e38a79f87fc108196937acdbcd758fcefc9b140e479b39ce1fcd1cd + languageName: node + linkType: hard + "is-map@npm:^2.0.3": version: 2.0.3 resolution: "is-map@npm:2.0.3" @@ -8809,6 +9124,15 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^3.1.0": + version: 3.1.0 + resolution: "is-wsl@npm:3.1.0" + dependencies: + is-inside-container: "npm:^1.0.0" + checksum: 10c0/d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947 + languageName: node + linkType: hard + "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -10098,6 +10422,18 @@ __metadata: languageName: node linkType: hard +"open@npm:^10.2.0": + version: 10.2.0 + resolution: "open@npm:10.2.0" + dependencies: + default-browser: "npm:^5.2.1" + define-lazy-prop: "npm:^3.0.0" + is-inside-container: "npm:^1.0.0" + wsl-utils: "npm:^0.1.0" + checksum: 10c0/5a36d0c1fd2f74ce553beb427ca8b8494b623fc22c6132d0c1688f246a375e24584ea0b44c67133d9ab774fa69be8e12fbe1ff12504b1142bd960fb09671948f + languageName: node + linkType: hard + "open@npm:^8.0.0": version: 8.4.2 resolution: "open@npm:8.4.2" @@ -12144,6 +12480,13 @@ __metadata: languageName: node linkType: hard +"run-applescript@npm:^7.0.0": + version: 7.1.0 + resolution: "run-applescript@npm:7.1.0" + checksum: 10c0/ab826c57c20f244b2ee807704b1ef4ba7f566aa766481ae5922aac785e2570809e297c69afcccc3593095b538a8a77d26f2b2e9a1d9dffee24e0e039502d1a03 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -12209,8 +12552,8 @@ __metadata: linkType: hard "sass@npm:^1.62.1, sass@npm:^1.70.0": - version: 1.96.0 - resolution: "sass@npm:1.96.0" + version: 1.97.0 + resolution: "sass@npm:1.97.0" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -12221,7 +12564,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: 10c0/a932054bcee6935757417af6072d31b65ce3557798a53351b3e1369d7f06e24b0ec211e1617bdaaee998b429a44bf0f52acd240fd47f88422d5bc241eeb71672 + checksum: 10c0/4c0e2596131054089beeeb6e98f7318beb909c4775187690656cbb85a45153d04eecd970e6a4431fe47dc94f9749cf8d58c9c5e59055d2cae39f4887a4bb6104 languageName: node linkType: hard @@ -12762,19 +13105,20 @@ __metadata: linkType: hard "storybook@npm:^10.0.5": - version: 10.0.5 - resolution: "storybook@npm:10.0.5" + version: 10.1.10 + resolution: "storybook@npm:10.1.10" dependencies: "@storybook/global": "npm:^5.0.0" - "@storybook/icons": "npm:^1.6.0" + "@storybook/icons": "npm:^2.0.0" "@testing-library/jest-dom": "npm:^6.6.3" "@testing-library/user-event": "npm:^14.6.1" "@vitest/expect": "npm:3.2.4" - "@vitest/mocker": "npm:3.2.4" "@vitest/spy": "npm:3.2.4" - esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0" + esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0" + open: "npm:^10.2.0" recast: "npm:^0.23.5" semver: "npm:^7.6.2" + use-sync-external-store: "npm:^1.5.0" ws: "npm:^8.18.0" peerDependencies: prettier: ^2 || ^3 @@ -12783,7 +13127,7 @@ __metadata: optional: true bin: storybook: ./dist/bin/dispatcher.js - checksum: 10c0/ea4bcdbc8d793f53970fe2e72de805bfd5b0872d3640f7526bdf42fbe0114f225c09f3683ab011ac08b5240450fd7726f17c5210d929c6f261dadc851ee09eec + checksum: 10c0/beff5472ee86a995cbde2789b2aabd941f823e31ca6957bb4434cb8ee3d3703cf1248e44f4b4d402416a52bfee94677e74f233cc906487901e831e8ab610defa languageName: node linkType: hard @@ -13994,12 +14338,12 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.4.0": - version: 1.4.0 - resolution: "use-sync-external-store@npm:1.4.0" +"use-sync-external-store@npm:^1.4.0, use-sync-external-store@npm:^1.5.0": + version: 1.6.0 + resolution: "use-sync-external-store@npm:1.6.0" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/ec011a5055962c0f6b509d6e78c0b143f8cd069890ae370528753053c55e3b360d3648e76cfaa854faa7a59eb08d6c5fb1015e60ffde9046d32f5b2a295acea5 + checksum: 10c0/35e1179f872a53227bdf8a827f7911da4c37c0f4091c29b76b1e32473d1670ebe7bcd880b808b7549ba9a5605c233350f800ffab963ee4a4ee346ee983b6019b languageName: node linkType: hard @@ -14085,8 +14429,8 @@ __metadata: linkType: hard "vite-tsconfig-paths@npm:^6.0.0": - version: 6.0.2 - resolution: "vite-tsconfig-paths@npm:6.0.2" + version: 6.0.3 + resolution: "vite-tsconfig-paths@npm:6.0.3" dependencies: debug: "npm:^4.1.1" globrex: "npm:^0.1.2" @@ -14096,7 +14440,7 @@ __metadata: peerDependenciesMeta: vite: optional: true - checksum: 10c0/878189e38a253b699998f94706b15718a03d59467b091e064f33090240f9ccfa4bf273c3b30b5f9711822c56a58b786c3e6c6cebb8859e56ec5ab49e360ff8c0 + checksum: 10c0/75cfe470f1ec0e776b2aec1d2e71316d5e1214f485fce7daaed4e4789d6f667881fb85d98129b6463a5b70c7524ef258b401c4871ed8b6318ac45cc892ee778a languageName: node linkType: hard @@ -14693,6 +15037,15 @@ __metadata: languageName: node linkType: hard +"wsl-utils@npm:^0.1.0": + version: 0.1.0 + resolution: "wsl-utils@npm:0.1.0" + dependencies: + is-wsl: "npm:^3.1.0" + checksum: 10c0/44318f3585eb97be994fc21a20ddab2649feaf1fbe893f1f866d936eea3d5f8c743bec6dc02e49fbdd3c0e69e9b36f449d90a0b165a4f47dd089747af4cf2377 + languageName: node + linkType: hard + "xml-name-validator@npm:^5.0.0": version: 5.0.0 resolution: "xml-name-validator@npm:5.0.0"