Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8c9dc985b9
commit
0388886f94
20 changed files with 162 additions and 138 deletions
|
@ -26,6 +26,7 @@ export function getBoardSortableDefaultOptions(obj) {
|
|||
scrollSpeed: 20,
|
||||
onStart: sortableStart,
|
||||
onEnd: sortableEnd,
|
||||
fallbackTolerance: 1,
|
||||
});
|
||||
|
||||
Object.keys(obj).forEach(key => {
|
||||
|
|
|
@ -11,7 +11,7 @@ module SpammableActions
|
|||
end
|
||||
|
||||
def mark_as_spam
|
||||
if Spam::MarkAsSpamService.new(spammable: spammable).execute
|
||||
if Spam::MarkAsSpamService.new(target: spammable).execute
|
||||
redirect_to spammable_path, notice: _("%{spammable_titlecase} was submitted to Akismet successfully.") % { spammable_titlecase: spammable.spammable_entity_type.titlecase }
|
||||
else
|
||||
redirect_to spammable_path, alert: _('Error with Akismet. Please check the logs for more info.')
|
||||
|
@ -42,7 +42,7 @@ module SpammableActions
|
|||
end
|
||||
|
||||
format.json do
|
||||
locals = { spammable: spammable, script: false, has_submit: false }
|
||||
locals = { target: spammable, script: false, has_submit: false }
|
||||
recaptcha_html = render_to_string(partial: 'shared/recaptcha_form', formats: :html, locals: locals)
|
||||
|
||||
render json: { recaptcha_html: recaptcha_html }
|
||||
|
|
|
@ -24,7 +24,7 @@ module Mutations
|
|||
private
|
||||
|
||||
def mark_as_spam(snippet)
|
||||
Spam::MarkAsSpamService.new(spammable: snippet).execute
|
||||
Spam::MarkAsSpamService.new(target: snippet).execute
|
||||
end
|
||||
|
||||
def authorized_resource?(snippet)
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AkismetMethods
|
||||
def spammable_owner
|
||||
@user ||= User.find(spammable.author_id)
|
||||
def target_owner
|
||||
@user ||= User.find(target.author_id)
|
||||
end
|
||||
|
||||
def akismet
|
||||
@akismet ||= Spam::AkismetService.new(
|
||||
spammable_owner.name,
|
||||
spammable_owner.email,
|
||||
spammable.try(:spammable_text) || spammable&.text,
|
||||
target_owner.name,
|
||||
target_owner.email,
|
||||
target.try(:spammable_text) || target&.text,
|
||||
options
|
||||
)
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# SpamCheckMethods
|
||||
#
|
||||
# Provide helper methods for checking if a given spammable object has
|
||||
# Provide helper methods for checking if a given target spammable object has
|
||||
# potential spam data.
|
||||
#
|
||||
# Dependencies:
|
||||
|
@ -18,13 +18,13 @@ module SpamCheckMethods
|
|||
end
|
||||
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
# In order to be proceed to the spam check process, @spammable has to be
|
||||
# In order to be proceed to the spam check process, @target has to be
|
||||
# a dirty instance, which means it should be already assigned with the new
|
||||
# attribute values.
|
||||
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
def spam_check(spammable, user)
|
||||
Spam::SpamCheckService.new(
|
||||
spammable: spammable,
|
||||
target: spammable,
|
||||
request: @request
|
||||
).execute(
|
||||
api: @api,
|
||||
|
|
|
@ -4,25 +4,23 @@ module Spam
|
|||
class HamService
|
||||
include AkismetMethods
|
||||
|
||||
attr_accessor :spam_log, :options
|
||||
attr_accessor :target, :options
|
||||
|
||||
def initialize(spam_log)
|
||||
@spam_log = spam_log
|
||||
@user = spam_log.user
|
||||
def initialize(target)
|
||||
@target = target
|
||||
@user = target.user
|
||||
@options = {
|
||||
ip_address: spam_log.source_ip,
|
||||
user_agent: spam_log.user_agent
|
||||
ip_address: target.source_ip,
|
||||
user_agent: target.user_agent
|
||||
}
|
||||
end
|
||||
|
||||
def execute
|
||||
if akismet.submit_ham
|
||||
spam_log.update_attribute(:submitted_as_ham, true)
|
||||
target.update_attribute(:submitted_as_ham, true)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :spammable, :spam_log
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,21 +4,21 @@ module Spam
|
|||
class MarkAsSpamService
|
||||
include ::AkismetMethods
|
||||
|
||||
attr_accessor :spammable, :options
|
||||
attr_accessor :target, :options
|
||||
|
||||
def initialize(spammable:)
|
||||
@spammable = spammable
|
||||
def initialize(target:)
|
||||
@target = target
|
||||
@options = {}
|
||||
|
||||
@options[:ip_address] = @spammable.ip_address
|
||||
@options[:user_agent] = @spammable.user_agent
|
||||
@options[:ip_address] = @target.ip_address
|
||||
@options[:user_agent] = @target.user_agent
|
||||
end
|
||||
|
||||
def execute
|
||||
return unless spammable.submittable_as_spam?
|
||||
return unless target.submittable_as_spam?
|
||||
return unless akismet.submit_spam
|
||||
|
||||
spammable.user_agent_detail.update_attribute(:submitted, true)
|
||||
target.user_agent_detail.update_attribute(:submitted, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,11 +4,11 @@ module Spam
|
|||
class SpamCheckService
|
||||
include AkismetMethods
|
||||
|
||||
attr_accessor :spammable, :request, :options
|
||||
attr_accessor :target, :request, :options
|
||||
attr_reader :spam_log
|
||||
|
||||
def initialize(spammable:, request:)
|
||||
@spammable = spammable
|
||||
def initialize(target:, request:)
|
||||
@target = target
|
||||
@request = request
|
||||
@options = {}
|
||||
|
||||
|
@ -17,8 +17,8 @@ module Spam
|
|||
@options[:user_agent] = @request.env['HTTP_USER_AGENT']
|
||||
@options[:referrer] = @request.env['HTTP_REFERRER']
|
||||
else
|
||||
@options[:ip_address] = @spammable.ip_address
|
||||
@options[:user_agent] = @spammable.user_agent
|
||||
@options[:ip_address] = @target.ip_address
|
||||
@options[:user_agent] = @target.user_agent
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -29,10 +29,10 @@ module Spam
|
|||
SpamLog.verify_recaptcha!(user_id: user_id, id: spam_log_id)
|
||||
else
|
||||
# Otherwise, it goes to Akismet for spam check.
|
||||
# If so, it assigns spammable object as "spam" and creates a SpamLog record.
|
||||
# If so, it assigns target spammable object as "spam" and creates a SpamLog record.
|
||||
possible_spam = check(api)
|
||||
spammable.spam = possible_spam unless spammable.allow_possible_spam?
|
||||
spammable.spam_log = spam_log
|
||||
target.spam = possible_spam unless target.allow_possible_spam?
|
||||
target.spam_log = spam_log
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -48,18 +48,18 @@ module Spam
|
|||
end
|
||||
|
||||
def check_for_spam?
|
||||
spammable.check_for_spam?
|
||||
target.check_for_spam?
|
||||
end
|
||||
|
||||
def create_spam_log(api)
|
||||
@spam_log = SpamLog.create!(
|
||||
{
|
||||
user_id: spammable.author_id,
|
||||
title: spammable.spam_title,
|
||||
description: spammable.spam_description,
|
||||
user_id: target.author_id,
|
||||
title: target.spam_title,
|
||||
description: target.spam_description,
|
||||
source_ip: options[:ip_address],
|
||||
user_agent: options[:user_agent],
|
||||
noteable_type: spammable.class.to_s,
|
||||
noteable_type: target.class.to_s,
|
||||
via_api: api
|
||||
}
|
||||
)
|
||||
|
|
5
changelogs/unreleased/refactoring-entities-file-16.yml
Normal file
5
changelogs/unreleased/refactoring-entities-file-16.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Separate service entities into own class files
|
||||
merge_request: 24936
|
||||
author: Rajendra Kadam
|
||||
type: added
|
|
@ -89,9 +89,9 @@ def lint_commits(commits)
|
|||
end
|
||||
|
||||
if squash_mr?
|
||||
multi_line_commit_linter = commit_linters.detect { |commit_linter| commit_linter.multi_line? }
|
||||
multi_line_commit_linter = commit_linters.detect { |commit_linter| !commit_linter.merge? && commit_linter.multi_line? }
|
||||
|
||||
if multi_line_commit_linter && multi_line_commit_linter.lint.failed?
|
||||
if multi_line_commit_linter && multi_line_commit_linter.failed?
|
||||
warn_or_fail_commits(multi_line_commit_linter)
|
||||
fail_message('The commit message that will be used in the squash commit does not meet our Git commit message standards.')
|
||||
else
|
||||
|
|
|
@ -163,78 +163,6 @@ module API
|
|||
end
|
||||
end
|
||||
|
||||
class GlobalNotificationSetting < NotificationSetting
|
||||
expose :notification_email do |notification_setting, options|
|
||||
notification_setting.user.notification_email
|
||||
end
|
||||
end
|
||||
|
||||
class ProjectServiceBasic < Grape::Entity
|
||||
expose :id, :title
|
||||
expose :slug do |service|
|
||||
service.to_param.dasherize
|
||||
end
|
||||
expose :created_at, :updated_at, :active
|
||||
expose :commit_events, :push_events, :issues_events, :confidential_issues_events
|
||||
expose :merge_requests_events, :tag_push_events, :note_events
|
||||
expose :confidential_note_events, :pipeline_events, :wiki_page_events
|
||||
expose :job_events, :comment_on_event_enabled
|
||||
end
|
||||
|
||||
class ProjectService < ProjectServiceBasic
|
||||
# Expose serialized properties
|
||||
expose :properties do |service, options|
|
||||
# TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
|
||||
if service.data_fields_present?
|
||||
service.data_fields.as_json.slice(*service.api_field_names)
|
||||
else
|
||||
service.properties.slice(*service.api_field_names)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ProjectWithAccess < Project
|
||||
expose :permissions do
|
||||
expose :project_access, using: Entities::ProjectAccess do |project, options|
|
||||
if options[:project_members]
|
||||
options[:project_members].find { |member| member.source_id == project.id }
|
||||
else
|
||||
project.project_member(options[:current_user])
|
||||
end
|
||||
end
|
||||
|
||||
expose :group_access, using: Entities::GroupAccess do |project, options|
|
||||
if project.group
|
||||
if options[:group_members]
|
||||
options[:group_members].find { |member| member.source_id == project.namespace_id }
|
||||
else
|
||||
project.group.highest_group_member(options[:current_user])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def self.preload_relation(projects_relation, options = {})
|
||||
relation = super(projects_relation, options)
|
||||
project_ids = relation.select('projects.id')
|
||||
namespace_ids = relation.select(:namespace_id)
|
||||
|
||||
options[:project_members] = options[:current_user]
|
||||
.project_members
|
||||
.where(source_id: project_ids)
|
||||
.preload(:source, user: [notification_settings: :source])
|
||||
|
||||
options[:group_members] = options[:current_user]
|
||||
.group_members
|
||||
.where(source_id: namespace_ids)
|
||||
.preload(:source, user: [notification_settings: :source])
|
||||
|
||||
relation
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
|
||||
class LabelBasic < Grape::Entity
|
||||
expose :id, :name, :color, :description, :description_html, :text_color
|
||||
end
|
||||
|
|
11
lib/api/entities/global_notification_setting.rb
Normal file
11
lib/api/entities/global_notification_setting.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class GlobalNotificationSetting < Entities::NotificationSetting
|
||||
expose :notification_email do |notification_setting, options|
|
||||
notification_setting.user.notification_email
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
lib/api/entities/project_service.rb
Normal file
17
lib/api/entities/project_service.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class ProjectService < Entities::ProjectServiceBasic
|
||||
# Expose serialized properties
|
||||
expose :properties do |service, options|
|
||||
# TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
|
||||
if service.data_fields_present?
|
||||
service.data_fields.as_json.slice(*service.api_field_names)
|
||||
else
|
||||
service.properties.slice(*service.api_field_names)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
lib/api/entities/project_service_basic.rb
Normal file
17
lib/api/entities/project_service_basic.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class ProjectServiceBasic < Grape::Entity
|
||||
expose :id, :title
|
||||
expose :slug do |service|
|
||||
service.to_param.dasherize
|
||||
end
|
||||
expose :created_at, :updated_at, :active
|
||||
expose :commit_events, :push_events, :issues_events, :confidential_issues_events
|
||||
expose :merge_requests_events, :tag_push_events, :note_events
|
||||
expose :confidential_note_events, :pipeline_events, :wiki_page_events
|
||||
expose :job_events, :comment_on_event_enabled
|
||||
end
|
||||
end
|
||||
end
|
47
lib/api/entities/project_with_access.rb
Normal file
47
lib/api/entities/project_with_access.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class ProjectWithAccess < Project
|
||||
expose :permissions do
|
||||
expose :project_access, using: Entities::ProjectAccess do |project, options|
|
||||
if options[:project_members]
|
||||
options[:project_members].find { |member| member.source_id == project.id }
|
||||
else
|
||||
project.project_member(options[:current_user])
|
||||
end
|
||||
end
|
||||
|
||||
expose :group_access, using: Entities::GroupAccess do |project, options|
|
||||
if project.group
|
||||
if options[:group_members]
|
||||
options[:group_members].find { |member| member.source_id == project.namespace_id }
|
||||
else
|
||||
project.group.highest_group_member(options[:current_user])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def self.preload_relation(projects_relation, options = {})
|
||||
relation = super(projects_relation, options)
|
||||
project_ids = relation.select('projects.id')
|
||||
namespace_ids = relation.select(:namespace_id)
|
||||
|
||||
options[:project_members] = options[:current_user]
|
||||
.project_members
|
||||
.where(source_id: project_ids)
|
||||
.preload(:source, user: [notification_settings: :source])
|
||||
|
||||
options[:group_members] = options[:current_user]
|
||||
.group_members
|
||||
.where(source_id: namespace_ids)
|
||||
.preload(:source, user: [notification_settings: :source])
|
||||
|
||||
relation
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,10 +4,10 @@ require 'spec_helper'
|
|||
|
||||
describe Spam::HamService do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let!(:spam_log) { create(:spam_log, user: user, submitted_as_ham: false) }
|
||||
let!(:target) { create(:spam_log, user: user, submitted_as_ham: false) }
|
||||
let(:fake_akismet_service) { double(:akismet_service) }
|
||||
|
||||
subject { described_class.new(spam_log) }
|
||||
subject { described_class.new(target) }
|
||||
|
||||
before do
|
||||
allow(Spam::AkismetService).to receive(:new).and_return fake_akismet_service
|
||||
|
@ -24,23 +24,23 @@ describe Spam::HamService do
|
|||
end
|
||||
|
||||
it 'does not update the record' do
|
||||
expect { subject.execute }.not_to change { spam_log.submitted_as_ham }
|
||||
expect { subject.execute }.not_to change { target.submitted_as_ham }
|
||||
end
|
||||
|
||||
context 'if spam log record has already been marked as spam' do
|
||||
before do
|
||||
spam_log.update_attribute(:submitted_as_ham, true)
|
||||
target.update_attribute(:submitted_as_ham, true)
|
||||
end
|
||||
|
||||
it 'does not update the record' do
|
||||
expect { subject.execute }.not_to change { spam_log.submitted_as_ham }
|
||||
expect { subject.execute }.not_to change { target.submitted_as_ham }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'Akismet ham submission is successful' do
|
||||
before do
|
||||
spam_log.update_attribute(:submitted_as_ham, false)
|
||||
target.update_attribute(:submitted_as_ham, false)
|
||||
allow(fake_akismet_service).to receive(:submit_ham).and_return true
|
||||
end
|
||||
|
||||
|
@ -49,7 +49,7 @@ describe Spam::HamService do
|
|||
end
|
||||
|
||||
it 'updates the record' do
|
||||
expect { subject.execute }.to change { spam_log.submitted_as_ham }.from(false).to(true)
|
||||
expect { subject.execute }.to change { target.submitted_as_ham }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,19 +4,19 @@ require 'spec_helper'
|
|||
|
||||
describe Spam::MarkAsSpamService do
|
||||
let(:user_agent_detail) { build(:user_agent_detail) }
|
||||
let(:spammable) { build(:issue, user_agent_detail: user_agent_detail) }
|
||||
let(:target) { build(:issue, user_agent_detail: user_agent_detail) }
|
||||
let(:fake_akismet_service) { double(:akismet_service, submit_spam: true) }
|
||||
|
||||
subject { described_class.new(spammable: spammable) }
|
||||
subject { described_class.new(target: target) }
|
||||
|
||||
describe '#execute' do
|
||||
before do
|
||||
allow(subject).to receive(:akismet).and_return(fake_akismet_service)
|
||||
end
|
||||
|
||||
context 'when the spammable object is not submittable' do
|
||||
context 'when the target object is not submittable' do
|
||||
before do
|
||||
allow(spammable).to receive(:submittable_as_spam?).and_return false
|
||||
allow(target).to receive(:submittable_as_spam?).and_return false
|
||||
end
|
||||
|
||||
it 'does not submit as spam' do
|
||||
|
@ -26,7 +26,7 @@ describe Spam::MarkAsSpamService do
|
|||
|
||||
context 'spam is submitted successfully' do
|
||||
before do
|
||||
allow(spammable).to receive(:submittable_as_spam?).and_return true
|
||||
allow(target).to receive(:submittable_as_spam?).and_return true
|
||||
allow(fake_akismet_service).to receive(:submit_spam).and_return true
|
||||
end
|
||||
|
||||
|
@ -34,14 +34,14 @@ describe Spam::MarkAsSpamService do
|
|||
expect(subject.execute).to be_truthy
|
||||
end
|
||||
|
||||
it "updates the spammable object's user agent detail as being submitted as spam" do
|
||||
it "updates the target object's user agent detail as being submitted as spam" do
|
||||
expect(user_agent_detail).to receive(:update_attribute)
|
||||
|
||||
subject.execute
|
||||
end
|
||||
|
||||
context 'when Akismet does not consider it spam' do
|
||||
it 'does not update the spammable object as spam' do
|
||||
it 'does not update the target object as spam' do
|
||||
allow(fake_akismet_service).to receive(:submit_spam).and_return false
|
||||
|
||||
expect(subject.execute).to be_falsey
|
||||
|
|
|
@ -22,12 +22,12 @@ describe Spam::SpamCheckService do
|
|||
end
|
||||
|
||||
describe '#initialize' do
|
||||
subject { described_class.new(spammable: issue, request: request) }
|
||||
subject { described_class.new(target: issue, request: request) }
|
||||
|
||||
context 'when the request is nil' do
|
||||
let(:request) { nil }
|
||||
|
||||
it 'assembles the options with information from the spammable' do
|
||||
it 'assembles the options with information from the target' do
|
||||
aggregate_failures do
|
||||
expect(subject.options[:ip_address]).to eq(issue.ip_address)
|
||||
expect(subject.options[:user_agent]).to eq(issue.user_agent)
|
||||
|
@ -39,7 +39,7 @@ describe Spam::SpamCheckService do
|
|||
context 'when the request is present' do
|
||||
let(:request) { double(:request, env: env) }
|
||||
|
||||
it 'assembles the options with information from the spammable' do
|
||||
it 'assembles the options with information from the target' do
|
||||
aggregate_failures do
|
||||
expect(subject.options[:ip_address]).to eq(fake_ip)
|
||||
expect(subject.options[:user_agent]).to eq(fake_user_agent)
|
||||
|
@ -55,7 +55,7 @@ describe Spam::SpamCheckService do
|
|||
let_it_be(:existing_spam_log) { create(:spam_log, user: user, recaptcha_verified: false) }
|
||||
|
||||
subject do
|
||||
described_service = described_class.new(spammable: issue, request: request)
|
||||
described_service = described_class.new(target: issue, request: request)
|
||||
described_service.execute(user_id: user.id, api: nil, recaptcha_verified: recaptcha_verified, spam_log_id: existing_spam_log.id)
|
||||
end
|
||||
|
||||
|
@ -81,7 +81,7 @@ describe Spam::SpamCheckService do
|
|||
context 'when recaptcha was not verified' do
|
||||
let(:recaptcha_verified) { false }
|
||||
|
||||
context 'when spammable attributes have not changed' do
|
||||
context 'when target attributes have not changed' do
|
||||
before do
|
||||
issue.closed_at = Time.zone.now
|
||||
|
||||
|
@ -98,7 +98,7 @@ describe Spam::SpamCheckService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when spammable attributes have changed' do
|
||||
context 'when target attributes have changed' do
|
||||
before do
|
||||
issue.description = 'SPAM!'
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
shared_examples 'akismet spam' do
|
||||
context 'when request is missing' do
|
||||
subject { described_class.new(spammable: issue, request: nil) }
|
||||
subject { described_class.new(target: issue, request: nil) }
|
||||
|
||||
it "doesn't check as spam" do
|
||||
subject
|
||||
|
|
|
@ -2446,9 +2446,9 @@ camelcase@^5.0.0:
|
|||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
caniuse-lite@^1.0.30000980, caniuse-lite@^1.0.30000984:
|
||||
version "1.0.30000985"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000985.tgz#0eb40f6c8a8c219155cbe43c4975c0efb4a0f77f"
|
||||
integrity sha512-1ngiwkgqAYPG0JSSUp3PUDGPKKY59EK7NrGGX+VOxaKCNzRbNc7uXMny+c3VJfZxtoK3wSImTvG9T9sXiTw2+w==
|
||||
version "1.0.30001025"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001025.tgz#30336a8aca7f98618eb3cf38e35184e13d4e5fe6"
|
||||
integrity sha512-SKyFdHYfXUZf5V85+PJgLYyit27q4wgvZuf8QTOk1osbypcROihMBlx9GRar2/pIcKH2r4OehdlBr9x6PXetAQ==
|
||||
|
||||
capture-exit@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
|
Loading…
Reference in a new issue