From 589d27fff9fdd5f30d5506c869a8a2ea6883b160 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 11:33:42 -0500 Subject: [PATCH 01/48] 200 | Add frontend to manage evaluators list page --- app/assets/stylesheets/application.sass.scss | 8 ++ app/views/manage_evaluators/index.html.erb | 132 +++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 app/views/manage_evaluators/index.html.erb diff --git a/app/assets/stylesheets/application.sass.scss b/app/assets/stylesheets/application.sass.scss index fa5d1ded..44aac020 100644 --- a/app/assets/stylesheets/application.sass.scss +++ b/app/assets/stylesheets/application.sass.scss @@ -9,3 +9,11 @@ dialog::backdrop { background-color: black; border-color: black; } + +.usa-table.usa-table--borderless.gray-header { + thead { + th { + background-color: #dfe1e2 !important; + } + } +} diff --git a/app/views/manage_evaluators/index.html.erb b/app/views/manage_evaluators/index.html.erb new file mode 100644 index 00000000..7ef1fa40 --- /dev/null +++ b/app/views/manage_evaluators/index.html.erb @@ -0,0 +1,132 @@ +
+
+ <%= link_to manage_submissions_path, class: "usa-link display-inline-flex flex-align-center" do %> + <%= image_tag('images/usa-icons/arrow_back.svg', class: "usa-icon--size-3", alt: "Back to previous page") %> + Back + <% end %> +
+ +

<%= @challenge.title %> - <%= @phase.title %>

+

Create and manage a list of evaluators for the challenge.

+ +

Add Evaluators

+

Evaluators will not be assigned to submissions until you assign in the submission detail view.

+ + <%= form_with(model: EvaluatorInvitation.new, url: challenge_manage_evaluators_path(@challenge), method: :post, local: true) do |form| %> + <%= form.hidden_field :challenge_id, value: @challenge.id %> + <%= form.hidden_field :phase_id, value: @phase.id %> + <%= form.hidden_field :last_invite_sent, value: Time.current %> + +
+ <%= form.label :first_name, class: "usa-label font-sans-md" do %> + First Name* + <% end %> +
Add first and last name in that order.
+ <%= form.text_field :first_name, class: "usa-input", required: true, autocomplete: "given-name", style: "border: 1.5px solid #565c65;" %> +
+ +
+ <%= form.label :last_name, class: "usa-label font-sans-md" do %> + Last Name* + <% end %> +
Add first and last name in that order.
+ <%= form.text_field :last_name, class: "usa-input", required: true, autocomplete: "family-name", style: "border: 1.5px solid #565c65;" %> +
+ +
+ <%= form.label :email, class: "usa-label font-sans-md" do %> + Email Address* + <% end %> +
Add a single email address for the individual.
+ <%= form.email_field :email, class: "usa-input", required: true, autocomplete: "email", style: "border: 1.5px solid #565c65;" %> +
+ + <%= form.button type: 'submit', class: "usa-button margin-top-5 margin-bottom-3" do %> + <%= image_tag('images/usa-icons/person.svg', class: "usa-icon--size-3 icon-white", alt: "Add evaluator") %> + Add to evaluator list + <% end %> + <% end %> + +

Evaluator List

+ + <% if @evaluator_invitations.empty? && @existing_evaluators.empty? %> +
+

There currently are no evaluators available for this challenge phase. Please enter and add evaluators in the section above.

+
+ <% else %> +
+

Review evaluators for this challenge phase and track their submissions assignments.

+
+ +
+ + + + + + + + + + + + + <% @existing_evaluators.each do |evaluator| %> + + + + + + + + + <% end %> + <% @challenge.evaluator_invitations.where(phase: @phase).each do |invitation| %> + + + + + + + + + <% end %> + +
Evaluator nameEmail addressUser roleUser statusAssigned submissions
+ <%= "#{evaluator.first_name} #{evaluator.last_name}" %> + <%= evaluator.email %><%= evaluator.role.titleize %><%= user_status(evaluator, @challenge) %><%= assigned_submissions_count(evaluator, @challenge, @phase) %> +
+ <%= link_to manage_submissions_path(@challenge), class: 'usa-button font-body-3xs', style: 'white-space: nowrap;' do %> + View Submissions + <% end %> + <%= button_tag "Delete", type: 'button', class: 'usa-button usa-button--outline font-body-3xs', data: { + open_modal: true, + evaluator_id: evaluator.id, + evaluator_type: 'user', + challenge_id: @challenge.id, + phase_id: @phase.id + } %> +
+
+ <%= "#{invitation.first_name} #{invitation.last_name}" %> + <%= invitation.email %>Evaluator +
+ Invite Sent + <%= button_to resend_invitation_challenge_evaluator_invitation_path(@challenge, invitation), method: :post, class: 'usa-button usa-button--unstyled margin-left-2', style: 'white-space: nowrap;', form: { class: 'display-inline' } do %> + Resend + <% end %> +
+
0 + <%= button_tag "Delete", type: 'button', class: 'usa-button usa-button--outline font-body-3xs', data: { + open_modal: true, + evaluator_id: invitation.id, + evaluator_type: 'invitation', + challenge_id: @challenge.id, + phase_id: invitation.phase_id + } %> +
+
+ <% end %> + + <%= render 'delete_evaluator_modal' %> +
From 8f3fa45672ea25b9b2865045fd11894a6e1d92b4 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 11:35:04 -0500 Subject: [PATCH 02/48] 200 | Add delete modal to manage evaluators and functionality --- app/javascript/application.js | 1 + app/javascript/delete_evaluator_modal.js | 77 +++++++++++++++++++ .../_delete_evaluator_modal.html.erb | 46 +++++++++++ 3 files changed, 124 insertions(+) create mode 100644 app/javascript/delete_evaluator_modal.js create mode 100644 app/views/manage_evaluators/_delete_evaluator_modal.html.erb diff --git a/app/javascript/application.js b/app/javascript/application.js index 25c2e08d..c2e1549a 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1 +1,2 @@ import "./controllers" +import "./delete_evaluator_modal" diff --git a/app/javascript/delete_evaluator_modal.js b/app/javascript/delete_evaluator_modal.js new file mode 100644 index 00000000..eb37b921 --- /dev/null +++ b/app/javascript/delete_evaluator_modal.js @@ -0,0 +1,77 @@ +export function initializeDeleteEvaluatorModal() { + let currentEvaluatorId, currentEvaluatorType, challengeId, phaseId; + + document.querySelectorAll('[data-open-modal]').forEach(button => { + button.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + currentEvaluatorId = button.dataset.evaluatorId; + currentEvaluatorType = button.dataset.evaluatorType; + challengeId = button.dataset.challengeId; + phaseId = button.dataset.phaseId; + const modal = document.getElementById('delete-evaluator-modal'); + modal.classList.add('is-visible'); + return false; + }); + }); + + document.getElementById('confirmDelete').addEventListener('click', () => { + deleteEvaluator(); + }); + + function deleteEvaluator(forceDelete = false) { + const csrfToken = document.querySelector('meta[name="csrf-token"]').content; + const challengeId = document.querySelector('[data-challenge-id]').dataset.challengeId; + + fetch(`/challenges/${challengeId}/manage_evaluators`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': csrfToken + }, + body: JSON.stringify({ + evaluator_id: currentEvaluatorId, + evaluator_type: currentEvaluatorType, + phase_id: phaseId, + force_delete: forceDelete + }) + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + if (data.success) { + const modal = document.getElementById('delete-evaluator-modal'); + modal.classList.remove('is-visible'); + window.location.reload(); + } else { + throw new Error(data.message || 'Failed to remove evaluator'); + } + }) + .catch(error => { + console.error('Error:', error); + alert(error.message || 'An error occurred while removing the evaluator'); + }); + } + + document.querySelectorAll('[data-close-modal]').forEach(element => { + element.addEventListener('click', () => { + const modal = document.getElementById('delete-evaluator-modal'); + modal.classList.remove('is-visible'); + + const modalDescription = document.getElementById('modal-1-description'); + modalDescription.textContent = 'Deleting an evaluator from the challenge will remove the evaluator from any submissions of this challenge that the evaluator is assigned to. It will also delete any of their completed or in progress evaluations for those submissions.'; + + const confirmButton = document.getElementById('confirmDelete'); + confirmButton.textContent = 'Yes'; + confirmButton.onclick = () => deleteEvaluator(); + }); + }); +} + +if (document.querySelector('[data-page="manage-evaluators"]')) { + document.addEventListener('DOMContentLoaded', initializeDeleteEvaluatorModal); +} diff --git a/app/views/manage_evaluators/_delete_evaluator_modal.html.erb b/app/views/manage_evaluators/_delete_evaluator_modal.html.erb new file mode 100644 index 00000000..790e9bfa --- /dev/null +++ b/app/views/manage_evaluators/_delete_evaluator_modal.html.erb @@ -0,0 +1,46 @@ +
+
+
+ +
+ +
+ +
+ +
From 84f3b132183e3dc8604a9e4ed29c398a90f148c4 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 11:35:38 -0500 Subject: [PATCH 03/48] 200 | Update header and add challenge status to manage submissions index --- app/views/manage_submissions/_table.html.erb | 25 +++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/views/manage_submissions/_table.html.erb b/app/views/manage_submissions/_table.html.erb index 674aa9f7..27d9014b 100644 --- a/app/views/manage_submissions/_table.html.erb +++ b/app/views/manage_submissions/_table.html.erb @@ -1,23 +1,25 @@ - +
+ <% @challenges.each do |challenge| %> <% challenge.phases.each do |phase| %> - - + - - <% end %> From 12c95ffcda9eed9414dd33ccb435ae0aaeb48d44 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 11:37:53 -0500 Subject: [PATCH 04/48] 200 | Add functionality to add/invite, delete, and resend evaluator + specs --- .../evaluator_invitations_controller.rb | 24 +++ .../manage_evaluators_controller.rb | 133 ++++++++++++++ app/helpers/manage_evaluators_helper.rb | 30 ++++ app/models/evaluator_invitation.rb | 2 +- config/routes.rb | 15 ++ db/structure.sql | 9 +- .../evaluator_invitations_controller_spec.rb | 18 ++ .../manage_evaluators_controller_spec.rb | 165 ++++++++++++++++++ spec/helpers/manage_evaluators_helper_spec.rb | 35 ++++ .../models/challenge_phases_evaluator_spec.rb | 11 ++ spec/models/evaluator_invitation_spec.rb | 2 +- 11 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 app/controllers/evaluator_invitations_controller.rb create mode 100644 app/controllers/manage_evaluators_controller.rb create mode 100644 app/helpers/manage_evaluators_helper.rb create mode 100644 spec/controllers/evaluator_invitations_controller_spec.rb create mode 100644 spec/controllers/manage_evaluators_controller_spec.rb create mode 100644 spec/helpers/manage_evaluators_helper_spec.rb diff --git a/app/controllers/evaluator_invitations_controller.rb b/app/controllers/evaluator_invitations_controller.rb new file mode 100644 index 00000000..9949a040 --- /dev/null +++ b/app/controllers/evaluator_invitations_controller.rb @@ -0,0 +1,24 @@ +class EvaluatorInvitationsController < ApplicationController + before_action :set_challenge + before_action :set_evaluator_invitation + + # Resending the invitation only updates the time of the last_invite_sent for now + def resend_invitation + if @evaluator_invitation.update(last_invite_sent: Time.current) + # TODO: Implement sending the actual invitation email here + redirect_to challenge_manage_evaluators_path(@challenge), notice: 'Invitation resent successfully.' + else + redirect_to challenge_manage_evaluators_path(@challenge), alert: 'Failed to resend invitation.' + end + end + + private + + def set_challenge + @challenge = Challenge.find(params[:challenge_id]) + end + + def set_evaluator_invitation + @evaluator_invitation = @challenge.evaluator_invitations.find(params[:id]) + end +end diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb new file mode 100644 index 00000000..d37ef591 --- /dev/null +++ b/app/controllers/manage_evaluators_controller.rb @@ -0,0 +1,133 @@ +class ManageEvaluatorsController < ApplicationController + include ManageEvaluatorsHelper + + before_action :set_challenge + + VALID_EVALUATOR_ROLES = ['evaluator', 'solver', 'challenge_manager'].freeze + + def index + @phases = @challenge.phases.order(:start_date) + + if @phases.empty? + flash.now[:alert] = "This challenge has no phases. Please add at least one phase before managing evaluators." + @evaluator_invitations = [] + @existing_evaluators = [] + else + @phase = params[:phase_id] ? @phases.find(params[:phase_id]) : @phases.first + @evaluator_invitations = @challenge.evaluator_invitations.where(phase: @phase) + @existing_evaluators = @challenge.evaluators + .joins(:challenge_phases_evaluators) + .where(challenge_phases_evaluators: { phase: @phase }) + .distinct + end + end + + def create + @phase = @challenge.phases.find(evaluator_invitation_params[:phase_id]) + result = process_evaluator_invitation(evaluator_invitation_params[:email]) + + if result[:success] + redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id), notice: result[:message] + else + @evaluator_invitations = @challenge.evaluator_invitations.where(phase: @phase) + @existing_evaluators = @challenge.evaluators.joins(:challenge_phases_evaluators).where(challenge_phases_evaluators: { phase: @phase }).distinct + render :index + end + end + + def destroy + @phase = @challenge.phases.find(params[:phase_id]) + result = process_evaluator_removal(params[:evaluator_type], params[:evaluator_id]) + + if result[:success] + flash[:notice] = result[:message] + render json: { success: true, message: result[:message] } + else + render json: { success: false, message: result[:message] }, status: :unprocessable_entity + end + end + + private + + def evaluator_invitation_params + params.require(:evaluator_invitation).permit(:first_name, :last_name, :email, :challenge_id, :phase_id, :last_invite_sent) + end + + def set_challenge + @challenge = Challenge.find(params[:challenge_id]) + end + + + # Inviting evaluators + def process_evaluator_invitation(email) + existing_invitation = @challenge.evaluator_invitations.find_by(email: email, phase: @phase) + user = User.find_by(email: email) + + if existing_invitation + return resend_invitation(existing_invitation) + elsif user && valid_evaluator_role?(user) + return add_user_as_evaluator(user) + else + return create_new_invitation(email) + end + end + + # prevent duplicate evaluator invitations + def resend_invitation(invitation) + invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now + { success: true, message: "An invitation to this challenge has already been sent to #{invitation.email}. Invitation has been resent." } + end + + def valid_evaluator_role?(user) + VALID_EVALUATOR_ROLES.include?(user.role) + end + + def add_user_as_evaluator(user) + cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user: user) + { success: true, message: "#{user.email} has been added as an evaluator for this phase." } + end + + def create_new_invitation(email) + invitation = @challenge.evaluator_invitations.new(evaluator_invitation_params.merge(phase: @phase)) + if invitation.save + { success: true, message: "Invitation sent to #{email} for this challenge phase." } + else + { success: false, message: invitation.errors.full_messages.join(", ") } + end + end + + # Removing an evaluator from a challenge + def process_evaluator_removal(evaluator_type, evaluator_id) + case evaluator_type + when 'user' + remove_user_evaluator(evaluator_id) + when 'invitation' + remove_evaluator_invitation(evaluator_id) + else + { success: false, message: 'Invalid evaluator type' } + end + end + + def remove_user_evaluator(evaluator_id) + evaluator = @challenge.evaluators.find_by(id: evaluator_id) + return { success: false, message: 'Evaluator not found' } unless evaluator + + cpe = ChallengePhasesEvaluator.find_by(challenge: @challenge, phase: @phase, user: evaluator) + if cpe&.destroy + { success: true, message: 'Evaluator successfully removed from this phase.' } + else + { success: false, message: 'Failed to remove evaluator from this phase.' } + end + rescue StandardError => e + { success: false, message: "Error: #{e.message}" } + end + + def remove_evaluator_invitation(invitation_id) + invitation = @challenge.evaluator_invitations.find_by(id: invitation_id, phase: @phase) + if invitation&.destroy + { success: true, message: 'Evaluator invitation successfully removed from the challenge.' } + else + { success: false, message: 'Failed to remove evaluator invitation.' } + end + end +end diff --git a/app/helpers/manage_evaluators_helper.rb b/app/helpers/manage_evaluators_helper.rb new file mode 100644 index 00000000..8bc142a7 --- /dev/null +++ b/app/helpers/manage_evaluators_helper.rb @@ -0,0 +1,30 @@ +module ManageEvaluatorsHelper + + def user_status(evaluator, challenge) + if evaluator.is_a?(User) + if challenge.challenge_phases_evaluators.exists?(user_id: evaluator.id) + evaluator.status == 'active' ? "Available" : "Awaiting Approval" + else + "Invite Sent" + end + else # Evaluator invitation + existing_user = User.find_by(email: evaluator.email) + if existing_user + existing_user.status == 'active' ? "Available" : "Awaiting Approval" + else + "Invite Sent" + end + end + end + + def assigned_submissions_count(evaluator, challenge, phase) + if evaluator.is_a?(User) + evaluator.evaluator_submission_assignments + .joins(:submission) + .where(submissions: { challenge: challenge, phase: phase }) + .count + else + 0 + end + end +end diff --git a/app/models/evaluator_invitation.rb b/app/models/evaluator_invitation.rb index 42ffa177..265778fd 100644 --- a/app/models/evaluator_invitation.rb +++ b/app/models/evaluator_invitation.rb @@ -23,5 +23,5 @@ class EvaluatorInvitation < ApplicationRecord validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } validates :last_invite_sent, presence: true - validates :email, uniqueness: { scope: [:challenge_id, :phase_id] } + validates :email, uniqueness: { scope: [:challenge_id, :phase_id], message: "has already been invited for this challenge phase" } end diff --git a/config/routes.rb b/config/routes.rb index 3eac84de..93a2e2e6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,4 +24,19 @@ if Rails.env.development? || Rails.env.dev? get "/sandbox", to: "sandbox#index" end + + resources :challenges do + resource :manage_evaluators, only: [] do + collection do + get :index + post :create + delete :destroy + end + end + resources :evaluator_invitations, only: [] do + member do + post 'resend_invitation' + end + end + end end diff --git a/db/structure.sql b/db/structure.sql index 120c2068..5be5b187 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9,6 +9,13 @@ SET xmloption = content; SET client_min_messages = warning; SET row_security = off; +-- +-- Name: public; Type: SCHEMA; Schema: -; Owner: - +-- + +-- *not* creating schema, since initdb creates it + + -- -- Name: oban_job_state; Type: TYPE; Schema: public; Owner: - -- @@ -685,7 +692,7 @@ CREATE TABLE public.oban_jobs ( attempted_by text[], discarded_at timestamp without time zone, priority integer DEFAULT 0 NOT NULL, - tags text[] DEFAULT ARRAY[]::text[], + tags character varying(255)[] DEFAULT ARRAY[]::character varying[], meta jsonb DEFAULT '{}'::jsonb, cancelled_at timestamp without time zone, CONSTRAINT attempt_range CHECK (((attempt >= 0) AND (attempt <= max_attempts))), diff --git a/spec/controllers/evaluator_invitations_controller_spec.rb b/spec/controllers/evaluator_invitations_controller_spec.rb new file mode 100644 index 00000000..38ead643 --- /dev/null +++ b/spec/controllers/evaluator_invitations_controller_spec.rb @@ -0,0 +1,18 @@ +require 'rails_helper' + +RSpec.describe EvaluatorInvitationsController, type: :request do + describe 'POST #resend_invitation' do + it 'updates the last_invite_sent timestamp and redirects with a success message' do + challenge = create(:challenge) + phase = create(:phase, challenge: challenge) + invitation = create(:evaluator_invitation, challenge: challenge, phase: phase) + + expect { + post resend_invitation_challenge_evaluator_invitation_path(challenge, invitation) + }.to change { invitation.reload.last_invite_sent } + + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge)) + expect(flash[:notice]).to eq('Invitation resent successfully.') + end + end +end diff --git a/spec/controllers/manage_evaluators_controller_spec.rb b/spec/controllers/manage_evaluators_controller_spec.rb new file mode 100644 index 00000000..57219653 --- /dev/null +++ b/spec/controllers/manage_evaluators_controller_spec.rb @@ -0,0 +1,165 @@ +require 'rails_helper' + +RSpec.describe ManageEvaluatorsController, type: :request do + let(:challenge) { create(:challenge) } + let(:user) { create_and_log_in_user(role: 'challenge_manager') } + + describe 'GET #index' do + it 'assigns @evaluator_invitations and @existing_evaluators' do + evaluator = create(:user, role: 'evaluator') + phase = create(:phase, challenge: challenge) + invitation = create(:evaluator_invitation, challenge: challenge, phase: phase) + challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase) + + get challenge_manage_evaluators_path(challenge, phase_id: phase.id) + + expect(assigns(:evaluator_invitations)).to eq([invitation]) + expect(assigns(:existing_evaluators)).to eq([evaluator]) + end + end + + describe 'POST #create' do + context 'with valid params' do + let(:phase) { create(:phase, challenge: challenge) } + let(:valid_params) do + { + evaluator_invitation: attributes_for(:evaluator_invitation).merge(phase_id: phase.id) + } + end + + it 'creates a new evaluator invitation' do + expect { + post challenge_manage_evaluators_path(challenge), params: valid_params + }.to change(EvaluatorInvitation, :count).by(1) + end + + it 'redirects to manage_evaluators path with success notice' do + post challenge_manage_evaluators_path(challenge), params: valid_params + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) + expect(flash[:notice]).to include('Invitation sent') + end + end + + context 'with invalid params' do + let(:phase) { create(:phase, challenge: challenge) } + let(:invalid_params) do + { + evaluator_invitation: attributes_for(:evaluator_invitation, email: nil, phase_id: phase.id) + } + end + + it 'does not create a new evaluator invitation' do + expect { + post challenge_manage_evaluators_path(challenge), params: invalid_params + }.not_to change(EvaluatorInvitation, :count) + end + + it 're-renders the manage_evaluators template' do + post challenge_manage_evaluators_path(challenge), params: invalid_params + expect(response).to render_template(:index) + end + end + + context 'when inviting an existing user' do + it 'adds the user as an evaluator without creating a new invitation' do + challenge = create(:challenge) + phase = create(:phase, challenge: challenge) + user = create(:user, role: 'evaluator') + + expect { + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: user.email, + phase_id: phase.id + } + } + }.to change(ChallengePhasesEvaluator, :count).by(1) + + expect(EvaluatorInvitation.count).to eq(0) + + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) + expect(flash[:notice]).to include("has been added as an evaluator for this phase") + end + end + + context 'when inviting a new user' do + it 'creates a new evaluator invitation' do + challenge = create(:challenge) + phase = create(:phase, challenge: challenge) + + expect { + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: 'new_evaluator@example.com', + phase_id: phase.id, + first_name: 'New', + last_name: 'Evaluator', + last_invite_sent: Time.current + } + } + }.to change(EvaluatorInvitation, :count).by(1) + + expect(ChallengePhasesEvaluator.count).to eq(0) + + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) + expect(flash[:notice]).to include("Invitation sent to") + end + end + end + + describe 'DELETE #destroy' do + context 'when removing a user evaluator from a specific phase' do + let!(:evaluator) { create(:user, role: 'evaluator') } + let!(:phase1) { create(:phase, challenge: challenge) } + let!(:phase2) { create(:phase, challenge: challenge) } + + before do + challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase1) + challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase2) + end + + it 'removes the evaluator from their associated phase' do + expect { + delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: evaluator.id, evaluator_type: 'user', phase_id: phase1.id } + }.to change { challenge.challenge_phases_evaluators.where(phase: phase1).count }.by(-1) + + expect(response).to have_http_status(:success) + expect(JSON.parse(response.body)['success']).to be true + + # Ensure the evaluator is still associated with the other phase + expect(challenge.challenge_phases_evaluators.where(phase: phase2, user: evaluator).count).to eq(1) + end + end + + context 'when removing an evaluator invitation' do + let!(:phase) { create(:phase, challenge: challenge) } + let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase) } + + it 'removes the evaluator invitation' do + expect { + delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: invitation.id, evaluator_type: 'invitation', phase_id: phase.id } + }.to change(EvaluatorInvitation, :count).by(-1) + end + + it 'returns a success JSON response' do + delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: invitation.id, evaluator_type: 'invitation', phase_id: phase.id } + expect(response).to have_http_status(:success) + expect(JSON.parse(response.body)['success']).to be true + end + end + + context 'with invalid evaluator type' do + let(:phase) { create(:phase, challenge: challenge) } + + it 'returns an error JSON response' do + delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: 1, evaluator_type: 'invalid', phase_id: phase.id } + + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)).to eq({ + 'success' => false, + 'message' => 'Invalid evaluator type' + }) + end + end + end +end diff --git a/spec/helpers/manage_evaluators_helper_spec.rb b/spec/helpers/manage_evaluators_helper_spec.rb new file mode 100644 index 00000000..3ed2c91c --- /dev/null +++ b/spec/helpers/manage_evaluators_helper_spec.rb @@ -0,0 +1,35 @@ +# spec/helpers/manage_evaluators_helper_spec.rb + +require 'rails_helper' + +RSpec.describe ManageEvaluatorsHelper, type: :helper do + describe '#assigned_submissions_count' do + let(:challenge) { create(:challenge) } + let(:phase) { create(:phase, challenge: challenge) } + let(:evaluator) { create(:user, role: :evaluator) } + let(:submission) { create(:submission, challenge: challenge, phase: phase) } + + it 'returns the correct count of assigned submissions' do + create(:evaluator_submission_assignment, evaluator: evaluator, submission: submission) + create(:evaluator_submission_assignment, evaluator: evaluator, submission: submission) + create(:evaluator_submission_assignment, evaluator: evaluator, submission: create(:submission, challenge: challenge, phase: phase)) + + expect(helper.assigned_submissions_count(evaluator, challenge, phase)).to eq(3) + end + + it 'returns 0 for non-User evaluators' do + expect(helper.assigned_submissions_count(nil, challenge, phase)).to eq(0) + end + + it 'returns 0 when there are no assigned submissions' do + expect(helper.assigned_submissions_count(evaluator, challenge, phase)).to eq(0) + end + + it 'only counts submissions for the specified challenge and phase' do + create(:evaluator_submission_assignment, evaluator: evaluator, submission: submission) + create(:evaluator_submission_assignment, evaluator: evaluator, submission: create(:submission, challenge: create(:challenge), phase: create(:phase))) + + expect(helper.assigned_submissions_count(evaluator, challenge, phase)).to eq(1) + end + end +end diff --git a/spec/models/challenge_phases_evaluator_spec.rb b/spec/models/challenge_phases_evaluator_spec.rb index b4c720c8..df993ed0 100644 --- a/spec/models/challenge_phases_evaluator_spec.rb +++ b/spec/models/challenge_phases_evaluator_spec.rb @@ -51,4 +51,15 @@ expect(evaluator2).to be_valid expect { evaluator2.save! }.not_to raise_error end + + it "associates a user as an evaluator for a specific challenge phase" do + challenge = create(:challenge) + phase = create(:phase, challenge: challenge) + user = create(:user, role: 'evaluator') + cpe = create(:challenge_phases_evaluator, challenge: challenge, phase: phase, user: user) + + expect(challenge.evaluators).to include(user) + expect(phase.evaluators).to include(user) + expect(user.evaluated_phases).to include(phase) + end end diff --git a/spec/models/evaluator_invitation_spec.rb b/spec/models/evaluator_invitation_spec.rb index 68e0b82b..2b6fd411 100644 --- a/spec/models/evaluator_invitation_spec.rb +++ b/spec/models/evaluator_invitation_spec.rb @@ -19,7 +19,7 @@ create(:evaluator_invitation, challenge:, phase:, email: "test@example.com") duplicate_invitation = build(:evaluator_invitation, challenge:, phase:, email: "test@example.com") expect(duplicate_invitation).not_to be_valid - expect(duplicate_invitation.errors[:email]).to include("has already been taken") + expect(duplicate_invitation.errors[:email]).to include("has already been invited for this challenge phase") end it "allows the same email for different challenges or phases" do From 89fedc4dbae9b10516399a836fdbd1614528c4a6 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 12:40:12 -0500 Subject: [PATCH 05/48] 200 | Center back link on delete evaluator modal --- app/views/manage_evaluators/_delete_evaluator_modal.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/manage_evaluators/_delete_evaluator_modal.html.erb b/app/views/manage_evaluators/_delete_evaluator_modal.html.erb index 790e9bfa..af67161d 100644 --- a/app/views/manage_evaluators/_delete_evaluator_modal.html.erb +++ b/app/views/manage_evaluators/_delete_evaluator_modal.html.erb @@ -21,7 +21,7 @@ Yes -
  • +
  • Date: Tue, 29 Oct 2024 12:40:50 -0500 Subject: [PATCH 06/48] 200 | Uniqueness message handled elsewhere --- app/models/evaluator_invitation.rb | 2 +- spec/models/evaluator_invitation_spec.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/models/evaluator_invitation.rb b/app/models/evaluator_invitation.rb index 265778fd..42ffa177 100644 --- a/app/models/evaluator_invitation.rb +++ b/app/models/evaluator_invitation.rb @@ -23,5 +23,5 @@ class EvaluatorInvitation < ApplicationRecord validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } validates :last_invite_sent, presence: true - validates :email, uniqueness: { scope: [:challenge_id, :phase_id], message: "has already been invited for this challenge phase" } + validates :email, uniqueness: { scope: [:challenge_id, :phase_id] } end diff --git a/spec/models/evaluator_invitation_spec.rb b/spec/models/evaluator_invitation_spec.rb index 2b6fd411..eab3e072 100644 --- a/spec/models/evaluator_invitation_spec.rb +++ b/spec/models/evaluator_invitation_spec.rb @@ -19,7 +19,6 @@ create(:evaluator_invitation, challenge:, phase:, email: "test@example.com") duplicate_invitation = build(:evaluator_invitation, challenge:, phase:, email: "test@example.com") expect(duplicate_invitation).not_to be_valid - expect(duplicate_invitation.errors[:email]).to include("has already been invited for this challenge phase") end it "allows the same email for different challenges or phases" do From 3335e3f68cad0cea595433f6fb9431e34d62752b Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 12:41:40 -0500 Subject: [PATCH 07/48] 200 | Add evaluator messages to locale --- .../evaluator_invitations_controller.rb | 8 ++- .../manage_evaluators_controller.rb | 65 ++++++++++++------- app/helpers/manage_evaluators_helper.rb | 36 +++++----- config/locales/en.yml | 14 ++++ 4 files changed, 79 insertions(+), 44 deletions(-) diff --git a/app/controllers/evaluator_invitations_controller.rb b/app/controllers/evaluator_invitations_controller.rb index 9949a040..3cb977df 100644 --- a/app/controllers/evaluator_invitations_controller.rb +++ b/app/controllers/evaluator_invitations_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EvaluatorInvitationsController < ApplicationController before_action :set_challenge before_action :set_evaluator_invitation @@ -6,9 +8,11 @@ class EvaluatorInvitationsController < ApplicationController def resend_invitation if @evaluator_invitation.update(last_invite_sent: Time.current) # TODO: Implement sending the actual invitation email here - redirect_to challenge_manage_evaluators_path(@challenge), notice: 'Invitation resent successfully.' + redirect_to challenge_manage_evaluators_path(@challenge), + notice: t('evaluator_invitations.resend_invitation.success') else - redirect_to challenge_manage_evaluators_path(@challenge), alert: 'Failed to resend invitation.' + redirect_to challenge_manage_evaluators_path(@challenge), + alert: t('evaluator_invitations.resend_invitation.failure') end end diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index d37ef591..2d1db61e 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -1,24 +1,26 @@ +# frozen_string_literal: true + class ManageEvaluatorsController < ApplicationController include ManageEvaluatorsHelper before_action :set_challenge - VALID_EVALUATOR_ROLES = ['evaluator', 'solver', 'challenge_manager'].freeze + VALID_EVALUATOR_ROLES = %w[evaluator solver challenge_manager].freeze def index @phases = @challenge.phases.order(:start_date) if @phases.empty? - flash.now[:alert] = "This challenge has no phases. Please add at least one phase before managing evaluators." + flash.now[:alert] = t('manage_evaluators.index.no_phases_alert') @evaluator_invitations = [] @existing_evaluators = [] else @phase = params[:phase_id] ? @phases.find(params[:phase_id]) : @phases.first @evaluator_invitations = @challenge.evaluator_invitations.where(phase: @phase) - @existing_evaluators = @challenge.evaluators - .joins(:challenge_phases_evaluators) - .where(challenge_phases_evaluators: { phase: @phase }) - .distinct + @existing_evaluators = @challenge.evaluators. + joins(:challenge_phases_evaluators). + where(challenge_phases_evaluators: { phase: @phase }). + distinct end end @@ -27,10 +29,14 @@ def create result = process_evaluator_invitation(evaluator_invitation_params[:email]) if result[:success] - redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id), notice: result[:message] + redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id), + notice: result[:message] else @evaluator_invitations = @challenge.evaluator_invitations.where(phase: @phase) - @existing_evaluators = @challenge.evaluators.joins(:challenge_phases_evaluators).where(challenge_phases_evaluators: { phase: @phase }).distinct + @existing_evaluators = @challenge.evaluators. + joins(:challenge_phases_evaluators). + where(challenge_phases_evaluators: { phase: @phase }). + distinct render :index end end @@ -50,33 +56,38 @@ def destroy private def evaluator_invitation_params - params.require(:evaluator_invitation).permit(:first_name, :last_name, :email, :challenge_id, :phase_id, :last_invite_sent) + params.require(:evaluator_invitation).permit( + :first_name, :last_name, :email, :challenge_id, :phase_id, :last_invite_sent + ) end def set_challenge @challenge = Challenge.find(params[:challenge_id]) end - # Inviting evaluators def process_evaluator_invitation(email) - existing_invitation = @challenge.evaluator_invitations.find_by(email: email, phase: @phase) - user = User.find_by(email: email) + existing_invitation = @challenge.evaluator_invitations.find_by(email:, phase: @phase) + user = User.find_by(email:) if existing_invitation - return resend_invitation(existing_invitation) + resend_invitation(existing_invitation) elsif user && valid_evaluator_role?(user) - return add_user_as_evaluator(user) + add_user_as_evaluator(user) else - return create_new_invitation(email) + create_new_invitation(email) end end - # prevent duplicate evaluator invitations - def resend_invitation(invitation) - invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now - { success: true, message: "An invitation to this challenge has already been sent to #{invitation.email}. Invitation has been resent." } - end +# prevent duplicate evaluator invitations +def resend_invitation(invitation) + invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now + { + success: true, + message: "An invitation to this challenge has already been sent to " \ + "#{invitation.email}. Invitation has been resent." + } +end def valid_evaluator_role?(user) VALID_EVALUATOR_ROLES.include?(user.role) @@ -84,7 +95,11 @@ def valid_evaluator_role?(user) def add_user_as_evaluator(user) cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user: user) - { success: true, message: "#{user.email} has been added as an evaluator for this phase." } + if cpe.persisted? + { success: true, message: "#{user.email} has been added as an evaluator for this phase." } + else + { success: false, message: "Failed to add #{user.email} as an evaluator." } + end end def create_new_invitation(email) @@ -114,9 +129,9 @@ def remove_user_evaluator(evaluator_id) cpe = ChallengePhasesEvaluator.find_by(challenge: @challenge, phase: @phase, user: evaluator) if cpe&.destroy - { success: true, message: 'Evaluator successfully removed from this phase.' } + { success: true, message: t('manage_evaluators.remove_user_evaluator.success') } else - { success: false, message: 'Failed to remove evaluator from this phase.' } + { success: false, message: t('manage_evaluators.remove_user_evaluator.failure') } end rescue StandardError => e { success: false, message: "Error: #{e.message}" } @@ -125,9 +140,9 @@ def remove_user_evaluator(evaluator_id) def remove_evaluator_invitation(invitation_id) invitation = @challenge.evaluator_invitations.find_by(id: invitation_id, phase: @phase) if invitation&.destroy - { success: true, message: 'Evaluator invitation successfully removed from the challenge.' } + { success: true, message: t('manage_evaluators.remove_evaluator_invitation.success') } else - { success: false, message: 'Failed to remove evaluator invitation.' } + { success: false, message: t('manage_evaluators.remove_evaluator_invitation.failure') } end end end diff --git a/app/helpers/manage_evaluators_helper.rb b/app/helpers/manage_evaluators_helper.rb index 8bc142a7..a20441f6 100644 --- a/app/helpers/manage_evaluators_helper.rb +++ b/app/helpers/manage_evaluators_helper.rb @@ -1,30 +1,32 @@ -module ManageEvaluatorsHelper +# frozen_string_literal: true +module ManageEvaluatorsHelper def user_status(evaluator, challenge) if evaluator.is_a?(User) - if challenge.challenge_phases_evaluators.exists?(user_id: evaluator.id) - evaluator.status == 'active' ? "Available" : "Awaiting Approval" - else - "Invite Sent" - end - else # Evaluator invitation - existing_user = User.find_by(email: evaluator.email) - if existing_user - existing_user.status == 'active' ? "Available" : "Awaiting Approval" - else - "Invite Sent" - end + user_status_for_existing_user(evaluator, challenge) + else + "Invite Sent" end end def assigned_submissions_count(evaluator, challenge, phase) if evaluator.is_a?(User) - evaluator.evaluator_submission_assignments - .joins(:submission) - .where(submissions: { challenge: challenge, phase: phase }) - .count + evaluator.evaluator_submission_assignments. + joins(:submission). + where(submissions: { challenge:, phase: }). + count else 0 end end + + private + + def user_status_for_existing_user(user, challenge) + if challenge.challenge_phases_evaluators.exists?(user_id: user.id) + user.status == 'active' ? "Available" : "Awaiting Approval" + else + "Invite Sent" + end + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 6ab6a8f2..83865f9a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -36,3 +36,17 @@ en: please_try_again: "Please try again." session_expired_alert: "Your session has expired. Please log in again." evaluation_criterion_unique_title_in_form: "must be unique within the same form." + manage_evaluators: + index: + no_phases_alert: "This challenge has no phases. Please add at least one phase before managing evaluators." + remove_user_evaluator: + evaluator_not_found: "Evaluator not found" + success: "Evaluator successfully removed from this phase." + failure: "Failed to remove evaluator from this phase." + remove_evaluator_invitation: + success: "Evaluator invitation successfully removed from the challenge." + failure: "Failed to remove evaluator invitation." + evaluator_invitations: + resend_invitation: + success: "Invitation resent successfully." + failure: "Failed to resend invitation." From b18a5afa4c6e68667c87e3388c317afc5a418b07 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 12:48:32 -0500 Subject: [PATCH 08/48] 200 | Indentation and lazy lookup for locale --- .../evaluator_invitations_controller.rb | 5 ++--- .../manage_evaluators_controller.rb | 22 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/controllers/evaluator_invitations_controller.rb b/app/controllers/evaluator_invitations_controller.rb index 3cb977df..332ca630 100644 --- a/app/controllers/evaluator_invitations_controller.rb +++ b/app/controllers/evaluator_invitations_controller.rb @@ -4,15 +4,14 @@ class EvaluatorInvitationsController < ApplicationController before_action :set_challenge before_action :set_evaluator_invitation - # Resending the invitation only updates the time of the last_invite_sent for now def resend_invitation if @evaluator_invitation.update(last_invite_sent: Time.current) # TODO: Implement sending the actual invitation email here redirect_to challenge_manage_evaluators_path(@challenge), - notice: t('evaluator_invitations.resend_invitation.success') + notice: t('.success') else redirect_to challenge_manage_evaluators_path(@challenge), - alert: t('evaluator_invitations.resend_invitation.failure') + alert: t('.failure') end end diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index 2d1db61e..0f04c7d3 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -11,7 +11,7 @@ def index @phases = @challenge.phases.order(:start_date) if @phases.empty? - flash.now[:alert] = t('manage_evaluators.index.no_phases_alert') + flash.now[:alert] = t('.no_phases_alert') @evaluator_invitations = [] @existing_evaluators = [] else @@ -79,22 +79,22 @@ def process_evaluator_invitation(email) end end -# prevent duplicate evaluator invitations -def resend_invitation(invitation) - invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now - { - success: true, - message: "An invitation to this challenge has already been sent to " \ - "#{invitation.email}. Invitation has been resent." - } -end + # prevent duplicate evaluator invitations + def resend_invitation(invitation) + invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now + { + success: true, + message: "An invitation to this challenge has already been sent to " \ + "#{invitation.email}. Invitation has been resent." + } + end def valid_evaluator_role?(user) VALID_EVALUATOR_ROLES.include?(user.role) end def add_user_as_evaluator(user) - cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user: user) + cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user:) if cpe.persisted? { success: true, message: "#{user.email} has been added as an evaluator for this phase." } else From f13edf19b56afe4e1e59eda7e6d5734ac016ba1b Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 13:06:08 -0500 Subject: [PATCH 09/48] set node-version for circleci --- .circleci/config.yml | 3 +- yarn.lock | 3068 +++++++++++++----------------------------- 2 files changed, 960 insertions(+), 2111 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a10030c2..03389488 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ orbs: cloudfoundry: circleci/cloudfoundry@1.0 ruby: circleci/ruby@2.1.3 browser-tools: circleci/browser-tools@1.4.8 - node: circleci/node@5.2.0 + node: circleci/node@6.3.0 docker: circleci/docker@2.6.0 executors: @@ -30,6 +30,7 @@ commands: description: 'Install yarn modules and build assets' steps: - node/install: + node-version: 20.15.1 install-yarn: true - node/install-packages: pkg-manager: yarn diff --git a/yarn.lock b/yarn.lock index f100822f..921fc991 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,144 +7,137 @@ resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-1.10.0.tgz#1a67ac889c2d464a3492b3e54c38f80517963b16" integrity sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag== -"@esbuild/aix-ppc64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz#145b74d5e4a5223489cabdc238d8dad902df5259" - integrity sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ== - -"@esbuild/android-arm64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz#453bbe079fc8d364d4c5545069e8260228559832" - integrity sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ== - -"@esbuild/android-arm@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.0.tgz#26c806853aa4a4f7e683e519cd9d68e201ebcf99" - integrity sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g== - -"@esbuild/android-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.0.tgz#1e51af9a6ac1f7143769f7ee58df5b274ed202e6" - integrity sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ== - -"@esbuild/darwin-arm64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz#d996187a606c9534173ebd78c58098a44dd7ef9e" - integrity sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow== - -"@esbuild/darwin-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz#30c8f28a7ef4e32fe46501434ebe6b0912e9e86c" - integrity sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ== - -"@esbuild/freebsd-arm64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz#30f4fcec8167c08a6e8af9fc14b66152232e7fb4" - integrity sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw== - -"@esbuild/freebsd-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz#1003a6668fe1f5d4439e6813e5b09a92981bc79d" - integrity sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ== - -"@esbuild/linux-arm64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz#3b9a56abfb1410bb6c9138790f062587df3e6e3a" - integrity sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw== - -"@esbuild/linux-arm@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz#237a8548e3da2c48cd79ae339a588f03d1889aad" - integrity sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw== - -"@esbuild/linux-ia32@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz#4269cd19cb2de5de03a7ccfc8855dde3d284a238" - integrity sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA== - -"@esbuild/linux-loong64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz#82b568f5658a52580827cc891cb69d2cb4f86280" - integrity sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A== - -"@esbuild/linux-mips64el@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz#9a57386c926262ae9861c929a6023ed9d43f73e5" - integrity sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w== - -"@esbuild/linux-ppc64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz#f3a79fd636ba0c82285d227eb20ed8e31b4444f6" - integrity sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw== - -"@esbuild/linux-riscv64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz#f9d2ef8356ce6ce140f76029680558126b74c780" - integrity sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw== - -"@esbuild/linux-s390x@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz#45390f12e802201f38a0229e216a6aed4351dfe8" - integrity sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg== - -"@esbuild/linux-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz#c8409761996e3f6db29abcf9b05bee8d7d80e910" - integrity sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ== - -"@esbuild/netbsd-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz#ba70db0114380d5f6cfb9003f1d378ce989cd65c" - integrity sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw== - -"@esbuild/openbsd-arm64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz#72fc55f0b189f7a882e3cf23f332370d69dfd5db" - integrity sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ== - -"@esbuild/openbsd-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz#b6ae7a0911c18fe30da3db1d6d17a497a550e5d8" - integrity sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg== - -"@esbuild/sunos-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz#58f0d5e55b9b21a086bfafaa29f62a3eb3470ad8" - integrity sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA== - -"@esbuild/win32-arm64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz#b858b2432edfad62e945d5c7c9e5ddd0f528ca6d" - integrity sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ== - -"@esbuild/win32-ia32@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz#167ef6ca22a476c6c0c014a58b4f43ae4b80dec7" - integrity sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA== - -"@esbuild/win32-x64@0.23.0": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz#db44a6a08520b5f25bbe409f34a59f2d4bcc7ced" - integrity sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g== - -"@gulp-sourcemaps/identity-map@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz#a6e8b1abec8f790ec6be2b8c500e6e68037c0019" - integrity sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q== - dependencies: - acorn "^6.4.1" - normalize-path "^3.0.0" - postcss "^7.0.16" - source-map "^0.6.0" - through2 "^3.0.1" +"@esbuild/aix-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz#51299374de171dbd80bb7d838e1cfce9af36f353" + integrity sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ== + +"@esbuild/android-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz#58565291a1fe548638adb9c584237449e5e14018" + integrity sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw== + +"@esbuild/android-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.1.tgz#5eb8c652d4c82a2421e3395b808e6d9c42c862ee" + integrity sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ== + +"@esbuild/android-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.1.tgz#ae19d665d2f06f0f48a6ac9a224b3f672e65d517" + integrity sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg== + +"@esbuild/darwin-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz#05b17f91a87e557b468a9c75e9d85ab10c121b16" + integrity sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q== + +"@esbuild/darwin-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz#c58353b982f4e04f0d022284b8ba2733f5ff0931" + integrity sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw== + +"@esbuild/freebsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz#f9220dc65f80f03635e1ef96cfad5da1f446f3bc" + integrity sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA== + +"@esbuild/freebsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz#69bd8511fa013b59f0226d1609ac43f7ce489730" + integrity sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g== + +"@esbuild/linux-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz#8050af6d51ddb388c75653ef9871f5ccd8f12383" + integrity sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g== + +"@esbuild/linux-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz#ecaabd1c23b701070484990db9a82f382f99e771" + integrity sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ== + +"@esbuild/linux-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz#3ed2273214178109741c09bd0687098a0243b333" + integrity sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ== + +"@esbuild/linux-loong64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz#a0fdf440b5485c81b0fbb316b08933d217f5d3ac" + integrity sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw== + +"@esbuild/linux-mips64el@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz#e11a2806346db8375b18f5e104c5a9d4e81807f6" + integrity sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q== + +"@esbuild/linux-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz#06a2744c5eaf562b1a90937855b4d6cf7c75ec96" + integrity sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw== + +"@esbuild/linux-riscv64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz#65b46a2892fc0d1af4ba342af3fe0fa4a8fe08e7" + integrity sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA== + +"@esbuild/linux-s390x@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz#e71ea18c70c3f604e241d16e4e5ab193a9785d6f" + integrity sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw== + +"@esbuild/linux-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz#d47f97391e80690d4dfe811a2e7d6927ad9eed24" + integrity sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ== + +"@esbuild/netbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz#44e743c9778d57a8ace4b72f3c6b839a3b74a653" + integrity sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA== + +"@esbuild/openbsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz#05c5a1faf67b9881834758c69f3e51b7dee015d7" + integrity sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q== + +"@esbuild/openbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz#2e58ae511bacf67d19f9f2dcd9e8c5a93f00c273" + integrity sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA== + +"@esbuild/sunos-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz#adb022b959d18d3389ac70769cef5a03d3abd403" + integrity sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA== + +"@esbuild/win32-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz#84906f50c212b72ec360f48461d43202f4c8b9a2" + integrity sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A== + +"@esbuild/win32-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz#5e3eacc515820ff729e90d0cb463183128e82fac" + integrity sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ== + +"@esbuild/win32-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" + integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== + +"@gulpjs/messages@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@gulpjs/messages/-/messages-1.1.0.tgz#94e70978ff676ade541faab459c37ae0c7095e5a" + integrity sha512-Ys9sazDatyTgZVb4xPlDufLweJ/Os2uHWOv+Caxvy2O85JcnT4M3vc73bi8pdLWlv3fdWQz3pdI9tVwo8rQQSg== -"@gulp-sourcemaps/map-sources@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda" - integrity sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A== +"@gulpjs/to-absolute-glob@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz#1fc2460d3953e1d9b9f2dfdb4bcc99da4710c021" + integrity sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA== dependencies: - normalize-path "^2.0.1" - through2 "^2.0.3" + is-negated-glob "^1.0.0" "@hotwired/stimulus@^3.2.2": version "3.2.2" @@ -172,17 +165,100 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@parcel/watcher-android-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" + integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== + +"@parcel/watcher-darwin-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" + integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== + +"@parcel/watcher-darwin-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" + integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== + +"@parcel/watcher-freebsd-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" + integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== + +"@parcel/watcher-linux-arm-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" + integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== + +"@parcel/watcher-linux-arm64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" + integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== + +"@parcel/watcher-linux-arm64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" + integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== + +"@parcel/watcher-linux-x64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" + integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== + +"@parcel/watcher-linux-x64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" + integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== + +"@parcel/watcher-win32-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" + integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== + +"@parcel/watcher-win32-ia32@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" + integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== + +"@parcel/watcher-win32-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" + integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== + +"@parcel/watcher@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" + integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.4.1" + "@parcel/watcher-darwin-arm64" "2.4.1" + "@parcel/watcher-darwin-x64" "2.4.1" + "@parcel/watcher-freebsd-x64" "2.4.1" + "@parcel/watcher-linux-arm-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-musl" "2.4.1" + "@parcel/watcher-linux-x64-glibc" "2.4.1" + "@parcel/watcher-linux-x64-musl" "2.4.1" + "@parcel/watcher-win32-arm64" "2.4.1" + "@parcel/watcher-win32-ia32" "2.4.1" + "@parcel/watcher-win32-x64" "2.4.1" + "@types/expect@^1.20.4": version "1.20.4" resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== "@types/node@*": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b" - integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw== + version "22.8.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.4.tgz#ab754f7ac52e1fe74174f761c5b03acaf06da0dc" + integrity sha512-SpNNxkftTJOPk0oN+y2bIqurEXHTA2AOZ3EJDDKeJ5VzkvvORSvmQXGQarcOzWV1ac7DCaPBEdMDxBsM+d8jWw== dependencies: - undici-types "~6.13.0" + undici-types "~6.19.8" "@types/vinyl@^2.0.4": version "2.0.12" @@ -193,22 +269,21 @@ "@types/node" "*" "@uswds/compile@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@uswds/compile/-/compile-1.1.0.tgz#f413285221703cc6453417cadfc14cd15f201d49" - integrity sha512-kKlszBhO13v/qa1jaNqaOWiAJjuKtASHCxjmu+OXw/wMGVdaJcNjkM8hWL9/sx4AUe07PBwlJoGPtkKRWqSxWw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/@uswds/compile/-/compile-1.2.0.tgz#7f685d4971f195f27a66d1d7aaa9ae10ed8e5eb0" + integrity sha512-QgZb1+BzgKUZM8oRHyTSQN6u9nDeAxIYIEocFpE1zkeW6uMaxqqaZdNmyzwCIMGieiXA0rv/DjEItNgkIQofOQ== dependencies: - autoprefixer "10.4.16" + autoprefixer "10.4.20" del "6.1.1" - gulp "4.0.2" + gulp "5.0.0" gulp-postcss "9.0.1" gulp-rename "2.0.0" gulp-replace "1.1.4" gulp-sass "5.1.0" - gulp-sourcemaps "3.0.0" gulp-svgstore "9.0.0" - postcss "8.4.31" + postcss "8.4.40" postcss-csso "6.0.1" - sass-embedded "1.69.5" + sass-embedded "1.77.8" "@uswds/uswds@3.8.1": version "3.8.1" @@ -220,11 +295,6 @@ receptor "1.0.0" resolve-id-refs "0.1.0" -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -247,30 +317,24 @@ ansi-gray@^0.1.1: dependencies: ansi-wrap "0.1.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw== -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@~3.1.2: +anymatch@^3.1.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -278,172 +342,87 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -append-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" - integrity sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA== - dependencies: - buffer-equal "^1.0.0" - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== - arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== -arr-filter@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" - integrity sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA== - dependencies: - make-iterator "^1.0.0" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-map@^2.0.0, arr-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" - integrity sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw== - dependencies: - make-iterator "^1.0.0" - arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== -array-each@^1.0.0, array-each@^1.0.1: +array-each@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" integrity sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA== -array-initial@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" - integrity sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw== - dependencies: - array-slice "^1.0.0" - is-number "^4.0.0" - -array-last@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" - integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== - dependencies: - is-number "^4.0.0" - array-slice@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== -async-done@^1.2.0, async-done@^1.2.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" - integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== +async-done@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/async-done/-/async-done-2.0.0.tgz#f1ec5df738c6383a52b0a30d0902fd897329c15a" + integrity sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw== dependencies: - end-of-stream "^1.1.0" - once "^1.3.2" - process-nextick-args "^2.0.0" - stream-exhaust "^1.0.1" - -async-each@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.6.tgz#52f1d9403818c179b7561e11a5d1b77eb2160e77" - integrity sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg== + end-of-stream "^1.4.4" + once "^1.4.0" + stream-exhaust "^1.0.2" -async-settle@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" - integrity sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw== +async-settle@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-2.0.0.tgz#c695ad14e070f6a755d019d32d6eb38029020287" + integrity sha512-Obu/KE8FurfQRN6ODdHN9LuXqwC+JFIM9NRyZqJJ4ZfLJmIYN9Rg0/kb+wF70VV5+fJusTMQlJ1t5rF7J/ETdg== dependencies: - async-done "^1.2.2" - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + async-done "^2.0.0" -autoprefixer@10.4.16: - version "10.4.16" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" - integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== +autoprefixer@10.4.20: + version "10.4.20" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" + integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== dependencies: - browserslist "^4.21.10" - caniuse-lite "^1.0.30001538" - fraction.js "^4.3.6" + browserslist "^4.23.3" + caniuse-lite "^1.0.30001646" + fraction.js "^4.3.7" normalize-range "^0.1.2" - picocolors "^1.0.0" + picocolors "^1.0.1" postcss-value-parser "^4.2.0" -bach@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" - integrity sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg== - dependencies: - arr-filter "^1.1.1" - arr-flatten "^1.0.1" - arr-map "^2.0.0" - array-each "^1.0.0" - array-initial "^1.0.0" - array-last "^1.1.1" - async-done "^1.2.2" - async-settle "^1.0.0" - now-and-later "^2.0.0" +bach@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bach/-/bach-2.0.1.tgz#45a3a3cbf7dbba3132087185c60357482b988972" + integrity sha512-A7bvGMGiTOxGMpNupYl9HQTf0FFDNF4VCmks4PJpFyN1AX2pdKuxuwdvUz2Hu388wcgp+OvGFNsumBfFNkR7eg== + dependencies: + async-done "^2.0.0" + async-settle "^2.0.0" + now-and-later "^3.0.0" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" +bare-events@^2.2.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.0.tgz#305b511e262ffd8b9d5616b056464f8e1b3329cc" + integrity sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A== -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== binary-extensions@^2.0.0: version "2.3.0" @@ -455,12 +434,14 @@ binaryextensions@^2.2.0: resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.3.0.tgz#1d269cbf7e6243ea886aa41453c3651ccbe13c22" integrity sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg== -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== +bl@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" + integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== dependencies: - file-uri-to-path "1.0.0" + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" boolbase@^1.0.0: version "1.0.0" @@ -475,22 +456,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -498,66 +463,41 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browserslist@^4.21.10: - version "4.23.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" - integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== +browserslist@^4.23.3: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: - caniuse-lite "^1.0.30001646" - electron-to-chromium "^1.5.4" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" node-releases "^2.0.18" - update-browserslist-db "^1.1.0" + update-browserslist-db "^1.1.1" buffer-builder@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/buffer-builder/-/buffer-builder-0.2.0.tgz#3322cd307d8296dab1f604618593b261a3fade8f" integrity sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg== -buffer-equal@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.1.tgz#2f7651be5b1b3f057fcd6e7ee16cf34767077d90" - integrity sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg== - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -call-bind@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" + base64-js "^1.3.1" + ieee754 "^1.2.1" -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg== +caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669: + version "1.0.30001674" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001674.tgz#eb200a716c3e796d33d30b9c8890517a72f862c8" + integrity sha512-jOsKlZVRnzfhLojb+Ykb+gyUSp9Xb57So+fAiFlLzzTKpqg8xxSav0e40c8/4F/v9N8QSvrRRaLeVzQbLqomYw== -caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001646: - version "1.0.30001646" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001646.tgz#d472f2882259ba032dd73ee069ff01bfd059b25d" - integrity sha512-dRg00gudiBDDTmUhClSdv3hqRfpbOnU28IpI1T6PBTLWa+kOj0681C8uML3PifYfREuBrVjDGhL3adYpBT6spw== +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" cheerio-select@^2.1.0: version "2.1.0" @@ -572,19 +512,23 @@ cheerio-select@^2.1.0: domutils "^3.0.1" cheerio@^1.0.0-rc.10: - version "1.0.0-rc.12" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" - integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== + version "1.0.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81" + integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww== dependencies: cheerio-select "^2.1.0" dom-serializer "^2.0.0" domhandler "^5.0.3" - domutils "^3.0.1" - htmlparser2 "^8.0.1" - parse5 "^7.0.0" + domutils "^3.1.0" + encoding-sniffer "^0.2.0" + htmlparser2 "^9.1.0" + parse5 "^7.1.2" parse5-htmlparser2-tree-adapter "^7.0.0" + parse5-parser-stream "^7.1.2" + undici "^6.19.5" + whatwg-mimetype "^4.0.0" -"chokidar@>=3.0.0 <4.0.0": +chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -599,34 +543,12 @@ cheerio@^1.0.0-rc.10: optionalDependencies: fsevents "~2.3.2" -chokidar@^2.0.0: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== +chokidar@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" + integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA== dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" + readdirp "^4.0.1" classlist-polyfill@1.2.0: version "1.2.0" @@ -638,14 +560,14 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" clone-buffer@^1.0.0: version "1.0.0" @@ -657,7 +579,7 @@ clone-stats@^1.0.0: resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" integrity sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag== -clone@^2.1.1: +clone@^2.1.1, clone@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== @@ -671,69 +593,39 @@ cloneable-readable@^1.0.0: process-nextick-args "^2.0.0" readable-stream "^2.3.5" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== - -collection-map@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" - integrity sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA== +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: - arr-map "^2.0.2" - for-own "^1.0.0" - make-iterator "^1.0.0" + color-name "~1.1.4" -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -component-emitter@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" - integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -convert-source-map@^1.0.0, convert-source-map@^1.5.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -copy-props@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.5.tgz#03cf9ae328d4ebb36f8f1d804448a6af9ee3f2d2" - integrity sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw== +copy-props@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-4.0.0.tgz#01d249198b8c2e4d8a5e87b90c9630f52c99a9c9" + integrity sha512-bVWtw1wQLzzKiYROtvNlbJgxgBYt2bMJpkCbKmXM3xyijvcjjWXEk5nyrrT3bgJ7ODb19ZohE2T0Y3FgNPyoTw== dependencies: - each-props "^1.3.2" + each-props "^3.0.0" is-plain-object "^5.0.0" core-util-is@~1.0.0: @@ -765,15 +657,6 @@ css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -css@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" - integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== - dependencies: - inherits "^2.0.4" - source-map "^0.6.1" - source-map-resolve "^0.6.0" - csso@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" @@ -781,99 +664,6 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" -d@1, d@^1.0.1, d@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" - integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== - dependencies: - es5-ext "^0.10.64" - type "^2.7.2" - -debug-fabulous@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e" - integrity sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg== - dependencies: - debug "3.X" - memoizee "0.4.X" - object-assign "4.X" - -debug@3.X: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -decamelize@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - -decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== - -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== - dependencies: - kind-of "^5.0.2" - -default-resolution@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" - integrity sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ== - -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - del@6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" @@ -893,10 +683,10 @@ detect-file@^1.0.0: resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q== -detect-newline@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg== +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== dir-glob@^3.0.1: version "3.0.1" @@ -926,7 +716,7 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" -domutils@^3.0.1: +domutils@^3.0.1, domutils@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== @@ -935,173 +725,89 @@ domutils@^3.0.1: domelementtype "^2.3.0" domhandler "^5.0.3" -duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -each-props@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" - integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== +each-props@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/each-props/-/each-props-3.0.0.tgz#a88fb17634a4828307610ec68269fba2f7280cd8" + integrity sha512-IYf1hpuWrdzse/s/YJOrFmU15lyhSzxelNVAHTEG3DtP4QsLTWZUzcUL3HMXmKQxXpa4EIrBPpwRgj0aehdvAw== dependencies: - is-plain-object "^2.0.1" + is-plain-object "^5.0.0" object.defaults "^1.1.0" -electron-to-chromium@^1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz#cd477c830dd6fca41fbd5465c1ff6ce08ac22343" - integrity sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA== +electron-to-chromium@^1.5.41: + version "1.5.49" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz#9358f514ab6eeed809a8689f4b39ea5114ae729c" + integrity sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A== element-closest@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/element-closest/-/element-closest-2.0.2.tgz#72a740a107453382e28df9ce5dbb5a8df0f966ec" integrity sha512-QCqAWP3kwj8Gz9UXncVXQGdrhnWxD8SQBSeZp5pOsyCcQ6RpL738L1/tfuwBiMi6F1fYkxqPnBrFBR4L+f49Cg== -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encoding-sniffer@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5" + integrity sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg== + dependencies: + iconv-lite "^0.6.3" + whatwg-encoding "^3.1.1" + +end-of-stream@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -entities@^4.2.0, entities@^4.4.0: +entities@^4.2.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.2: - version "0.10.64" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" - integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - esniff "^2.0.1" - next-tick "^1.1.0" - -es6-iterator@^2.0.1, es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" - integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== - dependencies: - d "^1.0.2" - ext "^1.7.0" - -es6-weak-map@^2.0.1, es6-weak-map@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - esbuild@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.0.tgz#de06002d48424d9fdb7eb52dbe8e95927f852599" - integrity sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA== + version "0.23.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.1.tgz#40fdc3f9265ec0beae6f59824ade1bd3d3d2dab8" + integrity sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg== optionalDependencies: - "@esbuild/aix-ppc64" "0.23.0" - "@esbuild/android-arm" "0.23.0" - "@esbuild/android-arm64" "0.23.0" - "@esbuild/android-x64" "0.23.0" - "@esbuild/darwin-arm64" "0.23.0" - "@esbuild/darwin-x64" "0.23.0" - "@esbuild/freebsd-arm64" "0.23.0" - "@esbuild/freebsd-x64" "0.23.0" - "@esbuild/linux-arm" "0.23.0" - "@esbuild/linux-arm64" "0.23.0" - "@esbuild/linux-ia32" "0.23.0" - "@esbuild/linux-loong64" "0.23.0" - "@esbuild/linux-mips64el" "0.23.0" - "@esbuild/linux-ppc64" "0.23.0" - "@esbuild/linux-riscv64" "0.23.0" - "@esbuild/linux-s390x" "0.23.0" - "@esbuild/linux-x64" "0.23.0" - "@esbuild/netbsd-x64" "0.23.0" - "@esbuild/openbsd-arm64" "0.23.0" - "@esbuild/openbsd-x64" "0.23.0" - "@esbuild/sunos-x64" "0.23.0" - "@esbuild/win32-arm64" "0.23.0" - "@esbuild/win32-ia32" "0.23.0" - "@esbuild/win32-x64" "0.23.0" - -escalade@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" - integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + "@esbuild/aix-ppc64" "0.23.1" + "@esbuild/android-arm" "0.23.1" + "@esbuild/android-arm64" "0.23.1" + "@esbuild/android-x64" "0.23.1" + "@esbuild/darwin-arm64" "0.23.1" + "@esbuild/darwin-x64" "0.23.1" + "@esbuild/freebsd-arm64" "0.23.1" + "@esbuild/freebsd-x64" "0.23.1" + "@esbuild/linux-arm" "0.23.1" + "@esbuild/linux-arm64" "0.23.1" + "@esbuild/linux-ia32" "0.23.1" + "@esbuild/linux-loong64" "0.23.1" + "@esbuild/linux-mips64el" "0.23.1" + "@esbuild/linux-ppc64" "0.23.1" + "@esbuild/linux-riscv64" "0.23.1" + "@esbuild/linux-s390x" "0.23.1" + "@esbuild/linux-x64" "0.23.1" + "@esbuild/netbsd-x64" "0.23.1" + "@esbuild/openbsd-arm64" "0.23.1" + "@esbuild/openbsd-x64" "0.23.1" + "@esbuild/sunos-x64" "0.23.1" + "@esbuild/win32-arm64" "0.23.1" + "@esbuild/win32-ia32" "0.23.1" + "@esbuild/win32-x64" "0.23.1" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^1.0.3: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -esniff@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" - integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== - dependencies: - d "^1.0.1" - es5-ext "^0.10.62" - event-emitter "^0.3.5" - type "^2.7.2" - -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" @@ -1109,21 +815,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -ext@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: +extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== @@ -1131,26 +823,12 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0: +extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -fancy-log@^1.3.2, fancy-log@^1.3.3: +fancy-log@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== @@ -1160,6 +838,11 @@ fancy-log@^1.3.2, fancy-log@^1.3.3: parse-node-version "^1.0.0" time-stamp "^1.0.0" +fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.2.9: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -1171,33 +854,25 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-levenshtein@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" - integrity sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw== +fast-levenshtein@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz#37b899ae47e1090e40e3fd2318e4d5f0142ca912" + integrity sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ== + dependencies: + fastest-levenshtein "^1.0.7" -fastq@^1.6.0: +fastest-levenshtein@^1.0.7: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.13.0, fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== dependencies: reusify "^1.0.4" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -1205,59 +880,33 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g== - dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== +findup-sync@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-5.0.0.tgz#54380ad965a7edca00cc8f63113559aadc541bd2" + integrity sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ== dependencies: detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" + is-glob "^4.0.3" + micromatch "^4.0.4" resolve-dir "^1.0.1" -fined@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== +fined@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fined/-/fined-2.0.0.tgz#6846563ed96879ce6de6c85c715c42250f8d8089" + integrity sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A== dependencies: expand-tilde "^2.0.2" - is-plain-object "^2.0.3" + is-plain-object "^5.0.0" object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -flagged-respawn@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== + object.pick "^1.3.0" + parse-filepath "^1.0.2" -flush-write-stream@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" +flagged-respawn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-2.0.0.tgz#abf39719dcfe1ac06c86c9466081c541c682987b" + integrity sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA== -for-in@^1.0.1, for-in@^1.0.2: +for-in@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== @@ -1269,39 +918,24 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -fraction.js@^4.3.6: +fraction.js@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== - dependencies: - map-cache "^0.2.2" - -fs-mkdirp-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" - integrity sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ== +fs-mkdirp-stream@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz#1e82575c4023929ad35cf69269f84f1a8c973aa7" + integrity sha512-UTOY+59K6IA94tec8Wjqm0FSh5OVudGNB0NL/P6fB3HiE3bYOY3VYBGijsnOHNkQSwC1FKkU77pmq7xp9CskLw== dependencies: - graceful-fs "^4.1.11" - through2 "^2.0.3" + graceful-fs "^4.2.8" + streamx "^2.12.0" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -1312,34 +946,10 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - -get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" @@ -1348,36 +958,36 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-stream@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" - integrity sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw== +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: - extend "^3.0.0" - glob "^7.1.1" - glob-parent "^3.1.0" - is-negated-glob "^1.0.0" - ordered-read-streams "^1.0.0" - pumpify "^1.3.5" - readable-stream "^2.1.5" - remove-trailing-separator "^1.0.1" - to-absolute-glob "^2.0.0" - unique-stream "^2.0.2" + is-glob "^4.0.3" -glob-watcher@^5.0.3: - version "5.0.5" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" - integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== - dependencies: - anymatch "^2.0.0" - async-done "^1.2.0" - chokidar "^2.0.0" +glob-stream@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-8.0.2.tgz#09e5818e41c16dd85274d72c7a7158d307426313" + integrity sha512-R8z6eTB55t3QeZMmU1C+Gv+t5UnNRkA55c5yo67fAVfxODxieTwsjNG7utxS/73NdP1NbDgCrhVEg2h00y4fFw== + dependencies: + "@gulpjs/to-absolute-glob" "^4.0.0" + anymatch "^3.1.3" + fastq "^1.13.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" is-negated-glob "^1.0.0" - just-debounce "^1.0.0" normalize-path "^3.0.0" - object.defaults "^1.1.0" + streamx "^2.12.5" -glob@^7.1.1, glob@^7.1.3: +glob-watcher@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-6.0.0.tgz#8565341978a92233fb3881b8857b4d1e9c6bf080" + integrity sha512-wGM28Ehmcnk2NqRORXFOTOR064L4imSw3EeOqU5bIwUf62eXGwg89WivH6VMahL8zlQHeodzvHpXplrqzrz3Nw== + dependencies: + async-done "^2.0.0" + chokidar "^3.5.3" + +glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1421,48 +1031,35 @@ globby@^11.0.1: merge2 "^1.4.1" slash "^3.0.0" -glogg@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" - integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== - dependencies: - sparkles "^1.0.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== +glogg@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-2.2.0.tgz#956ceb855a05a2aa1fa668d748f2be8e7361c11c" + integrity sha512-eWv1ds/zAlz+M1ioHsyKJomfY7jbDDPpwSkv14KQj89bycx1nvK5/2Cj/T9g7kzJcX5Bc7Yv22FjfBZS/jl94A== dependencies: - get-intrinsic "^1.1.3" + sparkles "^2.1.0" -graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4: +graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.8: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -gulp-cli@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" - integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== - dependencies: - ansi-colors "^1.0.1" - archy "^1.0.0" - array-sort "^1.0.0" - color-support "^1.1.3" - concat-stream "^1.6.0" - copy-props "^2.0.1" - fancy-log "^1.3.2" - gulplog "^1.0.0" - interpret "^1.4.0" - isobject "^3.0.1" - liftoff "^3.1.0" - matchdep "^2.0.0" - mute-stdout "^1.0.0" - pretty-hrtime "^1.0.0" - replace-homedir "^1.0.0" - semver-greatest-satisfied-range "^1.1.0" - v8flags "^3.2.0" - yargs "^7.1.0" +gulp-cli@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-3.0.0.tgz#577008f5323fad6106b44db24803c27c3a649841" + integrity sha512-RtMIitkT8DEMZZygHK2vEuLPqLPAFB4sntSxg4NoDta7ciwGZ18l7JuhCTiS5deOJi2IoK0btE+hs6R4sfj7AA== + dependencies: + "@gulpjs/messages" "^1.1.0" + chalk "^4.1.2" + copy-props "^4.0.0" + gulplog "^2.2.0" + interpret "^3.1.1" + liftoff "^5.0.0" + mute-stdout "^2.0.0" + replace-homedir "^2.0.0" + semver-greatest-satisfied-range "^2.0.0" + string-width "^4.2.3" + v8flags "^4.0.0" + yargs "^16.2.0" gulp-postcss@9.0.1: version "9.0.1" @@ -1502,23 +1099,6 @@ gulp-sass@5.1.0: strip-ansi "^6.0.1" vinyl-sourcemaps-apply "^0.2.1" -gulp-sourcemaps@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz#2e154e1a2efed033c0e48013969e6f30337b2743" - integrity sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ== - dependencies: - "@gulp-sourcemaps/identity-map" "^2.0.1" - "@gulp-sourcemaps/map-sources" "^1.0.0" - acorn "^6.4.1" - convert-source-map "^1.0.0" - css "^3.0.0" - debug-fabulous "^1.0.0" - detect-newline "^2.0.0" - graceful-fs "^4.0.0" - source-map "^0.6.0" - strip-bom-string "^1.0.0" - through2 "^2.0.0" - gulp-svgstore@9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/gulp-svgstore/-/gulp-svgstore-9.0.0.tgz#e59af41630410509e5ea044247a42c099a6db2c1" @@ -1529,77 +1109,29 @@ gulp-svgstore@9.0.0: plugin-error "^1.0.1" vinyl "^2.2.1" -gulp@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" - integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== +gulp@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-5.0.0.tgz#78f4b8ac48a0bf61b354d39e5be844de2c5cc3f3" + integrity sha512-S8Z8066SSileaYw1S2N1I64IUc/myI2bqe2ihOBzO6+nKpvNSg7ZcWJt/AwF8LC/NVN+/QZ560Cb/5OPsyhkhg== dependencies: - glob-watcher "^5.0.3" - gulp-cli "^2.2.0" - undertaker "^1.2.1" - vinyl-fs "^3.0.0" + glob-watcher "^6.0.0" + gulp-cli "^3.0.0" + undertaker "^2.0.0" + vinyl-fs "^4.0.0" -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw== +gulplog@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-2.2.0.tgz#71adf43ea5cd07c23ded0fb8af4a844b67c63be8" + integrity sha512-V2FaKiOhpR3DRXZuYdRLn/qiY0yI5XmqbTKrYbdemJ+xOh2d2MOweI/XFgMzd/9+1twdvMwllnZbWZNJ+BOm4A== dependencies: - glogg "^1.0.0" + glogg "^2.2.0" has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -hasown@^2.0.0, hasown@^2.0.2: +hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== @@ -1613,25 +1145,32 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -htmlparser2@^8.0.1: - version "8.0.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" - integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== +htmlparser2@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== dependencies: domelementtype "^2.3.0" domhandler "^5.0.3" - domutils "^3.0.1" - entities "^4.4.0" + domutils "^3.1.0" + entities "^4.5.0" + +iconv-lite@0.6.3, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" - integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== immutable@^4.0.0: version "4.3.7" @@ -1661,15 +1200,10 @@ ini@^1.3.4: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ== +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== is-absolute@^1.0.0: version "1.0.0" @@ -1679,25 +1213,6 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" -is-accessor-descriptor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz#3223b10628354644b86260db29b3e693f5ceedd4" - integrity sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA== - dependencies: - hasown "^2.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== - dependencies: - binary-extensions "^1.0.0" - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1705,46 +1220,13 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-core-module@^2.13.0: - version "2.15.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" - integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== dependencies: hasown "^2.0.2" -is-data-descriptor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz#2109164426166d32ea38c405c1e0945d9e6a4eeb" - integrity sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw== - dependencies: - hasown "^2.0.0" - -is-descriptor@^0.1.0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.7.tgz#2727eb61fd789dcd5bdf0ed4569f551d2fe3be33" - integrity sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg== - dependencies: - is-accessor-descriptor "^1.0.1" - is-data-descriptor "^1.0.1" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.3.tgz#92d27cb3cd311c4977a4db47df457234a13cb306" - integrity sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw== - dependencies: - is-accessor-descriptor "^1.0.1" - is-data-descriptor "^1.0.1" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" @@ -1752,26 +1234,17 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== - dependencies: - number-is-nan "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== - dependencies: - is-extglob "^2.1.0" +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1783,18 +1256,6 @@ is-negated-glob@^1.0.0: resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" integrity sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug== -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== - dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -1810,7 +1271,7 @@ is-path-inside@^3.0.2: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -1822,11 +1283,6 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-promise@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" @@ -1841,22 +1297,17 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" -is-utf8@^0.2.0, is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== - is-valid-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" integrity sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA== -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -isarray@1.0.0, isarray@~1.0.0: +isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== @@ -1866,13 +1317,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== - dependencies: - isarray "1.0.0" - isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -1886,145 +1330,49 @@ istextorbinary@^3.0.0: binaryextensions "^2.2.0" textextensions "^3.2.0" -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -just-debounce@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.1.0.tgz#2f81a3ad4121a76bc7cb45dbf704c0d76a8e5ddf" - integrity sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ== - keyboardevent-key-polyfill@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/keyboardevent-key-polyfill/-/keyboardevent-key-polyfill-1.1.0.tgz#8a319d8e45a13172fca56286372f90c1d4c7014c" integrity sha512-NTDqo7XhzL1fqmUzYroiyK2qGua7sOMzLav35BfNA/mPUSCtw8pZghHFMTYR9JdnJ23IQz695FcaM6EE6bpbFQ== -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" +last-run@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/last-run/-/last-run-2.0.0.tgz#f82dcfbfce6e63d041bd83d64c82e34cdba6572e" + integrity sha512-j+y6WhTLN4Itnf9j5ZQos1BGPCS8DAwmgMroR3OzfxAsBxam0hMw7J8M3KqZl0pLQJ1jNnwIexg5DYpC/ctwEQ== -kind-of@^4.0.0: +lead@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + resolved "https://registry.yarnpkg.com/lead/-/lead-4.0.0.tgz#5317a49effb0e7ec3a0c8fb9c1b24fb716aab939" + integrity sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg== -last-run@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" - integrity sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ== - dependencies: - default-resolution "^2.0.0" - es6-weak-map "^2.0.1" - -lazystream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" - integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== - dependencies: - readable-stream "^2.0.5" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw== - dependencies: - invert-kv "^1.0.0" - -lead@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" - integrity sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow== - dependencies: - flush-write-stream "^1.0.2" - -liftoff@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" - integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== +liftoff@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-5.0.0.tgz#0e5ed275bc334caec0e551ecf08bb22be583e236" + integrity sha512-a5BQjbCHnB+cy+gsro8lXJ4kZluzOijzJ1UVVfyJYZC+IP2pLv1h4+aysQeKuTmyO8NAqfyQAk4HWaP/HjcKTg== dependencies: - extend "^3.0.0" - findup-sync "^3.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" + extend "^3.0.2" + findup-sync "^5.0.0" + fined "^2.0.0" + flagged-respawn "^2.0.0" + is-plain-object "^5.0.0" + rechoir "^0.8.0" + resolve "^1.20.0" lilconfig@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== -lru-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== - dependencies: - es5-ext "~0.10.2" - -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== - dependencies: - kind-of "^6.0.2" - -map-cache@^0.2.0, map-cache@^0.2.2: +map-cache@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== - dependencies: - object-visit "^1.0.0" - -matchdep@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" - integrity sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA== - dependencies: - findup-sync "^2.0.0" - micromatch "^3.0.4" - resolve "^1.4.0" - stack-trace "0.0.10" - matches-selector@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/matches-selector/-/matches-selector-1.2.0.tgz#d1814e7e8f43e69d22ac33c9af727dc884ecf12a" @@ -2035,48 +1383,15 @@ mdn-data@2.0.28: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== -memoizee@0.4.X: - version "0.4.17" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.17.tgz#942a5f8acee281fa6fb9c620bddc57e3b7382949" - integrity sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA== - dependencies: - d "^1.0.2" - es5-ext "^0.10.64" - es6-weak-map "^2.0.3" - event-emitter "^0.3.5" - is-promise "^2.2.2" - lru-queue "^0.1.0" - next-tick "^1.1.0" - timers-ext "^0.1.7" - merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.4: - version "4.0.7" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" - integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== +micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" picomatch "^2.3.1" @@ -2088,84 +1403,27 @@ minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -ms@2.0.0: +mute-stdout@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-2.0.0.tgz#c6a9b4b6185d3b7f70d3ffcb734cbfc8b0f38761" + integrity sha512-32GSKM3Wyc8dg/p39lWPKYu8zci9mJFzV1Np9Of0ZEpe6Fhssn/FbI7ywAMd40uX+p3ZKh3T5EeCFv81qS3HmQ== -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -mute-stdout@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" - integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== - -nan@^2.12.1: - version "2.20.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" - integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== - -nanoid@^3.3.6: +nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== node-releases@^2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.0.1, normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@3.0.0, normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -2175,12 +1433,12 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== -now-and-later@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" - integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== +now-and-later@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-3.0.0.tgz#cdc045dc5b894b35793cf276cc3206077bb7302d" + integrity sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg== dependencies: - once "^1.3.2" + once "^1.4.0" nth-check@^2.0.1: version "2.1.1" @@ -2189,48 +1447,12 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== - -object-assign@4.1.1, object-assign@4.X, object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== - dependencies: - isobject "^3.0.0" - -object.assign@^4.0.4, object.assign@^4.1.0: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== - dependencies: - call-bind "^1.0.5" - define-properties "^1.2.1" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.defaults@^1.0.0, object.defaults@^1.1.0: +object.defaults@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" integrity sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA== @@ -2240,50 +1462,20 @@ object.defaults@^1.0.0, object.defaults@^1.1.0: for-own "^1.0.0" isobject "^3.0.0" -object.map@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - integrity sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w== - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.pick@^1.2.0, object.pick@^1.3.0: +object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== dependencies: isobject "^3.0.1" -object.reduce@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" - integrity sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw== - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -ordered-read-streams@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" - integrity sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw== - dependencies: - readable-stream "^2.0.1" - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g== - dependencies: - lcid "^1.0.0" - p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -2291,7 +1483,7 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -parse-filepath@^1.0.1: +parse-filepath@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" integrity sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q== @@ -2300,13 +1492,6 @@ parse-filepath@^1.0.1: map-cache "^0.2.0" path-root "^0.1.1" -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== - dependencies: - error-ex "^1.2.0" - parse-node-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" @@ -2318,36 +1503,26 @@ parse-passwd@^1.0.0: integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== parse5-htmlparser2-tree-adapter@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" - integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g== + version "7.1.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz#b5a806548ed893a43e24ccb42fbb78069311e81b" + integrity sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g== dependencies: - domhandler "^5.0.2" + domhandler "^5.0.3" parse5 "^7.0.0" -parse5@^7.0.0: +parse5-parser-stream@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" - integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1" + integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow== dependencies: - entities "^4.4.0" - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== + parse5 "^7.0.0" -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== +parse5@^7.0.0, parse5@^7.1.2: + version "7.2.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" + integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== dependencies: - pinkie-promise "^2.0.0" + entities "^4.5.0" path-is-absolute@^1.0.0: version "1.0.1" @@ -2371,52 +1546,21 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" - integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== - -picocolors@^1.0.0, picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== - plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" @@ -2427,11 +1571,6 @@ plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== - postcss-csso@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/postcss-csso/-/postcss-csso-6.0.1.tgz#6a3e812e236fde6d710a525f2b63e6d9da5a5008" @@ -2452,82 +1591,31 @@ postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.31: - version "8.4.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" - integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== - dependencies: - nanoid "^3.3.6" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -postcss@^7.0.16: - version "7.0.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== +postcss@8.4.40: + version "8.4.40" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.40.tgz#eb81f2a4dd7668ed869a6db25999e02e9ad909d8" + integrity sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q== dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" - -pretty-hrtime@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== + nanoid "^3.3.7" + picocolors "^1.0.1" + source-map-js "^1.2.0" process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.5: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -read-pkg-up@^1.0.1: +queue-tick@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -"readable-stream@2 || 3", readable-stream@3: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.2, readable-stream@^2.3.5: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -2540,14 +1628,19 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" + integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== readdirp@~3.6.0: version "3.6.0" @@ -2566,53 +1659,18 @@ receptor@1.0.0: matches-selector "^1.0.0" object-assign "^4.1.0" -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== dependencies: - resolve "^1.1.6" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -remove-bom-buffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" - integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== - dependencies: - is-buffer "^1.1.5" - is-utf8 "^0.2.1" - -remove-bom-stream@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" - integrity sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA== - dependencies: - remove-bom-buffer "^3.0.0" - safe-buffer "^5.1.0" - through2 "^2.0.3" + resolve "^1.20.0" remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== - replace-ext@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" @@ -2623,14 +1681,10 @@ replace-ext@^2.0.0: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-2.0.0.tgz#9471c213d22e1bcc26717cd6e50881d88f812b06" integrity sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug== -replace-homedir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" - integrity sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg== - dependencies: - homedir-polyfill "^1.0.1" - is-absolute "^1.0.0" - remove-trailing-separator "^1.1.0" +replace-homedir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-2.0.0.tgz#245bd9c909275e0beee75eae85bb40780cd61903" + integrity sha512-bgEuQQ/BHW0XkkJtawzrfzHFSN70f/3cNOiHa2QsYxqrjaC30X1k74FJ6xswVBP0sr0SpGIdVFuPwfrYziVeyw== replacestream@^4.0.3: version "4.0.3" @@ -2646,11 +1700,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug== - resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" @@ -2664,19 +1713,14 @@ resolve-id-refs@0.1.0: resolved "https://registry.yarnpkg.com/resolve-id-refs/-/resolve-id-refs-0.1.0.tgz#3126624b887489da8fc0ae889632f8413ac6c3ec" integrity sha512-hNS03NEmVpJheF7yfyagNh57XuKc0z+NkSO0oBbeO67o6IJKoqlDfnNIxhjp7aTWwjmSWZQhtiGrOgZXVyM90w== -resolve-options@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" - integrity sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A== +resolve-options@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-2.0.0.tgz#a1a57a9949db549dd075de3f5550675f02f1e4c5" + integrity sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A== dependencies: - value-or-function "^3.0.0" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + value-or-function "^4.0.0" -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.4.0: +resolve@^1.20.0: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -2685,11 +1729,6 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.4.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -2716,67 +1755,110 @@ rxjs@^7.4.0: dependencies: tslib "^2.1.0" -safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== - dependencies: - ret "~0.1.10" - -sass-embedded-darwin-arm64@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.69.5.tgz#69e246d4a6875184a593906dfd7b84a21eb6eeb2" - integrity sha512-zVuXJzgT54t24E4QPP/iteHsw/cawZE8gAXGEm20cP2DKsIQBF7bvSTk0zzY0bS01YFtJviYM13HcGUe4q7/7w== - -sass-embedded-darwin-x64@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.69.5.tgz#82c2a659dafa93b54d2690f08f7aac9b2447c43f" - integrity sha512-HcA9YER3Ax7lMnHouxnIY462gnst5lNL56QXkZaTQmg9nH7oqR2bMfWbckEQL+mHIXGSM/QfX0aD59VOm5iKZw== - -sass-embedded-linux-arm64@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.69.5.tgz#8585bbcc6996ba04d8aa4216a2bf65de2e6592ea" - integrity sha512-HWCjdFSLGh0dMUNLNh+slc2j9koSZnfTEO9qQR6WEIuC+We6vYKJugGPo1V9pFbBeoW6VAJGYdlqsRPquteCZw== - -sass-embedded-linux-arm@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.69.5.tgz#ded653fe37d6b07d778c5f414cba5f28107dc438" - integrity sha512-m0NxVkrfcS3UsF33q0FgItMWIz/F1FZdfVZpjp+dP6qd0KLeTuoPUCh2GSize0DAU5T0Zj24b2mXeowDKv463g== - -sass-embedded-linux-ia32@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.69.5.tgz#61ad8b3b43be563f6fcd6ff04d0e121b195ecc2a" - integrity sha512-0taR6AJDb+eLOBTEMc1nfX2fI1hgRF9M+Hmv+wwGrxfBu/MkASk6fmR9B8MDw9hPHIqGVUkTVizjOh50O7nIKg== - -sass-embedded-linux-x64@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.69.5.tgz#3d490f520200d596b2b6072d6d3f4460b7114241" - integrity sha512-gN9yLTbKC0hUHukx4mdRs4V39WD719PM2GhWQBUA+3Z8qr9ywywi7LiU2atWrKESRF34V+eqF9lYiYVQxtTHZw== - -sass-embedded-win32-ia32@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.69.5.tgz#e84a053d25aec1176fac485bee65a384f39dfa9e" - integrity sha512-9OgSaufHP53b33gaH1Y5NZ/Im3druCHIQvLUEqJBCFuOzly47g/hZGrO+dBDiWgYGYKbSYI7Z4/PBtQoK5Vkxg== - -sass-embedded-win32-x64@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.69.5.tgz#8d803e99cccc0e8105dde50e49a2fd7839e360ec" - integrity sha512-p1PsOJnpwXdPfiRbX6QdRa4PnL2QXPpIRy8fkeAZpQFYZ278ZIlwemC2MukKMVLcE3iQ5lBulbC8IYm91rod6Q== - -sass-embedded@1.69.5: - version "1.69.5" - resolved "https://registry.yarnpkg.com/sass-embedded/-/sass-embedded-1.69.5.tgz#ae217d4b17b0fb07e5ed146917c9c9de0c4383c6" - integrity sha512-0YNcRcbSpgGd4AnE+mm3a3g4S97puFLIZ0cYJgbwdD4iGz/hiOzE+yh72XS+u1LMhE+pQfNeC9MNnRsc8n1yRg== +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sass-embedded-android-arm64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.77.8.tgz#29dd70d04a13142b62a09bec35a6abe9244d58cf" + integrity sha512-EmWHLbEx0Zo/f/lTFzMeH2Du+/I4RmSRlEnERSUKQWVp3aBSO04QDvdxfFezgQ+2Yt/ub9WMqBpma9P/8MPsLg== + +sass-embedded-android-arm@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-android-arm/-/sass-embedded-android-arm-1.77.8.tgz#7de0641036f1f32e0aec4c250561a3fb9907171e" + integrity sha512-GpGL7xZ7V1XpFbnflib/NWbM0euRzineK0iwoo31/ntWKAXGj03iHhGzkSiOwWSFcXgsJJi3eRA5BTmBvK5Q+w== + +sass-embedded-android-ia32@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.77.8.tgz#24603c38361c916d181d30af79a23016fd110b37" + integrity sha512-+GjfJ3lDezPi4dUUyjQBxlNKXNa+XVWsExtGvVNkv1uKyaOxULJhubVo2G6QTJJU0esJdfeXf5Ca5/J0ph7+7w== + +sass-embedded-android-x64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-android-x64/-/sass-embedded-android-x64-1.77.8.tgz#f53d538f57f109d8a8b8bc64d69a2b1f849c13d2" + integrity sha512-YZbFDzGe5NhaMCygShqkeCWtzjhkWxGVunc7ULR97wmxYPQLPeVyx7XFQZc84Aj0lKAJBJS4qRZeqphMqZEJsQ== + +sass-embedded-darwin-arm64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.77.8.tgz#beb4f56677b9310c21ee1be48080cb70bbd1f145" + integrity sha512-aifgeVRNE+i43toIkDFFJc/aPLMo0PJ5s5hKb52U+oNdiJE36n65n2L8F/8z3zZRvCa6eYtFY2b7f1QXR3B0LA== + +sass-embedded-darwin-x64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.77.8.tgz#fc8a06d98e0d67cdad2e018fbc087fe19a124948" + integrity sha512-/VWZQtcWIOek60Zj6Sxk6HebXA1Qyyt3sD8o5qwbTgZnKitB1iEBuNunyGoAgMNeUz2PRd6rVki6hvbas9hQ6w== + +sass-embedded-linux-arm64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.77.8.tgz#0d771159659d5b2e5742fb9fc7f62c0bf5b5d7f0" + integrity sha512-6iIOIZtBFa2YfMsHqOb3qake3C9d/zlKxjooKKnTSo+6g6z+CLTzMXe1bOfayb7yxeenElmFoK1k54kWD/40+g== + +sass-embedded-linux-arm@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.77.8.tgz#67d73e6726df6d96a4223e1032fe452df3d307ba" + integrity sha512-2edZMB6jf0whx3T0zlgH+p131kOEmWp+I4wnKj7ZMUeokiY4Up05d10hSvb0Q63lOrSjFAWu6P5/pcYUUx8arQ== + +sass-embedded-linux-ia32@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.77.8.tgz#63294592cba393ba852590ed586897340d32caca" + integrity sha512-63GsFFHWN5yRLTWiSef32TM/XmjhCBx1DFhoqxmj+Yc6L9Z1h0lDHjjwdG6Sp5XTz5EmsaFKjpDgnQTP9hJX3Q== + +sass-embedded-linux-musl-arm64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.77.8.tgz#c31b3535e2c027d45155a423f3bebad8a7ed12a6" + integrity sha512-j8cgQxNWecYK+aH8ESFsyam/Q6G+9gg8eJegiRVpA9x8yk3ykfHC7UdQWwUcF22ZcuY4zegrjJx8k+thsgsOVA== + +sass-embedded-linux-musl-arm@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.77.8.tgz#3ed067de1a4c94d3c9462d26842e7f34e1282d6a" + integrity sha512-nFkhSl3uu9btubm+JBW7uRglNVJ8W8dGfzVqh3fyQJKS1oyBC3vT3VOtfbT9YivXk28wXscSHpqXZwY7bUuopA== + +sass-embedded-linux-musl-ia32@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.77.8.tgz#b594999e7fd44df31cf231af3b5dc9707081b64c" + integrity sha512-oWveMe+8TFlP8WBWPna/+Ec5TV0CE+PxEutyi0ltSruBds2zxRq9dPVOqrpPcDN9QUx50vNZC0Afgch0aQEd0g== + +sass-embedded-linux-musl-x64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.77.8.tgz#fb25d36f4640ddff94c9111733b9ce9ecad25a24" + integrity sha512-2NtRpMXHeFo9kaYxuZ+Ewwo39CE7BTS2JDfXkTjZTZqd8H+8KC53eBh516YQnn2oiqxSiKxm7a6pxbxGZGwXOQ== + +sass-embedded-linux-x64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.77.8.tgz#66344634aab8e38f0a8d7a5712a744430bef29d4" + integrity sha512-ND5qZLWUCpOn7LJfOf0gLSZUWhNIysY+7NZK1Ctq+pM6tpJky3JM5I1jSMplNxv5H3o8p80n0gSm+fcjsEFfjQ== + +sass-embedded-win32-arm64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.77.8.tgz#b34b9e637ee82fcf84e7af12fa85ddb1e59c2e62" + integrity sha512-7L8zT6xzEvTYj86MvUWnbkWYCNQP+74HvruLILmiPPE+TCgOjgdi750709BtppVJGGZSs40ZuN6mi/YQyGtwXg== + +sass-embedded-win32-ia32@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.77.8.tgz#284b5d4629c2ca3f406497b9cbb0a9f9a6a85dda" + integrity sha512-7Buh+4bP0WyYn6XPbthkIa3M2vtcR8QIsFVg3JElVlr+8Ng19jqe0t0SwggDgbMX6AdQZC+Wj4F1BprZSok42A== + +sass-embedded-win32-x64@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.77.8.tgz#01d32c063bbd5c3fe6b04a4ec2cdf690e61bbae7" + integrity sha512-rZmLIx4/LLQm+4GW39sRJW0MIlDqmyV0fkRzTmhFP5i/wVC7cuj8TUubPHw18rv2rkHFfBZKZJTCkPjCS5Z+SA== + +sass-embedded@1.77.8: + version "1.77.8" + resolved "https://registry.yarnpkg.com/sass-embedded/-/sass-embedded-1.77.8.tgz#d8d885ccd59c6040fcccd345299a115187d65726" + integrity sha512-WGXA6jcaoBo5Uhw0HX/s6z/sl3zyYQ7ZOnLOJzqwpctFcFmU4L07zn51e2VSkXXFpQZFAdMZNqOGz/7h/fvcRA== dependencies: "@bufbuild/protobuf" "^1.0.0" buffer-builder "^0.2.0" @@ -2785,206 +1867,97 @@ sass-embedded@1.69.5: supports-color "^8.1.1" varint "^6.0.0" optionalDependencies: - sass-embedded-darwin-arm64 "1.69.5" - sass-embedded-darwin-x64 "1.69.5" - sass-embedded-linux-arm "1.69.5" - sass-embedded-linux-arm64 "1.69.5" - sass-embedded-linux-ia32 "1.69.5" - sass-embedded-linux-x64 "1.69.5" - sass-embedded-win32-ia32 "1.69.5" - sass-embedded-win32-x64 "1.69.5" + sass-embedded-android-arm "1.77.8" + sass-embedded-android-arm64 "1.77.8" + sass-embedded-android-ia32 "1.77.8" + sass-embedded-android-x64 "1.77.8" + sass-embedded-darwin-arm64 "1.77.8" + sass-embedded-darwin-x64 "1.77.8" + sass-embedded-linux-arm "1.77.8" + sass-embedded-linux-arm64 "1.77.8" + sass-embedded-linux-ia32 "1.77.8" + sass-embedded-linux-musl-arm "1.77.8" + sass-embedded-linux-musl-arm64 "1.77.8" + sass-embedded-linux-musl-ia32 "1.77.8" + sass-embedded-linux-musl-x64 "1.77.8" + sass-embedded-linux-x64 "1.77.8" + sass-embedded-win32-arm64 "1.77.8" + sass-embedded-win32-ia32 "1.77.8" + sass-embedded-win32-x64 "1.77.8" sass@^1.77.8: - version "1.77.8" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.8.tgz#9f18b449ea401759ef7ec1752a16373e296b52bd" - integrity sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ== + version "1.80.4" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.80.4.tgz#bc0418fd796cad2f1a1309d8b4d7fe44b7027de0" + integrity sha512-rhMQ2tSF5CsuuspvC94nPM9rToiAFw2h3JTrLlgmNw1MH79v8Cr3DH6KF6o6r+8oofY3iYVPUf66KzC8yuVN1w== dependencies: - chokidar ">=3.0.0 <4.0.0" + "@parcel/watcher" "^2.4.1" + chokidar "^4.0.0" immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" -semver-greatest-satisfied-range@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" - integrity sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ== - dependencies: - sver-compat "^1.5.0" - -"semver@2 || 3 || 4 || 5": - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -set-blocking@^2.0.0: +semver-greatest-satisfied-range@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-2.0.0.tgz#4b62942a7a1ccbdb252e5329677c003bac546fe7" + integrity sha512-lH3f6kMbwyANB7HuOWRMlLCa2itaCrZJ+SAqqkSZrZKO/cAsk2EOyaKHUtNkVLFyFW9pct22SFesFp3Z7zpA0g== dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" + sver "^1.8.3" -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== - -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-resolve@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" - integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@^0.5.1, source-map@^0.5.6: +source-map@^0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -sparkles@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" - integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== - -spdx-correct@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" - integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" - integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.18" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz#22aa922dcf2f2885a6494a261f2d8b75345d0326" - integrity sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -stack-trace@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== +sparkles@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-2.1.0.tgz#8ad4e8cecba7e568bba660c39b6db46625ecf1ad" + integrity sha512-r7iW1bDw8R/cFifrD3JnQJX0K1jqT0kprL48BiBpLZLJPmAm34zsVBsK5lc7HirZYZqMW65dOXZgbAGt/I6frg== -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== +stream-composer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-composer/-/stream-composer-1.0.2.tgz#7ee61ca1587bf5f31b2e29aa2093cbf11442d152" + integrity sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w== dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" + streamx "^2.13.2" -stream-exhaust@^1.0.1: +stream-exhaust@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== -stream-shift@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" - integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== +streamx@^2.12.0, streamx@^2.12.5, streamx@^2.13.2, streamx@^2.14.0: + version "2.20.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.1.tgz#471c4f8b860f7b696feb83d5b125caab2fdbb93c" + integrity sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" string_decoder@^1.1.1: version "1.3.0" @@ -3000,31 +1973,19 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^6.0.1: +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: - is-utf8 "^0.2.0" + has-flag "^4.0.0" supports-color@^8.1.1: version "8.1.1" @@ -3038,85 +1999,35 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -sver-compat@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" - integrity sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg== +sver@^1.8.3: + version "1.8.4" + resolved "https://registry.yarnpkg.com/sver/-/sver-1.8.4.tgz#9bd6f6265263f01aab152df935dc7a554c15673f" + integrity sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA== + optionalDependencies: + semver "^6.3.0" + +teex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/teex/-/teex-1.0.1.tgz#b8fa7245ef8e8effa8078281946c85ab780a0b12" + integrity sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg== dependencies: - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" + streamx "^2.12.5" + +text-decoder@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.1.tgz#e173f5121d97bfa3ff8723429ad5ba92e1ead67e" + integrity sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ== textextensions@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-3.3.0.tgz#03530d5287b86773c08b77458589148870cc71d3" integrity sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw== -through2-filter@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.1.0.tgz#4a1b45d2b76b3ac93ec137951e372c268efc1a4e" - integrity sha512-VhZsTsfrIJjyUi6GeecnwcOJlmoqgIdGFDjqnV5ape+F1DN8GejfPO66XyIhoinxmxGImiUTrq9RwpTN5yszGA== - dependencies: - through2 "^4.0.2" - -through2@^2.0.0, through2@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through2@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" - integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== - dependencies: - inherits "^2.0.4" - readable-stream "2 || 3" - -through2@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" - integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== - dependencies: - readable-stream "3" - time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" integrity sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw== -timers-ext@^0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.8.tgz#b4e442f10b7624a29dd2aa42c295e257150cf16c" - integrity sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww== - dependencies: - es5-ext "^0.10.64" - next-tick "^1.1.0" - -to-absolute-glob@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" - integrity sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA== - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -3124,183 +2035,115 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -to-through@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" - integrity sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q== +to-through@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/to-through/-/to-through-3.0.0.tgz#bf4956eaca5a0476474850a53672bed6906ace54" + integrity sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw== dependencies: - through2 "^2.0.3" + streamx "^2.12.5" tslib@^2.1.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" - integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== - -type@^2.7.2: - version "2.7.3" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" - integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + version "2.8.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" + integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== -undertaker-registry@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" - integrity sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw== - -undertaker@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" - integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== - dependencies: - arr-flatten "^1.0.1" - arr-map "^2.0.0" - bach "^1.0.0" - collection-map "^1.0.0" - es6-weak-map "^2.0.1" - fast-levenshtein "^1.0.0" - last-run "^1.1.0" - object.defaults "^1.0.0" - object.reduce "^1.0.0" - undertaker-registry "^1.0.0" - -undici-types@~6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" - integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" +undertaker-registry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-2.0.0.tgz#d434246e398444740dd7fe4c9543e402ad99e4ca" + integrity sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew== -unique-stream@^2.0.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" - integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== +undertaker@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-2.0.0.tgz#fe4d40dc71823ce5a80f1ecc63ec8b88ad40b54a" + integrity sha512-tO/bf30wBbTsJ7go80j0RzA2rcwX6o7XPBpeFcb+jzoeb4pfMM2zUeSDIkY1AWqeZabWxaQZ/h8N9t35QKDLPQ== dependencies: - json-stable-stringify-without-jsonify "^1.0.1" - through2-filter "^3.0.0" + bach "^2.0.1" + fast-levenshtein "^3.0.0" + last-run "^2.0.0" + undertaker-registry "^2.0.0" -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" +undici-types@~6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +undici@^6.19.5: + version "6.20.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-6.20.1.tgz#fbb87b1e2b69d963ff2d5410a40ffb4c9e81b621" + integrity sha512-AjQF1QsmqfJys+LXfGTNum+qw4S88CojRInG/6t31W/1fk6G59s92bnAvGz5Cmur+kQv2SURXEvvudLmbrE8QA== -update-browserslist-db@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" - integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: - escalade "^3.1.2" - picocolors "^1.0.1" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + escalade "^3.2.0" + picocolors "^1.1.0" util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -v8flags@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" +v8flags@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-4.0.1.tgz#98fe6c4308317c5f394d85a435eb192490f7e132" + integrity sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg== -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -value-or-function@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" - integrity sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg== +value-or-function@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-4.0.0.tgz#70836b6a876a010dc3a2b884e7902e9db064378d" + integrity sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg== varint@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== -vinyl-fs@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" - integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== +vinyl-contents@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vinyl-contents/-/vinyl-contents-2.0.0.tgz#cc2ba4db3a36658d069249e9e36d9e2b41935d89" + integrity sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q== dependencies: - fs-mkdirp-stream "^1.0.0" - glob-stream "^6.1.0" - graceful-fs "^4.0.0" + bl "^5.0.0" + vinyl "^3.0.0" + +vinyl-fs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-4.0.0.tgz#06cb36efc911c6e128452f230b96584a9133c3a1" + integrity sha512-7GbgBnYfaquMk3Qu9g22x000vbYkOex32930rBnc3qByw6HfMEAoELjCjoJv4HuEQxHAurT+nvMHm6MnJllFLw== + dependencies: + fs-mkdirp-stream "^2.0.1" + glob-stream "^8.0.0" + graceful-fs "^4.2.11" + iconv-lite "^0.6.3" is-valid-glob "^1.0.0" - lazystream "^1.0.0" - lead "^1.0.0" - object.assign "^4.0.4" - pumpify "^1.3.5" - readable-stream "^2.3.3" - remove-bom-buffer "^3.0.0" - remove-bom-stream "^1.2.0" - resolve-options "^1.1.0" - through2 "^2.0.0" - to-through "^2.0.0" - value-or-function "^3.0.0" - vinyl "^2.0.0" - vinyl-sourcemap "^1.1.0" - -vinyl-sourcemap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" - integrity sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA== + lead "^4.0.0" + normalize-path "3.0.0" + resolve-options "^2.0.0" + stream-composer "^1.0.2" + streamx "^2.14.0" + to-through "^3.0.0" + value-or-function "^4.0.0" + vinyl "^3.0.0" + vinyl-sourcemap "^2.0.0" + +vinyl-sourcemap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz#422f410a0ea97cb54cebd698d56a06d7a22e0277" + integrity sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q== dependencies: - append-buffer "^1.0.2" - convert-source-map "^1.5.0" - graceful-fs "^4.1.6" - normalize-path "^2.1.1" - now-and-later "^2.0.0" - remove-bom-buffer "^3.0.0" - vinyl "^2.0.0" + convert-source-map "^2.0.0" + graceful-fs "^4.2.10" + now-and-later "^3.0.0" + streamx "^2.12.5" + vinyl "^3.0.0" + vinyl-contents "^2.0.0" vinyl-sourcemaps-apply@^0.2.1: version "0.2.1" @@ -3309,7 +2152,7 @@ vinyl-sourcemaps-apply@^0.2.1: dependencies: source-map "^0.5.1" -vinyl@^2.0.0, vinyl@^2.2.1: +vinyl@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== @@ -3321,10 +2164,28 @@ vinyl@^2.0.0, vinyl@^2.2.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - integrity sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ== +vinyl@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-3.0.0.tgz#11e14732bf56e2faa98ffde5157fe6c13259ff30" + integrity sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g== + dependencies: + clone "^2.1.2" + clone-stats "^1.0.0" + remove-trailing-separator "^1.1.0" + replace-ext "^2.0.0" + teex "^1.0.1" + +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== which@^1.2.14: version "1.3.1" @@ -3333,28 +2194,24 @@ which@^1.2.14: dependencies: isexe "^2.0.0" -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yaml@^1.10.2: version "1.10.2" @@ -3366,29 +2223,20 @@ yargs-parser@>=5.0.0-security.0: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs-parser@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.1.tgz#7ede329c1d8cdbbe209bd25cdb990e9b1ebbb394" - integrity sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA== - dependencies: - camelcase "^3.0.0" - object.assign "^4.1.0" +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.2.tgz#63a0a5d42143879fdbb30370741374e0641d55db" - integrity sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA== - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^5.0.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" From 74ebbfc5126bd6643dc1f92f22929958bb4e2b30 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 13:32:31 -0500 Subject: [PATCH 10/48] 200 | Break up logic into smaller methods --- .../manage_evaluators_controller.rb | 87 +++++++++++++------ 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index 0f04c7d3..ba060f0a 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -11,16 +11,9 @@ def index @phases = @challenge.phases.order(:start_date) if @phases.empty? - flash.now[:alert] = t('.no_phases_alert') - @evaluator_invitations = [] - @existing_evaluators = [] + handle_empty_phases else - @phase = params[:phase_id] ? @phases.find(params[:phase_id]) : @phases.first - @evaluator_invitations = @challenge.evaluator_invitations.where(phase: @phase) - @existing_evaluators = @challenge.evaluators. - joins(:challenge_phases_evaluators). - where(challenge_phases_evaluators: { phase: @phase }). - distinct + handle_existing_phases end end @@ -29,15 +22,9 @@ def create result = process_evaluator_invitation(evaluator_invitation_params[:email]) if result[:success] - redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id), - notice: result[:message] + handle_successful_creation(result) else - @evaluator_invitations = @challenge.evaluator_invitations.where(phase: @phase) - @existing_evaluators = @challenge.evaluators. - joins(:challenge_phases_evaluators). - where(challenge_phases_evaluators: { phase: @phase }). - distinct - render :index + handle_failed_creation end end @@ -45,27 +32,51 @@ def destroy @phase = @challenge.phases.find(params[:phase_id]) result = process_evaluator_removal(params[:evaluator_type], params[:evaluator_id]) - if result[:success] - flash[:notice] = result[:message] - render json: { success: true, message: result[:message] } - else - render json: { success: false, message: result[:message] }, status: :unprocessable_entity - end + render_json_response(result) end private + # Setup methods + def set_challenge + @challenge = Challenge.find(params[:challenge_id]) + end + def evaluator_invitation_params params.require(:evaluator_invitation).permit( :first_name, :last_name, :email, :challenge_id, :phase_id, :last_invite_sent ) end - def set_challenge - @challenge = Challenge.find(params[:challenge_id]) + # Index action helpers + def handle_empty_phases + flash.now[:alert] = t('.no_phases_alert') + @evaluator_invitations = [] + @existing_evaluators = [] + end + + def handle_existing_phases + @phase = select_phase + @evaluator_invitations = fetch_evaluator_invitations + @existing_evaluators = fetch_existing_evaluators + end + + def select_phase + params[:phase_id] ? @phases.find(params[:phase_id]) : @phases.first end - # Inviting evaluators + def fetch_evaluator_invitations + @challenge.evaluator_invitations.where(phase: @phase) + end + + def fetch_existing_evaluators + @challenge.evaluators + .joins(:challenge_phases_evaluators) + .where(challenge_phases_evaluators: { phase: @phase }) + .distinct + end + + # Create action helpers def process_evaluator_invitation(email) existing_invitation = @challenge.evaluator_invitations.find_by(email:, phase: @phase) user = User.find_by(email:) @@ -85,7 +96,7 @@ def resend_invitation(invitation) { success: true, message: "An invitation to this challenge has already been sent to " \ - "#{invitation.email}. Invitation has been resent." + "#{invitation.email}. Invitation has been resent." } end @@ -111,7 +122,18 @@ def create_new_invitation(email) end end - # Removing an evaluator from a challenge + def handle_successful_creation(result) + redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id), + notice: result[:message] + end + + def handle_failed_creation + @evaluator_invitations = fetch_evaluator_invitations + @existing_evaluators = fetch_existing_evaluators + render :index + end + + # Destroy action helpers def process_evaluator_removal(evaluator_type, evaluator_id) case evaluator_type when 'user' @@ -145,4 +167,13 @@ def remove_evaluator_invitation(invitation_id) { success: false, message: t('manage_evaluators.remove_evaluator_invitation.failure') } end end + + def render_json_response(result) + if result[:success] + flash[:notice] = result[:message] + render json: { success: true, message: result[:message] } + else + render json: { success: false, message: result[:message] }, status: :unprocessable_entity + end + end end From 25fc5b684d4f6e1d197ecb85b5e00e155621bc60 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 29 Oct 2024 13:36:19 -0500 Subject: [PATCH 11/48] 200 | Update call --- app/controllers/manage_evaluators_controller.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index ba060f0a..b2ebf4ba 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -70,10 +70,10 @@ def fetch_evaluator_invitations end def fetch_existing_evaluators - @challenge.evaluators - .joins(:challenge_phases_evaluators) - .where(challenge_phases_evaluators: { phase: @phase }) - .distinct + @challenge.evaluators. + joins(:challenge_phases_evaluators). + where(challenge_phases_evaluators: { phase: @phase }). + distinct end # Create action helpers From 045385585d3a94c2cfe2ab0c5e7ef6e958e3d955 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Wed, 30 Oct 2024 13:03:36 -0700 Subject: [PATCH 12/48] Update .circleci/config.yml remove duplicate node-version line --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c3aa54fd..a4fb5cd0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,7 +30,6 @@ commands: description: 'Install yarn modules and build assets' steps: - node/install: - node-version: 20.15.1 install-yarn: true node-version: 20.18.0 - node/install-packages: From 888d2ae33e60783340616e4feb6d7589eb338083 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Fri, 1 Nov 2024 15:43:59 -0500 Subject: [PATCH 13/48] 200 | Update queries and naming --- .../evaluator_invitations_controller.rb | 2 +- .../manage_evaluators_controller.rb | 27 +- app/views/manage_evaluators/index.html.erb | 247 +++++++++--------- config/locales/en.yml | 2 +- config/routes.rb | 6 +- .../evaluator_invitations_controller_spec.rb | 4 +- 6 files changed, 140 insertions(+), 148 deletions(-) diff --git a/app/controllers/evaluator_invitations_controller.rb b/app/controllers/evaluator_invitations_controller.rb index 332ca630..ea4508e8 100644 --- a/app/controllers/evaluator_invitations_controller.rb +++ b/app/controllers/evaluator_invitations_controller.rb @@ -4,7 +4,7 @@ class EvaluatorInvitationsController < ApplicationController before_action :set_challenge before_action :set_evaluator_invitation - def resend_invitation + def resend if @evaluator_invitation.update(last_invite_sent: Time.current) # TODO: Implement sending the actual invitation email here redirect_to challenge_manage_evaluators_path(@challenge), diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index b2ebf4ba..0d62a529 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -29,7 +29,7 @@ def create end def destroy - @phase = @challenge.phases.find(params[:phase_id]) + @phase = Phase.find(params[:phase_id]) result = process_evaluator_removal(params[:evaluator_type], params[:evaluator_id]) render_json_response(result) @@ -66,14 +66,11 @@ def select_phase end def fetch_evaluator_invitations - @challenge.evaluator_invitations.where(phase: @phase) + @phase.evaluator_invitations end def fetch_existing_evaluators - @challenge.evaluators. - joins(:challenge_phases_evaluators). - where(challenge_phases_evaluators: { phase: @phase }). - distinct + @phase.evaluators end # Create action helpers @@ -146,26 +143,30 @@ def process_evaluator_removal(evaluator_type, evaluator_id) end def remove_user_evaluator(evaluator_id) - evaluator = @challenge.evaluators.find_by(id: evaluator_id) - return { success: false, message: 'Evaluator not found' } unless evaluator - - cpe = ChallengePhasesEvaluator.find_by(challenge: @challenge, phase: @phase, user: evaluator) - if cpe&.destroy + evaluator = @challenge.evaluators.find(evaluator_id) + cpe = ChallengePhasesEvaluator.find_by!(challenge: @challenge, phase: @phase, user: evaluator) + if cpe.destroy { success: true, message: t('manage_evaluators.remove_user_evaluator.success') } else { success: false, message: t('manage_evaluators.remove_user_evaluator.failure') } end + rescue ActiveRecord::RecordNotFound + { success: false, message: 'Evaluator not found' } rescue StandardError => e { success: false, message: "Error: #{e.message}" } end def remove_evaluator_invitation(invitation_id) - invitation = @challenge.evaluator_invitations.find_by(id: invitation_id, phase: @phase) - if invitation&.destroy + invitation = @challenge.evaluator_invitations.find_by!(id: invitation_id, phase: @phase) + if invitation.destroy { success: true, message: t('manage_evaluators.remove_evaluator_invitation.success') } else { success: false, message: t('manage_evaluators.remove_evaluator_invitation.failure') } end + rescue ActiveRecord::RecordNotFound + { success: false, message: 'Invitation not found' } + rescue StandardError => e + { success: false, message: "Error: #{e.message}" } end def render_json_response(result) diff --git a/app/views/manage_evaluators/index.html.erb b/app/views/manage_evaluators/index.html.erb index 7ef1fa40..d313c11d 100644 --- a/app/views/manage_evaluators/index.html.erb +++ b/app/views/manage_evaluators/index.html.erb @@ -1,132 +1,125 @@ -
    -
    - <%= link_to manage_submissions_path, class: "usa-link display-inline-flex flex-align-center" do %> - <%= image_tag('images/usa-icons/arrow_back.svg', class: "usa-icon--size-3", alt: "Back to previous page") %> - Back - <% end %> -
    - -

    <%= @challenge.title %> - <%= @phase.title %>

    -

    Create and manage a list of evaluators for the challenge.

    - -

    Add Evaluators

    -

    Evaluators will not be assigned to submissions until you assign in the submission detail view.

    - - <%= form_with(model: EvaluatorInvitation.new, url: challenge_manage_evaluators_path(@challenge), method: :post, local: true) do |form| %> - <%= form.hidden_field :challenge_id, value: @challenge.id %> - <%= form.hidden_field :phase_id, value: @phase.id %> - <%= form.hidden_field :last_invite_sent, value: Time.current %> - -
    - <%= form.label :first_name, class: "usa-label font-sans-md" do %> - First Name* - <% end %> -
    Add first and last name in that order.
    - <%= form.text_field :first_name, class: "usa-input", required: true, autocomplete: "given-name", style: "border: 1.5px solid #565c65;" %> -
    - -
    - <%= form.label :last_name, class: "usa-label font-sans-md" do %> - Last Name* - <% end %> -
    Add first and last name in that order.
    - <%= form.text_field :last_name, class: "usa-input", required: true, autocomplete: "family-name", style: "border: 1.5px solid #565c65;" %> -
    - -
    - <%= form.label :email, class: "usa-label font-sans-md" do %> - Email Address* +
    +
    +
    + <%= link_to manage_submissions_path, class: "usa-link display-inline-flex flex-align-center" do %> + <%= image_tag('images/usa-icons/arrow_back.svg', class: "usa-icon--size-3", alt: "Back to previous page") %> + Back <% end %> -
    Add a single email address for the individual.
    - <%= form.email_field :email, class: "usa-input", required: true, autocomplete: "email", style: "border: 1.5px solid #565c65;" %> -
    - - <%= form.button type: 'submit', class: "usa-button margin-top-5 margin-bottom-3" do %> - <%= image_tag('images/usa-icons/person.svg', class: "usa-icon--size-3 icon-white", alt: "Add evaluator") %> - Add to evaluator list - <% end %> - <% end %> - -

    Evaluator List

    - - <% if @evaluator_invitations.empty? && @existing_evaluators.empty? %> -
    -

    There currently are no evaluators available for this challenge phase. Please enter and add evaluators in the section above.

    -
    - <% else %> -
    -

    Review evaluators for this challenge phase and track their submissions assignments.

    + +

    <%= @challenge.title %> - <%= @phase.title %>

    +

    Create and manage a list of evaluators for the challenge.

    +

    Add Evaluators

    +

    Evaluators will not be assigned to submissions until you assign in the submission detail view.

    + <%= form_with(model: EvaluatorInvitation.new, url: challenge_manage_evaluators_path(@challenge), method: :post, local: true) do |form| %> + <%= form.hidden_field :challenge_id, value: @challenge.id %> + <%= form.hidden_field :phase_id, value: @phase.id %> + <%= form.hidden_field :last_invite_sent, value: Time.current %> +
    + <%= form.label :first_name, class: "usa-label font-sans-md" do %> + First Name* + <% end %> +
    Add first and last name in that order.
    + <%= form.text_field :first_name, class: "usa-input", required: true, autocomplete: "given-name", style: "border: 1.5px solid #565c65;" %> +
    +
    + <%= form.label :last_name, class: "usa-label font-sans-md" do %> + Last Name* + <% end %> +
    Add first and last name in that order.
    + <%= form.text_field :last_name, class: "usa-input", required: true, autocomplete: "family-name", style: "border: 1.5px solid #565c65;" %> +
    +
    + <%= form.label :email, class: "usa-label font-sans-md" do %> + Email Address* + <% end %> +
    Add a single email address for the individual.
    + <%= form.email_field :email, class: "usa-input", required: true, autocomplete: "email", style: "border: 1.5px solid #565c65;" %> +
    + <%= form.button type: 'submit', class: "usa-button margin-top-5 margin-bottom-3" do %> + <%= image_tag('images/usa-icons/person.svg', class: "usa-icon--size-3 icon-white", alt: "Add evaluator") %> + Add to evaluator list + <% end %> + <% end %> -
    -
  • Challenge Number of Submissions Evaluation Form Evaluations are due by
    - <%= challenge_phase_title(challenge, phase) %> - + + <%= challenge_phase_title(challenge, phase) %> + <%= challenge.status.capitalize %> + <%= phase.submissions.length %> + <% if phase.evaluation_form %> <%= link_to edit_evaluation_form_path(phase.evaluation_form) do %> <%= phase.evaluation_form.title %> @@ -26,24 +28,25 @@ N/A <% end %> + <% if phase.evaluation_form %> <%= phase.evaluation_form.closing_date %> <% else %> N/A - <% end %> + <% end %> -
    - + <% end %> <% unless phase.submissions.empty? %> <% end %>
    +
    - - - - - - - - - - - - <% @existing_evaluators.each do |evaluator| %> - - - - - - - +

    Evaluator List

    + <% if @evaluator_invitations.empty? && @existing_evaluators.empty? %> +
    +

    There currently are no evaluators available for this challenge phase. Please enter and add evaluators in the section above.

    +
    + <% else %> +
    +

    Review evaluators for this challenge phase and track their submissions assignments.

    +
    +
    +
    Evaluator nameEmail addressUser roleUser statusAssigned submissions
    - <%= "#{evaluator.first_name} #{evaluator.last_name}" %> - <%= evaluator.email %><%= evaluator.role.titleize %><%= user_status(evaluator, @challenge) %><%= assigned_submissions_count(evaluator, @challenge, @phase) %> -
    - <%= link_to manage_submissions_path(@challenge), class: 'usa-button font-body-3xs', style: 'white-space: nowrap;' do %> - View Submissions - <% end %> - <%= button_tag "Delete", type: 'button', class: 'usa-button usa-button--outline font-body-3xs', data: { - open_modal: true, - evaluator_id: evaluator.id, - evaluator_type: 'user', - challenge_id: @challenge.id, - phase_id: @phase.id - } %> -
    -
    + + + + + + + + - <% end %> - <% @challenge.evaluator_invitations.where(phase: @phase).each do |invitation| %> - - - - - - - - - <% end %> - -
    Evaluator nameEmail addressUser roleUser statusAssigned submissions
    - <%= "#{invitation.first_name} #{invitation.last_name}" %> - <%= invitation.email %>Evaluator -
    - Invite Sent - <%= button_to resend_invitation_challenge_evaluator_invitation_path(@challenge, invitation), method: :post, class: 'usa-button usa-button--unstyled margin-left-2', style: 'white-space: nowrap;', form: { class: 'display-inline' } do %> - Resend - <% end %> -
    -
    0 - <%= button_tag "Delete", type: 'button', class: 'usa-button usa-button--outline font-body-3xs', data: { - open_modal: true, - evaluator_id: invitation.id, - evaluator_type: 'invitation', - challenge_id: @challenge.id, - phase_id: invitation.phase_id - } %> -
    -
    - <% end %> - - <%= render 'delete_evaluator_modal' %> + + + <% @existing_evaluators.each do |evaluator| %> + + + <%= "#{evaluator.first_name} #{evaluator.last_name}" %> + + <%= evaluator.email %> + <%= evaluator.role.titleize %> + <%= user_status(evaluator, @challenge) %> + <%= assigned_submissions_count(evaluator, @challenge, @phase) %> + +
    + <%= link_to manage_submissions_path(@challenge), class: 'usa-button font-body-3xs', style: 'white-space: nowrap;' do %> + View Submissions + <% end %> + <%= button_tag "Delete", type: 'button', class: 'usa-button usa-button--outline font-body-3xs', data: { + action: "click->delete-evaluator-modal#open", + evaluator_id: evaluator.id, + evaluator_type: 'user', + challenge_id: @challenge.id, + phase_id: @phase.id + } %> +
    + + + <% end %> + <% @challenge.evaluator_invitations.where(phase: @phase).each do |invitation| %> + + + <%= "#{invitation.first_name} #{invitation.last_name}" %> + + <%= invitation.email %> + Evaluator + +
    + Invite Sent + <%= button_to resend_challenge_evaluator_invitation_path(@challenge, invitation), method: :post, class: 'usa-button usa-button--unstyled margin-left-2', style: 'white-space: nowrap;', form: { class: 'display-inline' } do %> + Resend + <% end %> +
    + + 0 + + <%= button_tag "Delete", type: 'button', class: 'usa-button usa-button--outline font-body-3xs', data: { + action: "click->delete-evaluator-modal#open", + evaluator_id: invitation.id, + evaluator_type: 'invitation', + challenge_id: @challenge.id, + phase_id: @phase.id + } %> + + + <% end %> + + + + <% end %> + <%= render 'delete_evaluator_modal' %> + diff --git a/config/locales/en.yml b/config/locales/en.yml index 83865f9a..1584bc02 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -47,6 +47,6 @@ en: success: "Evaluator invitation successfully removed from the challenge." failure: "Failed to remove evaluator invitation." evaluator_invitations: - resend_invitation: + resend: success: "Invitation resent successfully." failure: "Failed to resend invitation." diff --git a/config/routes.rb b/config/routes.rb index 49bea8df..d142e943 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,16 +30,14 @@ end resources :challenges do - resource :manage_evaluators, only: [] do + resources :manage_evaluators, only: [:index, :create] do collection do - get :index - post :create delete :destroy end end resources :evaluator_invitations, only: [] do member do - post 'resend_invitation' + post 'resend' end end end diff --git a/spec/controllers/evaluator_invitations_controller_spec.rb b/spec/controllers/evaluator_invitations_controller_spec.rb index 38ead643..d300ebb8 100644 --- a/spec/controllers/evaluator_invitations_controller_spec.rb +++ b/spec/controllers/evaluator_invitations_controller_spec.rb @@ -1,14 +1,14 @@ require 'rails_helper' RSpec.describe EvaluatorInvitationsController, type: :request do - describe 'POST #resend_invitation' do + describe 'POST #resend' do it 'updates the last_invite_sent timestamp and redirects with a success message' do challenge = create(:challenge) phase = create(:phase, challenge: challenge) invitation = create(:evaluator_invitation, challenge: challenge, phase: phase) expect { - post resend_invitation_challenge_evaluator_invitation_path(challenge, invitation) + post resend_challenge_evaluator_invitation_path(challenge, invitation) }.to change { invitation.reload.last_invite_sent } expect(response).to redirect_to(challenge_manage_evaluators_path(challenge)) From e4b10f740bebb876c73b2b82dbb5be8d443b0e1a Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Fri, 1 Nov 2024 15:44:23 -0500 Subject: [PATCH 14/48] 200 | Use stimulus for delete evaluator modal --- app/javascript/application.js | 1 - .../delete_evaluator_modal_controller.js | 83 +++++++++++++++++++ app/javascript/controllers/index.js | 2 + app/javascript/delete_evaluator_modal.js | 77 ----------------- .../_delete_evaluator_modal.html.erb | 16 ++-- 5 files changed, 94 insertions(+), 85 deletions(-) create mode 100644 app/javascript/controllers/delete_evaluator_modal_controller.js delete mode 100644 app/javascript/delete_evaluator_modal.js diff --git a/app/javascript/application.js b/app/javascript/application.js index c2e1549a..25c2e08d 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,2 +1 @@ import "./controllers" -import "./delete_evaluator_modal" diff --git a/app/javascript/controllers/delete_evaluator_modal_controller.js b/app/javascript/controllers/delete_evaluator_modal_controller.js new file mode 100644 index 00000000..badb8b2b --- /dev/null +++ b/app/javascript/controllers/delete_evaluator_modal_controller.js @@ -0,0 +1,83 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["modal", "confirmButton", "modalDescription"] + static values = { + challengeId: String, + evaluatorId: String, + evaluatorType: String, + phaseId: String + } + + connect() { + this.modalTarget.addEventListener('click', this.handleOutsideClick.bind(this)) + } + + disconnect() { + this.modalTarget.removeEventListener('click', this.handleOutsideClick.bind(this)) + } + + open(event) { + event.preventDefault() + this.evaluatorIdValue = event.currentTarget.dataset.evaluatorId + this.evaluatorTypeValue = event.currentTarget.dataset.evaluatorType + this.challengeIdValue = event.currentTarget.dataset.challengeId + this.phaseIdValue = event.currentTarget.dataset.phaseId + this.modalTarget.showModal() + } + + close() { + this.modalTarget.close() + this.resetModal() + } + + handleOutsideClick(event) { + if (event.target === this.modalTarget) { + this.close() + } + } + + confirm() { + this.deleteEvaluator() + } + + deleteEvaluator(forceDelete = false) { + const csrfToken = document.querySelector('meta[name="csrf-token"]').content + + fetch(`/challenges/${this.challengeIdValue}/manage_evaluators`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': csrfToken + }, + body: JSON.stringify({ + evaluator_id: this.evaluatorIdValue, + evaluator_type: this.evaluatorTypeValue, + phase_id: this.phaseIdValue, + force_delete: forceDelete + }) + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok') + } + return response.json() + }) + .then(data => { + if (data.success) { + this.close() + window.location.reload() + } else { + throw new Error(data.message || 'Failed to remove evaluator') + } + }) + .catch(error => { + alert(error.message || 'An error occurred while removing the evaluator') + }) + } + + resetModal() { + this.modalDescriptionTarget.textContent = 'Deleting an evaluator from the challenge will remove the evaluator from any submissions of this challenge that the evaluator is assigned to. It will also delete any of their completed or in progress evaluations for those submissions.' + this.confirmButtonTarget.textContent = 'Yes' + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index b752d4cc..a8acd08f 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -7,3 +7,5 @@ import { application } from "./application" import EvaluationFormController from "./evaluation_form_controller" application.register("evaluation-form", EvaluationFormController) +import DeleteEvaluatorModalController from "./delete_evaluator_modal_controller" +application.register("delete-evaluator-modal", DeleteEvaluatorModalController) diff --git a/app/javascript/delete_evaluator_modal.js b/app/javascript/delete_evaluator_modal.js deleted file mode 100644 index eb37b921..00000000 --- a/app/javascript/delete_evaluator_modal.js +++ /dev/null @@ -1,77 +0,0 @@ -export function initializeDeleteEvaluatorModal() { - let currentEvaluatorId, currentEvaluatorType, challengeId, phaseId; - - document.querySelectorAll('[data-open-modal]').forEach(button => { - button.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - currentEvaluatorId = button.dataset.evaluatorId; - currentEvaluatorType = button.dataset.evaluatorType; - challengeId = button.dataset.challengeId; - phaseId = button.dataset.phaseId; - const modal = document.getElementById('delete-evaluator-modal'); - modal.classList.add('is-visible'); - return false; - }); - }); - - document.getElementById('confirmDelete').addEventListener('click', () => { - deleteEvaluator(); - }); - - function deleteEvaluator(forceDelete = false) { - const csrfToken = document.querySelector('meta[name="csrf-token"]').content; - const challengeId = document.querySelector('[data-challenge-id]').dataset.challengeId; - - fetch(`/challenges/${challengeId}/manage_evaluators`, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': csrfToken - }, - body: JSON.stringify({ - evaluator_id: currentEvaluatorId, - evaluator_type: currentEvaluatorType, - phase_id: phaseId, - force_delete: forceDelete - }) - }) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - }) - .then(data => { - if (data.success) { - const modal = document.getElementById('delete-evaluator-modal'); - modal.classList.remove('is-visible'); - window.location.reload(); - } else { - throw new Error(data.message || 'Failed to remove evaluator'); - } - }) - .catch(error => { - console.error('Error:', error); - alert(error.message || 'An error occurred while removing the evaluator'); - }); - } - - document.querySelectorAll('[data-close-modal]').forEach(element => { - element.addEventListener('click', () => { - const modal = document.getElementById('delete-evaluator-modal'); - modal.classList.remove('is-visible'); - - const modalDescription = document.getElementById('modal-1-description'); - modalDescription.textContent = 'Deleting an evaluator from the challenge will remove the evaluator from any submissions of this challenge that the evaluator is assigned to. It will also delete any of their completed or in progress evaluations for those submissions.'; - - const confirmButton = document.getElementById('confirmDelete'); - confirmButton.textContent = 'Yes'; - confirmButton.onclick = () => deleteEvaluator(); - }); - }); -} - -if (document.querySelector('[data-page="manage-evaluators"]')) { - document.addEventListener('DOMContentLoaded', initializeDeleteEvaluatorModal); -} diff --git a/app/views/manage_evaluators/_delete_evaluator_modal.html.erb b/app/views/manage_evaluators/_delete_evaluator_modal.html.erb index af67161d..7df629b7 100644 --- a/app/views/manage_evaluators/_delete_evaluator_modal.html.erb +++ b/app/views/manage_evaluators/_delete_evaluator_modal.html.erb @@ -1,23 +1,24 @@ -
    -
    +
    -
    + From 333a5909148f7e99746e4245ad38e97dbf26b6b1 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Fri, 1 Nov 2024 16:02:16 -0500 Subject: [PATCH 15/48] 200 | Add missing bracket after merge conflict --- app/assets/stylesheets/application.sass.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/application.sass.scss b/app/assets/stylesheets/application.sass.scss index 809d52f5..9eea4bab 100644 --- a/app/assets/stylesheets/application.sass.scss +++ b/app/assets/stylesheets/application.sass.scss @@ -16,6 +16,7 @@ dialog::backdrop { background-color: #dfe1e2 !important; } } +} #add-criteria-button.usa-button { background-color: #4d8055; From 296a0fe250abd8274cb9c335c4d83f840eb32298 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Mon, 4 Nov 2024 11:01:07 -0600 Subject: [PATCH 16/48] 200 | Update query calls --- .../evaluator_invitations_controller.rb | 2 +- .../manage_evaluators_controller.rb | 11 ++- .../evaluator_invitations_controller_spec.rb | 14 +++- .../manage_evaluators_controller_spec.rb | 74 +++++++++---------- 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/app/controllers/evaluator_invitations_controller.rb b/app/controllers/evaluator_invitations_controller.rb index ea4508e8..8d4d7d97 100644 --- a/app/controllers/evaluator_invitations_controller.rb +++ b/app/controllers/evaluator_invitations_controller.rb @@ -18,7 +18,7 @@ def resend private def set_challenge - @challenge = Challenge.find(params[:challenge_id]) + @challenge = current_user.challenge_manager_challenges.find(params[:challenge_id]) end def set_evaluator_invitation diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index 0d62a529..57d13a90 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -4,12 +4,11 @@ class ManageEvaluatorsController < ApplicationController include ManageEvaluatorsHelper before_action :set_challenge + before_action :set_phases, only: [:index] VALID_EVALUATOR_ROLES = %w[evaluator solver challenge_manager].freeze def index - @phases = @challenge.phases.order(:start_date) - if @phases.empty? handle_empty_phases else @@ -29,7 +28,7 @@ def create end def destroy - @phase = Phase.find(params[:phase_id]) + @phase = @challenge.phases.find(params[:phase_id]) result = process_evaluator_removal(params[:evaluator_type], params[:evaluator_id]) render_json_response(result) @@ -39,7 +38,11 @@ def destroy # Setup methods def set_challenge - @challenge = Challenge.find(params[:challenge_id]) + @challenge = current_user.challenge_manager_challenges.find(params[:challenge_id]) + end + + def set_phases + @phases = @challenge.phases.order(:start_date) end def evaluator_invitation_params diff --git a/spec/controllers/evaluator_invitations_controller_spec.rb b/spec/controllers/evaluator_invitations_controller_spec.rb index d300ebb8..af1ee895 100644 --- a/spec/controllers/evaluator_invitations_controller_spec.rb +++ b/spec/controllers/evaluator_invitations_controller_spec.rb @@ -1,12 +1,18 @@ require 'rails_helper' RSpec.describe EvaluatorInvitationsController, type: :request do + let(:user) { create_and_log_in_user(role: 'challenge_manager') } + let(:challenge) { create(:challenge) } + let(:phase) { create(:phase, challenge: challenge) } + let(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase) } + + before do + ChallengeManager.create(user: user, challenge: challenge) + log_in_user(user) + end + describe 'POST #resend' do it 'updates the last_invite_sent timestamp and redirects with a success message' do - challenge = create(:challenge) - phase = create(:phase, challenge: challenge) - invitation = create(:evaluator_invitation, challenge: challenge, phase: phase) - expect { post resend_challenge_evaluator_invitation_path(challenge, invitation) }.to change { invitation.reload.last_invite_sent } diff --git a/spec/controllers/manage_evaluators_controller_spec.rb b/spec/controllers/manage_evaluators_controller_spec.rb index 57219653..7f54578f 100644 --- a/spec/controllers/manage_evaluators_controller_spec.rb +++ b/spec/controllers/manage_evaluators_controller_spec.rb @@ -1,16 +1,23 @@ require 'rails_helper' RSpec.describe ManageEvaluatorsController, type: :request do - let(:challenge) { create(:challenge) } let(:user) { create_and_log_in_user(role: 'challenge_manager') } + let(:challenge) { create(:challenge) } + let(:phase) { create(:phase, challenge: challenge) } + let(:evaluator) { create(:user, role: 'evaluator') } + let(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase) } + + before do + ChallengeManager.create(user: user, challenge: challenge) + log_in_user(user) + end describe 'GET #index' do - it 'assigns @evaluator_invitations and @existing_evaluators' do - evaluator = create(:user, role: 'evaluator') - phase = create(:phase, challenge: challenge) - invitation = create(:evaluator_invitation, challenge: challenge, phase: phase) + before do challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase) + end + it 'assigns @evaluator_invitations and @existing_evaluators' do get challenge_manage_evaluators_path(challenge, phase_id: phase.id) expect(assigns(:evaluator_invitations)).to eq([invitation]) @@ -20,7 +27,6 @@ describe 'POST #create' do context 'with valid params' do - let(:phase) { create(:phase, challenge: challenge) } let(:valid_params) do { evaluator_invitation: attributes_for(:evaluator_invitation).merge(phase_id: phase.id) @@ -41,7 +47,6 @@ end context 'with invalid params' do - let(:phase) { create(:phase, challenge: challenge) } let(:invalid_params) do { evaluator_invitation: attributes_for(:evaluator_invitation, email: nil, phase_id: phase.id) @@ -61,46 +66,43 @@ end context 'when inviting an existing user' do - it 'adds the user as an evaluator without creating a new invitation' do - challenge = create(:challenge) - phase = create(:phase, challenge: challenge) - user = create(:user, role: 'evaluator') + let(:existing_user) { create(:user, role: 'evaluator') } + it 'adds the user as an evaluator without creating a new invitation' do expect { post challenge_manage_evaluators_path(challenge), params: { evaluator_invitation: { - email: user.email, + email: existing_user.email, phase_id: phase.id } } }.to change(ChallengePhasesEvaluator, :count).by(1) expect(EvaluatorInvitation.count).to eq(0) - expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) expect(flash[:notice]).to include("has been added as an evaluator for this phase") end end context 'when inviting a new user' do - it 'creates a new evaluator invitation' do - challenge = create(:challenge) - phase = create(:phase, challenge: challenge) + let(:new_user_params) do + { + evaluator_invitation: { + email: 'new_evaluator@example.com', + phase_id: phase.id, + first_name: 'New', + last_name: 'Evaluator', + last_invite_sent: Time.current + } + } + end + it 'creates a new evaluator invitation' do expect { - post challenge_manage_evaluators_path(challenge), params: { - evaluator_invitation: { - email: 'new_evaluator@example.com', - phase_id: phase.id, - first_name: 'New', - last_name: 'Evaluator', - last_invite_sent: Time.current - } - } + post challenge_manage_evaluators_path(challenge), params: new_user_params }.to change(EvaluatorInvitation, :count).by(1) expect(ChallengePhasesEvaluator.count).to eq(0) - expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) expect(flash[:notice]).to include("Invitation sent to") end @@ -108,11 +110,11 @@ end describe 'DELETE #destroy' do - context 'when removing a user evaluator from a specific phase' do - let!(:evaluator) { create(:user, role: 'evaluator') } - let!(:phase1) { create(:phase, challenge: challenge) } - let!(:phase2) { create(:phase, challenge: challenge) } + let(:phase1) { create(:phase, challenge: challenge) } + let(:phase2) { create(:phase, challenge: challenge) } + let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase1) } + context 'when removing a user evaluator from a specific phase' do before do challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase1) challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase2) @@ -125,32 +127,24 @@ expect(response).to have_http_status(:success) expect(JSON.parse(response.body)['success']).to be true - - # Ensure the evaluator is still associated with the other phase expect(challenge.challenge_phases_evaluators.where(phase: phase2, user: evaluator).count).to eq(1) end end context 'when removing an evaluator invitation' do - let!(:phase) { create(:phase, challenge: challenge) } - let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase) } + let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase1) } it 'removes the evaluator invitation' do expect { - delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: invitation.id, evaluator_type: 'invitation', phase_id: phase.id } + delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: invitation.id, evaluator_type: 'invitation', phase_id: phase1.id } }.to change(EvaluatorInvitation, :count).by(-1) - end - it 'returns a success JSON response' do - delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: invitation.id, evaluator_type: 'invitation', phase_id: phase.id } expect(response).to have_http_status(:success) expect(JSON.parse(response.body)['success']).to be true end end context 'with invalid evaluator type' do - let(:phase) { create(:phase, challenge: challenge) } - it 'returns an error JSON response' do delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: 1, evaluator_type: 'invalid', phase_id: phase.id } From f25eb6dbf533a153d5427c17e9c4dc9c1aeedb87 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 5 Nov 2024 17:18:31 -0600 Subject: [PATCH 17/48] 200 | Update manage evaluators destroy route --- app/controllers/manage_evaluators_controller.rb | 2 +- .../controllers/delete_evaluator_modal_controller.js | 3 +-- config/routes.rb | 6 +----- spec/controllers/manage_evaluators_controller_spec.rb | 6 +++--- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index 57d13a90..ae44ae3b 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -29,7 +29,7 @@ def create def destroy @phase = @challenge.phases.find(params[:phase_id]) - result = process_evaluator_removal(params[:evaluator_type], params[:evaluator_id]) + result = process_evaluator_removal(params[:evaluator_type], params[:id]) render_json_response(result) end diff --git a/app/javascript/controllers/delete_evaluator_modal_controller.js b/app/javascript/controllers/delete_evaluator_modal_controller.js index badb8b2b..a6c0714f 100644 --- a/app/javascript/controllers/delete_evaluator_modal_controller.js +++ b/app/javascript/controllers/delete_evaluator_modal_controller.js @@ -44,14 +44,13 @@ export default class extends Controller { deleteEvaluator(forceDelete = false) { const csrfToken = document.querySelector('meta[name="csrf-token"]').content - fetch(`/challenges/${this.challengeIdValue}/manage_evaluators`, { + fetch(`/challenges/${this.challengeIdValue}/manage_evaluators/${this.evaluatorIdValue}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ - evaluator_id: this.evaluatorIdValue, evaluator_type: this.evaluatorTypeValue, phase_id: this.phaseIdValue, force_delete: forceDelete diff --git a/config/routes.rb b/config/routes.rb index 5dd4002f..24fba733 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,11 +31,7 @@ end resources :challenges do - resources :manage_evaluators, only: [:index, :create] do - collection do - delete :destroy - end - end + resources :manage_evaluators, only: [:index, :create, :destroy] resources :evaluator_invitations, only: [] do member do post 'resend' diff --git a/spec/controllers/manage_evaluators_controller_spec.rb b/spec/controllers/manage_evaluators_controller_spec.rb index 7f54578f..c7d25d8d 100644 --- a/spec/controllers/manage_evaluators_controller_spec.rb +++ b/spec/controllers/manage_evaluators_controller_spec.rb @@ -122,7 +122,7 @@ it 'removes the evaluator from their associated phase' do expect { - delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: evaluator.id, evaluator_type: 'user', phase_id: phase1.id } + delete challenge_manage_evaluator_path(challenge, evaluator), params: { evaluator_type: 'user', phase_id: phase1.id } }.to change { challenge.challenge_phases_evaluators.where(phase: phase1).count }.by(-1) expect(response).to have_http_status(:success) @@ -136,7 +136,7 @@ it 'removes the evaluator invitation' do expect { - delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: invitation.id, evaluator_type: 'invitation', phase_id: phase1.id } + delete challenge_manage_evaluator_path(challenge, invitation), params: { evaluator_type: 'invitation', phase_id: phase1.id } }.to change(EvaluatorInvitation, :count).by(-1) expect(response).to have_http_status(:success) @@ -146,7 +146,7 @@ context 'with invalid evaluator type' do it 'returns an error JSON response' do - delete challenge_manage_evaluators_path(challenge), params: { evaluator_id: 1, evaluator_type: 'invalid', phase_id: phase.id } + delete challenge_manage_evaluator_path(challenge, 1), params: { evaluator_type: 'invalid', phase_id: phase1.id } expect(response).to have_http_status(:unprocessable_entity) expect(JSON.parse(response.body)).to eq({ From f587d15d0cb37c3fbd906b33fae938a2b9dc5e64 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Wed, 6 Nov 2024 14:38:51 -0800 Subject: [PATCH 18/48] [245] Invited Evaluators User Setup (#254) * 245 | Update invited evaluator user setup --- .../manage_evaluators_controller.rb | 87 +++++++------- app/helpers/manage_evaluators_helper.rb | 12 +- app/models/challenge_phases_evaluator.rb | 10 ++ app/models/user.rb | 17 ++- .../manage_evaluators_controller_spec.rb | 110 +++++++++++++++--- .../models/challenge_phases_evaluator_spec.rb | 39 ++++--- spec/models/user_spec.rb | 51 ++++++++ 7 files changed, 238 insertions(+), 88 deletions(-) diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index ae44ae3b..be7b532b 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -6,8 +6,6 @@ class ManageEvaluatorsController < ApplicationController before_action :set_challenge before_action :set_phases, only: [:index] - VALID_EVALUATOR_ROLES = %w[evaluator solver challenge_manager].freeze - def index if @phases.empty? handle_empty_phases @@ -18,12 +16,12 @@ def index def create @phase = @challenge.phases.find(evaluator_invitation_params[:phase_id]) - result = process_evaluator_invitation(evaluator_invitation_params[:email]) + user = User.find_by(email: evaluator_invitation_params[:email]) - if result[:success] - handle_successful_creation(result) + if existing_evaluator?(user) + handle_existing_evaluator(user) else - handle_failed_creation + process_new_evaluator end end @@ -45,6 +43,10 @@ def set_phases @phases = @challenge.phases.order(:start_date) end + def existing_evaluator?(user) + user && ChallengePhasesEvaluator.exists?(challenge: @challenge, phase: @phase, user: user) + end + def evaluator_invitation_params params.require(:evaluator_invitation).permit( :first_name, :last_name, :email, :challenge_id, :phase_id, :last_invite_sent @@ -60,56 +62,44 @@ def handle_empty_phases def handle_existing_phases @phase = select_phase - @evaluator_invitations = fetch_evaluator_invitations - @existing_evaluators = fetch_existing_evaluators + fetch_evaluators_and_invitations end def select_phase params[:phase_id] ? @phases.find(params[:phase_id]) : @phases.first end - def fetch_evaluator_invitations - @phase.evaluator_invitations + def fetch_evaluators_and_invitations + @evaluator_invitations = @phase.evaluator_invitations + @existing_evaluators = @phase.evaluators end - def fetch_existing_evaluators - @phase.evaluators + # Create action helpers + def process_new_evaluator + result = process_evaluator_invitation(evaluator_invitation_params[:email]) + result[:success] ? handle_successful_creation(result) : handle_failed_creation(result[:message]) end - # Create action helpers def process_evaluator_invitation(email) - existing_invitation = @challenge.evaluator_invitations.find_by(email:, phase: @phase) - user = User.find_by(email:) - - if existing_invitation - resend_invitation(existing_invitation) - elsif user && valid_evaluator_role?(user) + user = User.find_by(email: email) + if user add_user_as_evaluator(user) else - create_new_invitation(email) + existing_invitation = @challenge.evaluator_invitations.find_by(email: email, phase: @phase) + existing_invitation ? resend_invitation(existing_invitation) : create_new_invitation(email) end end - # prevent duplicate evaluator invitations - def resend_invitation(invitation) - invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now - { - success: true, - message: "An invitation to this challenge has already been sent to " \ - "#{invitation.email}. Invitation has been resent." - } - end - - def valid_evaluator_role?(user) - VALID_EVALUATOR_ROLES.include?(user.role) - end - def add_user_as_evaluator(user) - cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user:) - if cpe.persisted? - { success: true, message: "#{user.email} has been added as an evaluator for this phase." } + if User::VALID_EVALUATOR_ROLES.include?(user.role) + cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user: user) + if cpe.persisted? + { success: true, message: "#{user.email} has been added as an evaluator for this phase." } + else + { success: false, message: "Failed to add #{user.email} as an evaluator." } + end else - { success: false, message: "Failed to add #{user.email} as an evaluator." } + { success: false, message: "#{user.email} does not have a valid evaluator role." } end end @@ -127,12 +117,27 @@ def handle_successful_creation(result) notice: result[:message] end - def handle_failed_creation - @evaluator_invitations = fetch_evaluator_invitations - @existing_evaluators = fetch_existing_evaluators + def handle_failed_creation(error_message) + flash.now[:alert] = error_message + fetch_evaluators_and_invitations render :index end + # prevent duplicate evaluators or evaluator invitations + def handle_existing_evaluator(user) + flash[:notice] = "#{user.email} has already been added as an evaluator for this phase." + redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id) + end + + def resend_invitation(invitation) + invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now + { + success: true, + message: "An invitation to this challenge has already been sent to " \ + "#{invitation.email}. Invitation has been resent." + } + end + # Destroy action helpers def process_evaluator_removal(evaluator_type, evaluator_id) case evaluator_type diff --git a/app/helpers/manage_evaluators_helper.rb b/app/helpers/manage_evaluators_helper.rb index a20441f6..08b8854a 100644 --- a/app/helpers/manage_evaluators_helper.rb +++ b/app/helpers/manage_evaluators_helper.rb @@ -3,7 +3,7 @@ module ManageEvaluatorsHelper def user_status(evaluator, challenge) if evaluator.is_a?(User) - user_status_for_existing_user(evaluator, challenge) + evaluator.status == 'active' ? "Available" : "Awaiting Approval" else "Invite Sent" end @@ -19,14 +19,4 @@ def assigned_submissions_count(evaluator, challenge, phase) 0 end end - - private - - def user_status_for_existing_user(user, challenge) - if challenge.challenge_phases_evaluators.exists?(user_id: user.id) - user.status == 'active' ? "Available" : "Awaiting Approval" - else - "Invite Sent" - end - end end diff --git a/app/models/challenge_phases_evaluator.rb b/app/models/challenge_phases_evaluator.rb index d2389a27..8c836bc2 100644 --- a/app/models/challenge_phases_evaluator.rb +++ b/app/models/challenge_phases_evaluator.rb @@ -15,4 +15,14 @@ class ChallengePhasesEvaluator < ApplicationRecord belongs_to :challenge belongs_to :phase belongs_to :user + + validate :user_has_valid_role + + private + + def user_has_valid_role + unless User::VALID_EVALUATOR_ROLES.include?(user.role) + errors.add(:user, "must have a valid evaluator role") + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index db1b074e..dfffde1e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -33,6 +33,10 @@ # recertification_expired_at :datetime # class User < ApplicationRecord + after_create :accept_evaluator_invitation + + VALID_EVALUATOR_ROLES = %w[evaluator solver challenge_manager].freeze + belongs_to :agency, optional: true has_many :challenges, dependent: :destroy @@ -130,7 +134,9 @@ def self.create_user_from_userinfo(userinfo) end def self.default_role_and_status_for_email(email) - if default_challenge_manager?(email) + if EvaluatorInvitation.exists?(email: email) + %w[evaluator pending] + elsif default_challenge_manager?(email) %w[challenge_manager pending] else %w[solver active] @@ -140,4 +146,13 @@ def self.default_role_and_status_for_email(email) def self.default_challenge_manager?(email) /\.(gov|mil)$/.match?(email) end + + private + + def accept_evaluator_invitation + EvaluatorInvitation.where(email: self.email).each do |invite| + ChallengePhasesEvaluator.create(challenge: invite.challenge, phase: invite.phase, user: self) + invite.destroy + end + end end diff --git a/spec/controllers/manage_evaluators_controller_spec.rb b/spec/controllers/manage_evaluators_controller_spec.rb index c7d25d8d..016d7157 100644 --- a/spec/controllers/manage_evaluators_controller_spec.rb +++ b/spec/controllers/manage_evaluators_controller_spec.rb @@ -5,7 +5,7 @@ let(:challenge) { create(:challenge) } let(:phase) { create(:phase, challenge: challenge) } let(:evaluator) { create(:user, role: 'evaluator') } - let(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase) } + let(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase, email: 'invitation@example.com') } before do ChallengeManager.create(user: user, challenge: challenge) @@ -66,7 +66,7 @@ end context 'when inviting an existing user' do - let(:existing_user) { create(:user, role: 'evaluator') } + let(:existing_user) { create(:user, role: 'evaluator', status: 'pending') } it 'adds the user as an evaluator without creating a new invitation' do expect { @@ -84,27 +84,103 @@ end end - context 'when inviting a new user' do - let(:new_user_params) do - { - evaluator_invitation: { - email: 'new_evaluator@example.com', - phase_id: phase.id, - first_name: 'New', - last_name: 'Evaluator', - last_invite_sent: Time.current + context 'when inviting a user who is already an evaluator for the challenge phase' do + let(:existing_user) { create(:user, role: 'evaluator', status: 'pending') } + + before do + ChallengePhasesEvaluator.create!(challenge: challenge, phase: phase, user: existing_user) + end + + it 'it does not create an additional ChallengePhaseEvaluator' do + expect { + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: existing_user.email, + phase_id: phase.id + } } - } + }.to_not change(ChallengePhasesEvaluator, :count) + + expect(ChallengePhasesEvaluator.where(challenge: challenge, phase: phase, user: existing_user).count).to eq(1) + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) + expect(flash[:notice]).to include("has already been added as an evaluator for this phase") + end + end + + context 'when inviting a user who already has an invitation for the challenge phase' do + it 'does not create an additional EvaluatorInvitation' do + invitation # create existing invitation + + expect { + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: invitation.email, + phase_id: phase.id + } + } + }.not_to change(EvaluatorInvitation, :count) + + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) + expect(flash[:notice]).to include("An invitation to this challenge has already been sent to #{invitation.email}. Invitation has been resent.") end + end - it 'creates a new evaluator invitation' do + context 'when adding an existing user with a non-evaluator role' do + let(:existing_user) { create(:user, role: 'solver', status: 'pending') } + + it 'adds the user as an evaluator without changing their role' do expect { - post challenge_manage_evaluators_path(challenge), params: new_user_params - }.to change(EvaluatorInvitation, :count).by(1) + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: existing_user.email, + phase_id: phase.id + } + } + }.to change(ChallengePhasesEvaluator, :count).by(1) + + existing_user.reload + expect(existing_user.role).to eq('solver') + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) + expect(flash[:notice]).to include("has been added as an evaluator for this phase") + end + end + + context 'when adding a new user with default evaluator role' do + let(:existing_evaluator) { create(:user, role: 'evaluator', status: 'pending') } + + it 'adds the user without changing their role' do + expect { + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: existing_evaluator.email, + phase_id: phase.id + } + } + }.to change(ChallengePhasesEvaluator, :count).by(1) - expect(ChallengePhasesEvaluator.count).to eq(0) + existing_evaluator.reload + expect(existing_evaluator.role).to eq('evaluator') expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) - expect(flash[:notice]).to include("Invitation sent to") + expect(flash[:notice]).to include("has been added as an evaluator for this phase") + end + end + + context 'when adding an existing user with an invalid role' do + let(:existing_user) { create(:user, role: 'admin') } + + it 'does not add the user as an evaluator and returns an error' do + initial_count = ChallengePhasesEvaluator.count + + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: existing_user.email, + phase_id: phase.id + } + } + + expect(ChallengePhasesEvaluator.count).to eq(initial_count) + expect(response).to render_template(:index) + expect(flash[:alert]).to include("does not have a valid evaluator role") end end end diff --git a/spec/models/challenge_phases_evaluator_spec.rb b/spec/models/challenge_phases_evaluator_spec.rb index df993ed0..b7d1eec9 100644 --- a/spec/models/challenge_phases_evaluator_spec.rb +++ b/spec/models/challenge_phases_evaluator_spec.rb @@ -21,24 +21,6 @@ expect(challenge.evaluators).to include(user) end - it "requires a challenge" do - evaluator = build(:challenge_phases_evaluator, challenge: nil) - expect(evaluator).not_to be_valid - expect(evaluator.errors[:challenge]).to include("must exist") - end - - it "requires a phase" do - evaluator = build(:challenge_phases_evaluator, phase: nil) - expect(evaluator).not_to be_valid - expect(evaluator.errors[:phase]).to include("must exist") - end - - it "requires a user" do - evaluator = build(:challenge_phases_evaluator, user: nil) - expect(evaluator).not_to be_valid - expect(evaluator.errors[:user]).to include("must exist") - end - it "allows multiple evaluators for the same challenge and phase" do challenge = create(:challenge) phase = create(:phase, challenge:) @@ -62,4 +44,25 @@ expect(phase.evaluators).to include(user) expect(user.evaluated_phases).to include(phase) end + + context "with invalid user role" do + let(:invalid_user) { create(:user, role: 'admin') } + + it "is invalid" do + evaluator = build(:challenge_phases_evaluator, challenge:, phase:, user: invalid_user) + expect(evaluator).not_to be_valid + expect(evaluator.errors[:user]).to include("must have a valid evaluator role") + end + end + + User::VALID_EVALUATOR_ROLES.each do |role| + context "with #{role} role" do + let(:valid_user) { create(:user, role: role) } + + it "is valid" do + evaluator = build(:challenge_phases_evaluator, challenge:, phase:, user: valid_user) + expect(evaluator).to be_valid + end + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 39923ad1..9aefc071 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -174,4 +174,55 @@ expect(updated_user.token).to eq(token) end end + + describe '#accept_evaluator_invitation' do + let(:challenge1) { create(:challenge) } + let(:challenge2) { create(:challenge) } + let(:phase1) { create(:phase, challenge: challenge1) } + let(:phase2) { create(:phase, challenge: challenge1) } + let(:phase3) { create(:phase, challenge: challenge2) } + let(:user_email) { 'test@example.com' } + + context 'when there are existing evaluator invitations' do + before do + create(:evaluator_invitation, challenge: challenge1, phase: phase1, email: user_email) + create(:evaluator_invitation, challenge: challenge1, phase: phase2, email: user_email) + create(:evaluator_invitation, challenge: challenge2, phase: phase3, email: user_email) + end + + it 'creates ChallengePhasesEvaluator records for all invitations when user is created' do + expect { + create(:user, email: user_email, role: 'evaluator') + }.to change(ChallengePhasesEvaluator, :count).by(3) + end + + it 'destroys all EvaluatorInvitation records when user is created' do + expect { + create(:user, email: user_email, role: 'evaluator') + }.to change(EvaluatorInvitation, :count).by(-3) + end + + it 'associates the new user with the correct challenges and phases' do + user = create(:user, email: user_email, role: 'evaluator') + expect(user.challenge_phases_evaluators.count).to eq(3) + expect(user.challenge_phases_evaluators.map(&:challenge)).to contain_exactly(challenge1, challenge1, challenge2) + expect(user.challenge_phases_evaluators.map(&:phase)).to contain_exactly(phase1, phase2, phase3) + end + end + + context 'when there are no existing evaluator invitations' do + it 'does not create any ChallengePhasesEvaluator records' do + expect { + create(:user, email: user_email) + }.not_to change(ChallengePhasesEvaluator, :count) + end + + it 'does not destroy any EvaluatorInvitation records' do + expect { + create(:user, email: user_email) + }.not_to change(EvaluatorInvitation, :count) + end + end + + end end From 2a17abd24b947342fdf2e605412d18bbed49182a Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Wed, 6 Nov 2024 16:53:03 -0600 Subject: [PATCH 19/48] 200 | Adjust challenge route --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 24fba733..d457ac5e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,7 +30,7 @@ end end - resources :challenges do + resources :challenges, only: [] do resources :manage_evaluators, only: [:index, :create, :destroy] resources :evaluator_invitations, only: [] do member do From 219095e560d0b6e7b9ccf1619909950f15fa0bd6 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Wed, 6 Nov 2024 16:53:15 -0600 Subject: [PATCH 20/48] 200 | remove unused param --- app/helpers/manage_evaluators_helper.rb | 2 +- app/views/manage_evaluators/index.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/manage_evaluators_helper.rb b/app/helpers/manage_evaluators_helper.rb index 08b8854a..a8058708 100644 --- a/app/helpers/manage_evaluators_helper.rb +++ b/app/helpers/manage_evaluators_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ManageEvaluatorsHelper - def user_status(evaluator, challenge) + def user_status(evaluator) if evaluator.is_a?(User) evaluator.status == 'active' ? "Available" : "Awaiting Approval" else diff --git a/app/views/manage_evaluators/index.html.erb b/app/views/manage_evaluators/index.html.erb index d313c11d..848b7460 100644 --- a/app/views/manage_evaluators/index.html.erb +++ b/app/views/manage_evaluators/index.html.erb @@ -71,7 +71,7 @@ <%= evaluator.email %> <%= evaluator.role.titleize %> - <%= user_status(evaluator, @challenge) %> + <%= user_status(evaluator) %> <%= assigned_submissions_count(evaluator, @challenge, @phase) %>
    From db6e2a79e26f29ee3864609746ed86fd256218bd Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Wed, 6 Nov 2024 16:53:28 -0600 Subject: [PATCH 21/48] 200 | Add guard clause --- app/models/challenge_phases_evaluator.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/models/challenge_phases_evaluator.rb b/app/models/challenge_phases_evaluator.rb index 8c836bc2..03fb38e6 100644 --- a/app/models/challenge_phases_evaluator.rb +++ b/app/models/challenge_phases_evaluator.rb @@ -21,8 +21,7 @@ class ChallengePhasesEvaluator < ApplicationRecord private def user_has_valid_role - unless User::VALID_EVALUATOR_ROLES.include?(user.role) - errors.add(:user, "must have a valid evaluator role") - end + return if User::VALID_EVALUATOR_ROLES.include?(user.role) + errors.add(:user, "must have a valid evaluator role") end end From ffb623d93b09f94430aa5482486bfa3312b398b5 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Wed, 6 Nov 2024 16:54:00 -0600 Subject: [PATCH 22/48] 200 | Remove redundant self check --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index dfffde1e..e8985808 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -150,7 +150,7 @@ def self.default_challenge_manager?(email) private def accept_evaluator_invitation - EvaluatorInvitation.where(email: self.email).each do |invite| + EvaluatorInvitation.where(email: email).find_each do |invite| ChallengePhasesEvaluator.create(challenge: invite.challenge, phase: invite.phase, user: self) invite.destroy end From 1764e7ef008a479b68bfb3aef8459cdfeb869e69 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Thu, 7 Nov 2024 09:07:36 -0600 Subject: [PATCH 23/48] 200 | Fix failing tests --- .../manage_submissions/_phases_table.html.erb | 18 +++++++++--------- spec/requests/manage_submissions_spec.rb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/manage_submissions/_phases_table.html.erb b/app/views/manage_submissions/_phases_table.html.erb index 4c231413..27f82833 100644 --- a/app/views/manage_submissions/_phases_table.html.erb +++ b/app/views/manage_submissions/_phases_table.html.erb @@ -5,7 +5,7 @@ Number of Submissions Evaluation Form Evaluations are due by - + Actions @@ -36,17 +36,16 @@ <% end %> -
    - <%= link_to challenge_manage_evaluators_path(challenge, phase_id: phase.id), class: "usa-button font-body-2xs text-no-wrap margin-bottom-105" do %> +
    + <%= link_to challenge_manage_evaluators_path(challenge, phase_id: phase.id), class: "usa-button font-body-2xs text-no-wrap width-full margin-bottom-105" do %> Manage Evaluators <% end %> <% unless phase.submissions.empty? %> - <%= link_to(challenge_manage_submission_path(challenge, phase)) do %> - + <%= link_to challenge_manage_submission_path(challenge, phase), class: "usa-button font-body-2xs text-no-wrap width-full" do %> + View Submissions <% end %> - <% end %> + <% end %> +
    @@ -54,4 +53,5 @@ <% end %> <% end %> - \ No newline at end of file + + \ No newline at end of file diff --git a/spec/requests/manage_submissions_spec.rb b/spec/requests/manage_submissions_spec.rb index b9320e8c..63735dfa 100644 --- a/spec/requests/manage_submissions_spec.rb +++ b/spec/requests/manage_submissions_spec.rb @@ -70,7 +70,7 @@ it "renders a list of submissions for a user's challenge" do challenge = create_challenge(user: challenge_user, title: "Boston Tea Party Cleanup") phase = create_phase(challenge_id: challenge.id) - submission = create(:submission, challenge: challenge) + submission = create(:submission, challenge: challenge, phase: phase) get challenge_manage_submission_path(challenge, phase) expect(response.body).to include("Boston Tea Party Cleanup") From f77730f7a91cab2127d32a4a12e28e10433f86c1 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Thu, 7 Nov 2024 09:33:31 -0800 Subject: [PATCH 24/48] consolidate routes --- config/routes.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index b916fdcc..92c972f6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,6 +17,12 @@ resources :manage_submissions, only: [:index] resources :challenges, only: [] do resources :manage_submissions, only: [:show] + resources :manage_evaluators, only: [:index, :create, :destroy] + resources :evaluator_invitations, only: [] do + member do + post 'resend' + end + end end # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. @@ -32,13 +38,4 @@ post "/login", to: "accounts#login" end end - - resources :challenges, only: [] do - resources :manage_evaluators, only: [:index, :create, :destroy] - resources :evaluator_invitations, only: [] do - member do - post 'resend' - end - end - end end From adc6422e8458d782fc62a6f15968ce883a18265b Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Thu, 7 Nov 2024 14:53:09 -0800 Subject: [PATCH 25/48] revert structure.sql changes --- db/structure.sql | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/db/structure.sql b/db/structure.sql index 95995622..eef337bd 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9,13 +9,6 @@ SET xmloption = content; SET client_min_messages = warning; SET row_security = off; --- --- Name: public; Type: SCHEMA; Schema: -; Owner: - --- - --- *not* creating schema, since initdb creates it - - -- -- Name: oban_job_state; Type: TYPE; Schema: public; Owner: - -- @@ -692,7 +685,7 @@ CREATE TABLE public.oban_jobs ( attempted_by text[], discarded_at timestamp without time zone, priority integer DEFAULT 0 NOT NULL, - tags character varying(255)[] DEFAULT ARRAY[]::character varying[], + tags text[] DEFAULT ARRAY[]::text[], meta jsonb DEFAULT '{}'::jsonb, cancelled_at timestamp without time zone, CONSTRAINT attempt_range CHECK (((attempt >= 0) AND (attempt <= max_attempts))), From 98bb679befafbc921d62d0ea1d899a7b87030147 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Thu, 7 Nov 2024 14:53:48 -0800 Subject: [PATCH 26/48] remove rbenv from .envrc --- .envrc | 3 --- 1 file changed, 3 deletions(-) diff --git a/.envrc b/.envrc index dd85f90b..4fbb075a 100644 --- a/.envrc +++ b/.envrc @@ -5,8 +5,5 @@ use nix mkdir -p .nix-bundler export BUNDLE_PATH=./.nix-bundler -# hook for rbenv locally -which rbenv &> /dev/null && eval "$(rbenv init - -zsh)" - # Login Env Vars source .env_login From d23da7372a0ba069cae41319b6c19ce639b63715 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:17:22 -0600 Subject: [PATCH 27/48] 200 | Remove unnecessary css property --- app/assets/stylesheets/application.sass.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/application.sass.scss b/app/assets/stylesheets/application.sass.scss index 9eea4bab..9d0fd363 100644 --- a/app/assets/stylesheets/application.sass.scss +++ b/app/assets/stylesheets/application.sass.scss @@ -13,7 +13,7 @@ dialog::backdrop { .usa-table.usa-table--borderless.gray-header { thead { th { - background-color: #dfe1e2 !important; + background-color: #dfe1e2; } } } From b9364aab20701a9bd9a3d4a519e47747dbed2bf0 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:17:51 -0600 Subject: [PATCH 28/48] 200 | Adjust UI --- app/views/manage_evaluators/index.html.erb | 2 +- app/views/manage_submissions/_phases_table.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/manage_evaluators/index.html.erb b/app/views/manage_evaluators/index.html.erb index 848b7460..e1659f31 100644 --- a/app/views/manage_evaluators/index.html.erb +++ b/app/views/manage_evaluators/index.html.erb @@ -51,7 +51,7 @@

    Review evaluators for this challenge phase and track their submissions assignments.

    -
    +
    diff --git a/app/views/manage_submissions/_phases_table.html.erb b/app/views/manage_submissions/_phases_table.html.erb index 27f82833..9e6ec027 100644 --- a/app/views/manage_submissions/_phases_table.html.erb +++ b/app/views/manage_submissions/_phases_table.html.erb @@ -1,4 +1,4 @@ -
    +
    From b725416ccce0488bfe4e1cf6bb320e8bb86635a5 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:18:51 -0600 Subject: [PATCH 29/48] 200 | Move controller specs to spec/requests --- .../evaluator_invitations_controller_spec.rb | 0 .../manage_evaluators_controller_spec.rb | 134 +++++++++--------- 2 files changed, 64 insertions(+), 70 deletions(-) rename spec/{controllers => requests}/evaluator_invitations_controller_spec.rb (100%) rename spec/{controllers => requests}/manage_evaluators_controller_spec.rb (57%) diff --git a/spec/controllers/evaluator_invitations_controller_spec.rb b/spec/requests/evaluator_invitations_controller_spec.rb similarity index 100% rename from spec/controllers/evaluator_invitations_controller_spec.rb rename to spec/requests/evaluator_invitations_controller_spec.rb diff --git a/spec/controllers/manage_evaluators_controller_spec.rb b/spec/requests/manage_evaluators_controller_spec.rb similarity index 57% rename from spec/controllers/manage_evaluators_controller_spec.rb rename to spec/requests/manage_evaluators_controller_spec.rb index 016d7157..07d45ec4 100644 --- a/spec/controllers/manage_evaluators_controller_spec.rb +++ b/spec/requests/manage_evaluators_controller_spec.rb @@ -7,7 +7,10 @@ let(:evaluator) { create(:user, role: 'evaluator') } let(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase, email: 'invitation@example.com') } + let(:evaluator_service) { instance_double(EvaluatorManagementService) } + before do + allow(EvaluatorManagementService).to receive(:new).and_return(evaluator_service) ChallengeManager.create(user: user, challenge: challenge) log_in_user(user) end @@ -29,20 +32,22 @@ context 'with valid params' do let(:valid_params) do { + challenge_id: challenge.id, evaluator_invitation: attributes_for(:evaluator_invitation).merge(phase_id: phase.id) } end - it 'creates a new evaluator invitation' do - expect { - post challenge_manage_evaluators_path(challenge), params: valid_params - }.to change(EvaluatorInvitation, :count).by(1) + it 'calls the EvaluatorManagementService to process the invitation' do + expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, message: 'Invitation sent successfully.' }) + post challenge_manage_evaluators_path(challenge), params: valid_params + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) end it 'redirects to manage_evaluators path with success notice' do + allow(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, message: 'Invitation sent successfully.' }) post challenge_manage_evaluators_path(challenge), params: valid_params expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) - expect(flash[:notice]).to include('Invitation sent') + expect(flash[:notice]).to eq('Invitation sent successfully.') end end @@ -54,33 +59,32 @@ end it 'does not create a new evaluator invitation' do + allow(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: false, message: 'Invalid email' }) expect { post challenge_manage_evaluators_path(challenge), params: invalid_params }.not_to change(EvaluatorInvitation, :count) - end - - it 're-renders the manage_evaluators template' do - post challenge_manage_evaluators_path(challenge), params: invalid_params expect(response).to render_template(:index) end end context 'when inviting an existing user' do - let(:existing_user) { create(:user, role: 'evaluator', status: 'pending') } + let(:existing_user) { create(:user, role: 'evaluator') } it 'adds the user as an evaluator without creating a new invitation' do - expect { - post challenge_manage_evaluators_path(challenge), params: { - evaluator_invitation: { - email: existing_user.email, - phase_id: phase.id - } + expect(evaluator_service).to receive(:process_evaluator_invitation).with( + existing_user.email, + hash_including(phase_id: phase.id.to_s) + ).and_return({ success: true, message: 'User added as an evaluator.' }) + + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: existing_user.email, + phase_id: phase.id } - }.to change(ChallengePhasesEvaluator, :count).by(1) + } - expect(EvaluatorInvitation.count).to eq(0) expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) - expect(flash[:notice]).to include("has been added as an evaluator for this phase") + expect(flash[:notice]).to eq('User added as an evaluator.') end end @@ -91,7 +95,8 @@ ChallengePhasesEvaluator.create!(challenge: challenge, phase: phase, user: existing_user) end - it 'it does not create an additional ChallengePhaseEvaluator' do + it 'does not create an additional ChallengePhaseEvaluator' do + expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: false, message: 'User is already an evaluator for this phase.' }) expect { post challenge_manage_evaluators_path(challenge), params: { evaluator_invitation: { @@ -99,18 +104,14 @@ phase_id: phase.id } } - }.to_not change(ChallengePhasesEvaluator, :count) - - expect(ChallengePhasesEvaluator.where(challenge: challenge, phase: phase, user: existing_user).count).to eq(1) - expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) - expect(flash[:notice]).to include("has already been added as an evaluator for this phase") + }.not_to change(ChallengePhasesEvaluator, :count) end end context 'when inviting a user who already has an invitation for the challenge phase' do it 'does not create an additional EvaluatorInvitation' do invitation # create existing invitation - + expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, message: 'Invitation resent.' }) expect { post challenge_manage_evaluators_path(challenge), params: { evaluator_invitation: { @@ -119,9 +120,6 @@ } } }.not_to change(EvaluatorInvitation, :count) - - expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) - expect(flash[:notice]).to include("An invitation to this challenge has already been sent to #{invitation.email}. Invitation has been resent.") end end @@ -129,39 +127,37 @@ let(:existing_user) { create(:user, role: 'solver', status: 'pending') } it 'adds the user as an evaluator without changing their role' do - expect { - post challenge_manage_evaluators_path(challenge), params: { - evaluator_invitation: { - email: existing_user.email, - phase_id: phase.id - } + expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, message: 'User added as an evaluator.' }) + + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: existing_user.email, + phase_id: phase.id } - }.to change(ChallengePhasesEvaluator, :count).by(1) + } + expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) + expect(flash[:notice]).to eq('User added as an evaluator.') existing_user.reload expect(existing_user.role).to eq('solver') - expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) - expect(flash[:notice]).to include("has been added as an evaluator for this phase") end end - context 'when adding a new user with default evaluator role' do - let(:existing_evaluator) { create(:user, role: 'evaluator', status: 'pending') } + context 'when adding a new user' do + let(:new_user_email) { 'new_user@example.com' } - it 'adds the user without changing their role' do - expect { - post challenge_manage_evaluators_path(challenge), params: { - evaluator_invitation: { - email: existing_evaluator.email, - phase_id: phase.id - } + it 'creates an evaluator invitation' do + expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, message: 'Invitation sent successfully.' }) + + post challenge_manage_evaluators_path(challenge), params: { + evaluator_invitation: { + email: new_user_email, + phase_id: phase.id } - }.to change(ChallengePhasesEvaluator, :count).by(1) + } - existing_evaluator.reload - expect(existing_evaluator.role).to eq('evaluator') expect(response).to redirect_to(challenge_manage_evaluators_path(challenge, phase_id: phase.id)) - expect(flash[:notice]).to include("has been added as an evaluator for this phase") + expect(flash[:notice]).to eq('Invitation sent successfully.') end end @@ -169,7 +165,7 @@ let(:existing_user) { create(:user, role: 'admin') } it 'does not add the user as an evaluator and returns an error' do - initial_count = ChallengePhasesEvaluator.count + expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: false, message: 'User does not have a valid evaluator role.' }) post challenge_manage_evaluators_path(challenge), params: { evaluator_invitation: { @@ -178,9 +174,8 @@ } } - expect(ChallengePhasesEvaluator.count).to eq(initial_count) expect(response).to render_template(:index) - expect(flash[:alert]).to include("does not have a valid evaluator role") + expect(flash[:alert]).to eq('User does not have a valid evaluator role.') end end end @@ -189,41 +184,40 @@ let(:phase1) { create(:phase, challenge: challenge) } let(:phase2) { create(:phase, challenge: challenge) } let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase1) } + let(:evaluator_service) { instance_double(EvaluatorManagementService) } + + before do + allow(EvaluatorManagementService).to receive(:new).and_return(evaluator_service) + end context 'when removing a user evaluator from a specific phase' do - before do - challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase1) - challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase2) - end + let(:evaluator) { create(:user, role: 'evaluator') } it 'removes the evaluator from their associated phase' do - expect { - delete challenge_manage_evaluator_path(challenge, evaluator), params: { evaluator_type: 'user', phase_id: phase1.id } - }.to change { challenge.challenge_phases_evaluators.where(phase: phase1).count }.by(-1) + expect(evaluator_service).to receive(:remove_evaluator).with('user', evaluator.id.to_s).and_return({ success: true, message: 'Evaluator removed successfully.' }) + + delete challenge_manage_evaluator_path(challenge, evaluator), params: { evaluator_type: 'user', phase_id: phase.id } expect(response).to have_http_status(:success) - expect(JSON.parse(response.body)['success']).to be true - expect(challenge.challenge_phases_evaluators.where(phase: phase2, user: evaluator).count).to eq(1) + expect(JSON.parse(response.body)).to eq({ 'success' => true, 'message' => 'Evaluator removed successfully.' }) end end context 'when removing an evaluator invitation' do - let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase1) } - it 'removes the evaluator invitation' do - expect { - delete challenge_manage_evaluator_path(challenge, invitation), params: { evaluator_type: 'invitation', phase_id: phase1.id } - }.to change(EvaluatorInvitation, :count).by(-1) + expect(evaluator_service).to receive(:remove_evaluator).with('invitation', invitation.id.to_s).and_return({ success: true, message: 'Invitation removed successfully.' }) + + delete challenge_manage_evaluator_path(challenge, invitation), params: { evaluator_type: 'invitation', phase_id: phase1.id } expect(response).to have_http_status(:success) - expect(JSON.parse(response.body)['success']).to be true + expect(JSON.parse(response.body)).to eq({ 'success' => true, 'message' => 'Invitation removed successfully.' }) end end context 'with invalid evaluator type' do it 'returns an error JSON response' do + expect(evaluator_service).to receive(:remove_evaluator).and_return({ success: false, message: 'Invalid evaluator type' }) delete challenge_manage_evaluator_path(challenge, 1), params: { evaluator_type: 'invalid', phase_id: phase1.id } - expect(response).to have_http_status(:unprocessable_entity) expect(JSON.parse(response.body)).to eq({ 'success' => false, From 667cb62a06e538f45232a21f007cd8a2df3de7fc Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:20:38 -0600 Subject: [PATCH 30/48] 200 | Require authorized user in evaluator invitations controller --- app/controllers/evaluator_invitations_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/evaluator_invitations_controller.rb b/app/controllers/evaluator_invitations_controller.rb index 8d4d7d97..89069783 100644 --- a/app/controllers/evaluator_invitations_controller.rb +++ b/app/controllers/evaluator_invitations_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class EvaluatorInvitationsController < ApplicationController + before_action -> { authorize_user('challenge_manager') } before_action :set_challenge before_action :set_evaluator_invitation From 9f3d3af0a97fd10d7261e1792be0427fd9d04519 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:21:13 -0600 Subject: [PATCH 31/48] 200 | Move business logic to evaluator management service object --- .../manage_evaluators_controller.rb | 158 +++--------------- 1 file changed, 25 insertions(+), 133 deletions(-) diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index be7b532b..67ac2968 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -1,50 +1,41 @@ -# frozen_string_literal: true - class ManageEvaluatorsController < ApplicationController include ManageEvaluatorsHelper + before_action -> { authorize_user('challenge_manager') } before_action :set_challenge - before_action :set_phases, only: [:index] + before_action :set_phase, only: [:index, :create, :destroy] def index - if @phases.empty? - handle_empty_phases - else - handle_existing_phases - end + @phase ? handle_existing_phases : handle_empty_phases end def create - @phase = @challenge.phases.find(evaluator_invitation_params[:phase_id]) - user = User.find_by(email: evaluator_invitation_params[:email]) + result = evaluator_service.process_evaluator_invitation( + evaluator_invitation_params[:email], + evaluator_invitation_params + ) - if existing_evaluator?(user) - handle_existing_evaluator(user) - else - process_new_evaluator - end + handle_invitation_result(result) end def destroy - @phase = @challenge.phases.find(params[:phase_id]) - result = process_evaluator_removal(params[:evaluator_type], params[:id]) - + result = evaluator_service.remove_evaluator(params[:evaluator_type], params[:id]) render_json_response(result) end private - # Setup methods def set_challenge - @challenge = current_user.challenge_manager_challenges.find(params[:challenge_id]) + @challenge = Challenge.find(params[:challenge_id]) end - def set_phases - @phases = @challenge.phases.order(:start_date) + def set_phase + phase_id = params.dig(:evaluator_invitation, :phase_id) || params[:phase_id] + @phase = phase_id ? @challenge.phases.find(phase_id) : @challenge.phases.order(:start_date).first end - def existing_evaluator?(user) - user && ChallengePhasesEvaluator.exists?(challenge: @challenge, phase: @phase, user: user) + def evaluator_service + @evaluator_service ||= EvaluatorManagementService.new(@challenge, @phase) end def evaluator_invitation_params @@ -53,20 +44,14 @@ def evaluator_invitation_params ) end - # Index action helpers - def handle_empty_phases - flash.now[:alert] = t('.no_phases_alert') - @evaluator_invitations = [] - @existing_evaluators = [] - end - def handle_existing_phases - @phase = select_phase fetch_evaluators_and_invitations end - def select_phase - params[:phase_id] ? @phases.find(params[:phase_id]) : @phases.first + def handle_empty_phases + flash.now[:alert] = t('.no_phases_alert') + @evaluator_invitations = [] + @existing_evaluators = [] end def fetch_evaluators_and_invitations @@ -74,107 +59,14 @@ def fetch_evaluators_and_invitations @existing_evaluators = @phase.evaluators end - # Create action helpers - def process_new_evaluator - result = process_evaluator_invitation(evaluator_invitation_params[:email]) - result[:success] ? handle_successful_creation(result) : handle_failed_creation(result[:message]) - end - - def process_evaluator_invitation(email) - user = User.find_by(email: email) - if user - add_user_as_evaluator(user) - else - existing_invitation = @challenge.evaluator_invitations.find_by(email: email, phase: @phase) - existing_invitation ? resend_invitation(existing_invitation) : create_new_invitation(email) - end - end - - def add_user_as_evaluator(user) - if User::VALID_EVALUATOR_ROLES.include?(user.role) - cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user: user) - if cpe.persisted? - { success: true, message: "#{user.email} has been added as an evaluator for this phase." } - else - { success: false, message: "Failed to add #{user.email} as an evaluator." } - end - else - { success: false, message: "#{user.email} does not have a valid evaluator role." } - end - end - - def create_new_invitation(email) - invitation = @challenge.evaluator_invitations.new(evaluator_invitation_params.merge(phase: @phase)) - if invitation.save - { success: true, message: "Invitation sent to #{email} for this challenge phase." } - else - { success: false, message: invitation.errors.full_messages.join(", ") } - end - end - - def handle_successful_creation(result) - redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id), - notice: result[:message] - end - - def handle_failed_creation(error_message) - flash.now[:alert] = error_message - fetch_evaluators_and_invitations - render :index - end - - # prevent duplicate evaluators or evaluator invitations - def handle_existing_evaluator(user) - flash[:notice] = "#{user.email} has already been added as an evaluator for this phase." - redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id) - end - - def resend_invitation(invitation) - invitation.update(last_invite_sent: Time.current) # only update last_invite_sent for now - { - success: true, - message: "An invitation to this challenge has already been sent to " \ - "#{invitation.email}. Invitation has been resent." - } - end - - # Destroy action helpers - def process_evaluator_removal(evaluator_type, evaluator_id) - case evaluator_type - when 'user' - remove_user_evaluator(evaluator_id) - when 'invitation' - remove_evaluator_invitation(evaluator_id) - else - { success: false, message: 'Invalid evaluator type' } - end - end - - def remove_user_evaluator(evaluator_id) - evaluator = @challenge.evaluators.find(evaluator_id) - cpe = ChallengePhasesEvaluator.find_by!(challenge: @challenge, phase: @phase, user: evaluator) - if cpe.destroy - { success: true, message: t('manage_evaluators.remove_user_evaluator.success') } - else - { success: false, message: t('manage_evaluators.remove_user_evaluator.failure') } - end - rescue ActiveRecord::RecordNotFound - { success: false, message: 'Evaluator not found' } - rescue StandardError => e - { success: false, message: "Error: #{e.message}" } - end - - def remove_evaluator_invitation(invitation_id) - invitation = @challenge.evaluator_invitations.find_by!(id: invitation_id, phase: @phase) - if invitation.destroy - { success: true, message: t('manage_evaluators.remove_evaluator_invitation.success') } + def handle_invitation_result(result) + if result[:success] + redirect_to challenge_manage_evaluators_path(@challenge, phase_id: @phase.id), notice: result[:message] else - { success: false, message: t('manage_evaluators.remove_evaluator_invitation.failure') } + flash.now[:alert] = result[:message] + handle_existing_phases + render :index end - rescue ActiveRecord::RecordNotFound - { success: false, message: 'Invitation not found' } - rescue StandardError => e - { success: false, message: "Error: #{e.message}" } end def render_json_response(result) From c1cb69c0cabe3f4d34764dddf2631b365edb6fa8 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:21:33 -0600 Subject: [PATCH 32/48] 200 | Add evaluator management service object + tests --- app/services/evaluator_management_service.rb | 95 +++++++++++++++ .../evaluator_management_service_spec.rb | 110 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 app/services/evaluator_management_service.rb create mode 100644 spec/services/evaluator_management_service_spec.rb diff --git a/app/services/evaluator_management_service.rb b/app/services/evaluator_management_service.rb new file mode 100644 index 00000000..1c36711e --- /dev/null +++ b/app/services/evaluator_management_service.rb @@ -0,0 +1,95 @@ +class EvaluatorManagementService + def initialize(challenge, phase) + @challenge = challenge + @phase = phase + end + + def process_evaluator_invitation(email, invitation_params) + user = User.find_by(email: email) + user ? add_existing_user_as_evaluator(user) : handle_invitation(email, invitation_params) + end + + def remove_evaluator(evaluator_type, evaluator_id) + case evaluator_type + when 'user' + remove_user_evaluator(evaluator_id) + when 'invitation' + remove_evaluator_invitation(evaluator_id) + else + { success: false, message: 'Invalid evaluator type' } + end + end + + def self.accept_evaluator_invitation(user) + invitations = EvaluatorInvitation.where(email: user.email) + invitations.each do |invite| + ChallengePhasesEvaluator.create(challenge: invite.challenge, phase: invite.phase, user: user) + invite.destroy + end + { success: true, message: I18n.t('manage_evaluators.accept_evaluator_invitation.success') } + end + + private + + def add_existing_user_as_evaluator(user) + return { success: true, message: I18n.t('manage_evaluators.process_evaluator_invitation.already_added', email: user.email) } if @phase.evaluators.include?(user) + return { success: false, message: I18n.t('manage_evaluators.process_evaluator_invitation.invalid_role', email: user.email) } unless User::VALID_EVALUATOR_ROLES.include?(user.role) + + cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user: user) + + if cpe.persisted? + { success: true, message: I18n.t('manage_evaluators.process_evaluator_invitation.add_success', email: user.email) } + else + { success: false, message: I18n.t('manage_evaluators.process_evaluator_invitation.add_failure', email: user.email) } + end + end + + def handle_invitation(email, invitation_params) + existing_invitation = @challenge.evaluator_invitations.find_by(email: email, phase: @phase) + existing_invitation ? resend_invitation(existing_invitation) : create_new_invitation(invitation_params) + end + + def create_new_invitation(invitation_params) + invitation = @challenge.evaluator_invitations.new(invitation_params.merge(phase: @phase, last_invite_sent: Time.current)) + if invitation.save + { success: true, message: I18n.t('manage_evaluators.process_evaluator_invitation.invitation_sent', email: invitation_params[:email]) } + else + { success: false, message: invitation.errors.full_messages.join(", ") } + end + end + + def resend_invitation(invitation) + invitation.update(last_invite_sent: Time.current) + { + success: true, + message: I18n.t('manage_evaluators.process_evaluator_invitation.invitation_resent', email: invitation.email) + } + end + + def remove_user_evaluator(evaluator_id) + evaluator = User.find(evaluator_id) + cpe = ChallengePhasesEvaluator.find_by(challenge: @challenge, phase: @phase, user: evaluator) + if cpe.destroy + { success: true, message: I18n.t('manage_evaluators.remove_user_evaluator.success') } + else + { success: false, message: I18n.t('manage_evaluators.remove_user_evaluator.failure') } + end + rescue ActiveRecord::RecordNotFound + { success: false, message: I18n.t('manage_evaluators.remove_user_evaluator.evaluator_not_found') } + rescue StandardError => e + { success: false, message: "Error: #{e.message}" } + end + + def remove_evaluator_invitation(invitation_id) + invitation = @challenge.evaluator_invitations.find_by!(id: invitation_id, phase: @phase) + if invitation.destroy + { success: true, message: I18n.t('manage_evaluators.remove_evaluator_invitation.success') } + else + { success: false, message: I18n.t('manage_evaluators.remove_evaluator_invitation.failure') } + end + rescue ActiveRecord::RecordNotFound + { success: false, message: I18n.t('manage_evaluators.remove_evaluator_invitation.invitation_not_found') } + rescue StandardError => e + { success: false, message: "Error: #{e.message}" } + end +end diff --git a/spec/services/evaluator_management_service_spec.rb b/spec/services/evaluator_management_service_spec.rb new file mode 100644 index 00000000..ff052c75 --- /dev/null +++ b/spec/services/evaluator_management_service_spec.rb @@ -0,0 +1,110 @@ +require 'rails_helper' + +RSpec.describe EvaluatorManagementService do + let(:user) { create_and_log_in_user(role: 'challenge_manager') } + let(:challenge) { create(:challenge) } + let(:phase) { create(:phase, challenge: challenge) } + let(:service) { EvaluatorManagementService.new(challenge, phase) } + + describe '#process_evaluator_invitation' do + context 'with an existing user' do + let(:evaluator) { create(:user, role: 'evaluator') } + + it 'adds the user as an evaluator if not already added' do + result = service.process_evaluator_invitation(evaluator.email, {}) + expect(result[:success]).to be true + expect(result[:message]).to include('has been added as an evaluator') + expect(ChallengePhasesEvaluator.where(challenge: challenge, phase: phase, user: evaluator).count).to eq(1) + end + + it 'does not add the user if already an evaluator' do + create(:challenge_phases_evaluator, challenge: challenge, phase: phase, user: evaluator) + result = service.process_evaluator_invitation(evaluator.email, {}) + expect(result[:success]).to be true + expect(result[:message]).to include('has already been added as an evaluator') + expect(ChallengePhasesEvaluator.where(challenge: challenge, phase: phase, user: evaluator).count).to eq(1) + end + + it 'does not add the user with an invalid role' do + evaluator.update(role: 'admin') + result = service.process_evaluator_invitation(evaluator.email, {}) + expect(result[:success]).to be false + expect(result[:message]).to include('does not have a valid evaluator role') + end + end + + context 'with a new user' do + let(:email) { 'new_evaluator@example.com' } + let(:invitation_params) do + { + email: email, + first_name: 'John', + last_name: 'Doe', + last_invite_sent: Time.current + } + end + + it 'creates a new invitation' do + result = service.process_evaluator_invitation(email, invitation_params) + expect(result[:success]).to be true + expect(result[:message]).to include('Invitation sent') + expect(EvaluatorInvitation.find_by(email: email)).to be_present + end + + it 'resends an existing invitation' do + create(:evaluator_invitation, challenge: challenge, phase: phase, email: email) + result = service.process_evaluator_invitation(email, { email: email }) + expect(result[:success]).to be true + expect(result[:message]).to include('Invitation has been resent') + end + end + end + + describe '#remove_evaluator' do + context 'when removing a user evaluator' do + let(:evaluator) { create(:user, role: 'evaluator') } + let!(:cpe) { create(:challenge_phases_evaluator, challenge: challenge, phase: phase, user: evaluator) } + + it 'removes the evaluator successfully' do + result = service.remove_evaluator('user', evaluator.id) + expect(result[:success]).to be true + expect(result[:message]).to include('Evaluator successfully removed') + expect(ChallengePhasesEvaluator.find_by(id: cpe.id)).to be_nil + end + end + + context 'when removing an invitation' do + let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase) } + + it 'removes the invitation successfully' do + result = service.remove_evaluator('invitation', invitation.id) + expect(result[:success]).to be true + expect(result[:message]).to include('Evaluator invitation successfully removed') + expect(EvaluatorInvitation.find_by(id: invitation.id)).to be_nil + end + end + + it 'handles invalid evaluator types' do + result = service.remove_evaluator('invalid', 1) + expect(result[:success]).to be false + expect(result[:message]).to eq('Invalid evaluator type') + end + end + + describe '.accept_evaluator_invitation' do + let(:evaluator) { create(:user, role: 'evaluator') } + let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase, email: evaluator.email) } + + it 'processes all invitations for the user' do + expect { + EvaluatorManagementService.accept_evaluator_invitation(evaluator) + }.to change(ChallengePhasesEvaluator, :count).by(1) + .and change(EvaluatorInvitation, :count).by(-1) + end + + it 'returns a success message' do + result = EvaluatorManagementService.accept_evaluator_invitation(evaluator) + expect(result).to eq({ success: true, message: 'Evaluator created and added to challenge phase successfully.' }) + end + end +end From a630bb9a588cf5f1c25fdf487570a2f09f9a16f7 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:22:10 -0600 Subject: [PATCH 33/48] 200 | Use service object on after_create in user model for processign evaluator invitations --- app/models/user.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index e8985808..771bc1a4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +require_relative '../services/evaluator_management_service' # == Schema Information # @@ -33,7 +34,8 @@ # recertification_expired_at :datetime # class User < ApplicationRecord - after_create :accept_evaluator_invitation + after_create :process_evaluator_invitations + VALID_EVALUATOR_ROLES = %w[evaluator solver challenge_manager].freeze @@ -149,10 +151,7 @@ def self.default_challenge_manager?(email) private - def accept_evaluator_invitation - EvaluatorInvitation.where(email: email).find_each do |invite| - ChallengePhasesEvaluator.create(challenge: invite.challenge, phase: invite.phase, user: self) - invite.destroy - end + def process_evaluator_invitations + EvaluatorManagementService.accept_evaluator_invitation(self) end end From 5e7eb94c4c2916328d0b9468f65ad43a70683a40 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 12 Nov 2024 16:22:16 -0600 Subject: [PATCH 34/48] 200 | Add translations --- config/locales/en.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index b47db12e..2c012c09 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -40,12 +40,22 @@ en: index: no_phases_alert: "This challenge has no phases. Please add at least one phase before managing evaluators." remove_user_evaluator: - evaluator_not_found: "Evaluator not found" + evaluator_not_found: "Evaluator not found." success: "Evaluator successfully removed from this phase." failure: "Failed to remove evaluator from this phase." remove_evaluator_invitation: + invitation_not_found: "Invitation not found." success: "Evaluator invitation successfully removed from the challenge." - failure: "Failed to remove evaluator invitation." + failure: "Failed to remove evaluator invitation." + process_evaluator_invitation: + already_added: "%{email} has already been added as an evaluator for this phase." + invalid_role: "%{email} does not have a valid evaluator role." + add_success: "%{email} has been added as an evaluator for this phase." + add_failure: "Failed to add %{email} as an evaluator." + invitation_sent: "Invitation sent to %{email} for this challenge phase." + invitation_resent: "An invitation to this challenge has already been sent to %{email}. Invitation has been resent." + accept_evaluator_invitation: + success: "Evaluator created and added to challenge phase successfully." evaluator_invitations: resend: success: "Invitation resent successfully." From de375d6c7bdc2f3adc29ee3d921514203e41d1e2 Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Wed, 13 Nov 2024 09:20:12 -0600 Subject: [PATCH 35/48] 200 | Fix some formatting --- .../manage_evaluators_controller.rb | 2 + app/models/challenge_phases_evaluator.rb | 1 + app/models/user.rb | 4 +- app/services/evaluator_management_service.rb | 59 +++++++++++++++---- 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index 67ac2968..2df778cd 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ManageEvaluatorsController < ApplicationController include ManageEvaluatorsHelper diff --git a/app/models/challenge_phases_evaluator.rb b/app/models/challenge_phases_evaluator.rb index 03fb38e6..b622f4cb 100644 --- a/app/models/challenge_phases_evaluator.rb +++ b/app/models/challenge_phases_evaluator.rb @@ -22,6 +22,7 @@ class ChallengePhasesEvaluator < ApplicationRecord def user_has_valid_role return if User::VALID_EVALUATOR_ROLES.include?(user.role) + errors.add(:user, "must have a valid evaluator role") end end diff --git a/app/models/user.rb b/app/models/user.rb index 771bc1a4..6aaa1c08 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require_relative '../services/evaluator_management_service' # == Schema Information @@ -36,7 +37,6 @@ class User < ApplicationRecord after_create :process_evaluator_invitations - VALID_EVALUATOR_ROLES = %w[evaluator solver challenge_manager].freeze belongs_to :agency, optional: true @@ -136,7 +136,7 @@ def self.create_user_from_userinfo(userinfo) end def self.default_role_and_status_for_email(email) - if EvaluatorInvitation.exists?(email: email) + if EvaluatorInvitation.exists?(email:) %w[evaluator pending] elsif default_challenge_manager?(email) %w[challenge_manager pending] diff --git a/app/services/evaluator_management_service.rb b/app/services/evaluator_management_service.rb index 1c36711e..a026b781 100644 --- a/app/services/evaluator_management_service.rb +++ b/app/services/evaluator_management_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EvaluatorManagementService def initialize(challenge, phase) @challenge = challenge @@ -5,7 +7,7 @@ def initialize(challenge, phase) end def process_evaluator_invitation(email, invitation_params) - user = User.find_by(email: email) + user = User.find_by(email:) user ? add_existing_user_as_evaluator(user) : handle_invitation(email, invitation_params) end @@ -23,7 +25,7 @@ def remove_evaluator(evaluator_type, evaluator_id) def self.accept_evaluator_invitation(user) invitations = EvaluatorInvitation.where(email: user.email) invitations.each do |invite| - ChallengePhasesEvaluator.create(challenge: invite.challenge, phase: invite.phase, user: user) + ChallengePhasesEvaluator.create(challenge: invite.challenge, phase: invite.phase, user:) invite.destroy end { success: true, message: I18n.t('manage_evaluators.accept_evaluator_invitation.success') } @@ -32,29 +34,64 @@ def self.accept_evaluator_invitation(user) private def add_existing_user_as_evaluator(user) - return { success: true, message: I18n.t('manage_evaluators.process_evaluator_invitation.already_added', email: user.email) } if @phase.evaluators.include?(user) - return { success: false, message: I18n.t('manage_evaluators.process_evaluator_invitation.invalid_role', email: user.email) } unless User::VALID_EVALUATOR_ROLES.include?(user.role) + if @phase.evaluators.include?(user) + return { + success: true, + message: I18n.t('manage_evaluators.process_evaluator_invitation.already_added', + email: user.email) + } + end + + unless User::VALID_EVALUATOR_ROLES.include?(user.role) + return { + success: false, + message: I18n.t('manage_evaluators.process_evaluator_invitation.invalid_role', + email: user.email) + } + end - cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user: user) + cpe = ChallengePhasesEvaluator.find_or_create_by(challenge: @challenge, phase: @phase, user:) if cpe.persisted? - { success: true, message: I18n.t('manage_evaluators.process_evaluator_invitation.add_success', email: user.email) } + { + success: true, + message: I18n.t('manage_evaluators.process_evaluator_invitation.add_success', + email: user.email) + } else - { success: false, message: I18n.t('manage_evaluators.process_evaluator_invitation.add_failure', email: user.email) } + { + success: false, + message: I18n.t('manage_evaluators.process_evaluator_invitation.add_failure', + email: user.email) + } end end def handle_invitation(email, invitation_params) - existing_invitation = @challenge.evaluator_invitations.find_by(email: email, phase: @phase) + existing_invitation = @challenge.evaluator_invitations.find_by(email:, phase: @phase) existing_invitation ? resend_invitation(existing_invitation) : create_new_invitation(invitation_params) end def create_new_invitation(invitation_params) - invitation = @challenge.evaluator_invitations.new(invitation_params.merge(phase: @phase, last_invite_sent: Time.current)) + invitation = @challenge.evaluator_invitations.new( + invitation_params.merge( + phase: @phase, + last_invite_sent: Time.current + ) + ) if invitation.save - { success: true, message: I18n.t('manage_evaluators.process_evaluator_invitation.invitation_sent', email: invitation_params[:email]) } + { + success: true, + message: I18n.t( + 'manage_evaluators.process_evaluator_invitation.invitation_sent', + email: invitation_params[:email] + ) + } else - { success: false, message: invitation.errors.full_messages.join(", ") } + { + success: false, + message: invitation.errors.full_messages.join(", ") + } end end From 32e32056a85090eb0df02ff7e29c8320d9ba2259 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Wed, 13 Nov 2024 15:56:13 -0800 Subject: [PATCH 36/48] scope the Challenge query to the current_user --- app/controllers/manage_evaluators_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/manage_evaluators_controller.rb index 2df778cd..5388bf8b 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/manage_evaluators_controller.rb @@ -28,7 +28,7 @@ def destroy private def set_challenge - @challenge = Challenge.find(params[:challenge_id]) + @challenge = current_user.challenge_manager_challenges.find(params[:challenge_id]) end def set_phase From a56ba861980adeb8711826c7ab5ff2a622b14ba5 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Thu, 14 Nov 2024 17:24:18 -0800 Subject: [PATCH 37/48] review feedback --- app/views/manage_evaluators/index.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/manage_evaluators/index.html.erb b/app/views/manage_evaluators/index.html.erb index 87c77248..4127f5ea 100644 --- a/app/views/manage_evaluators/index.html.erb +++ b/app/views/manage_evaluators/index.html.erb @@ -19,14 +19,14 @@ <%= form.label :first_name, class: "usa-label font-sans-md" do %> First Name* <% end %> -
    Add first and last name in that order.
    +
    Add evaluator's first name.
    <%= form.text_field :first_name, class: "usa-input", required: true, autocomplete: "given-name", style: "border: 1.5px solid #565c65;" %>
    <%= form.label :last_name, class: "usa-label font-sans-md" do %> Last Name* <% end %> -
    Add first and last name in that order.
    +
    Add evaluator's last name.
    <%= form.text_field :last_name, class: "usa-input", required: true, autocomplete: "family-name", style: "border: 1.5px solid #565c65;" %>
    From f15ffad72c9927d0dbfde8e4f4be9da93d4e7adb Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Thu, 14 Nov 2024 17:33:39 -0800 Subject: [PATCH 38/48] update javascript url --- .../controllers/delete_evaluator_modal_controller.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/javascript/controllers/delete_evaluator_modal_controller.js b/app/javascript/controllers/delete_evaluator_modal_controller.js index a6c0714f..1e05095a 100644 --- a/app/javascript/controllers/delete_evaluator_modal_controller.js +++ b/app/javascript/controllers/delete_evaluator_modal_controller.js @@ -21,7 +21,6 @@ export default class extends Controller { event.preventDefault() this.evaluatorIdValue = event.currentTarget.dataset.evaluatorId this.evaluatorTypeValue = event.currentTarget.dataset.evaluatorType - this.challengeIdValue = event.currentTarget.dataset.challengeId this.phaseIdValue = event.currentTarget.dataset.phaseId this.modalTarget.showModal() } @@ -44,7 +43,7 @@ export default class extends Controller { deleteEvaluator(forceDelete = false) { const csrfToken = document.querySelector('meta[name="csrf-token"]').content - fetch(`/challenges/${this.challengeIdValue}/manage_evaluators/${this.evaluatorIdValue}`, { + fetch(`/phases/${this.phaseIdValue}/manage_evaluators/${this.evaluatorIdValue}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', From 86c9b6f18fdccf785ed5336d31a7e8b8f9ccd82a Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Thu, 14 Nov 2024 17:40:43 -0800 Subject: [PATCH 39/48] fix n+1 queries --- app/controllers/phases_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/phases_controller.rb b/app/controllers/phases_controller.rb index f6acde74..537ffc0c 100644 --- a/app/controllers/phases_controller.rb +++ b/app/controllers/phases_controller.rb @@ -5,7 +5,7 @@ class PhasesController < ApplicationController before_action :set_phase, except: [:index] def index - @challenges = current_user.challenge_manager_challenges + @challenges = current_user.challenge_manager_challenges.includes([phases: [:evaluation_form, :submissions]]) end def submissions From 4fe4a1b57209c95abe4879a95c6398b9461200f7 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Thu, 14 Nov 2024 17:47:52 -0800 Subject: [PATCH 40/48] codeclimate shrink JS function --- .../delete_evaluator_modal_controller.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/javascript/controllers/delete_evaluator_modal_controller.js b/app/javascript/controllers/delete_evaluator_modal_controller.js index 1e05095a..eac5fee0 100644 --- a/app/javascript/controllers/delete_evaluator_modal_controller.js +++ b/app/javascript/controllers/delete_evaluator_modal_controller.js @@ -45,23 +45,14 @@ export default class extends Controller { fetch(`/phases/${this.phaseIdValue}/manage_evaluators/${this.evaluatorIdValue}`, { method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': csrfToken - }, - body: JSON.stringify({ - evaluator_type: this.evaluatorTypeValue, - phase_id: this.phaseIdValue, - force_delete: forceDelete - }) + headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, + body: JSON.stringify({ evaluator_type: this.evaluatorTypeValue, phase_id: this.phaseIdValue, force_delete: forceDelete }) }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok') } - return response.json() - }) - .then(data => { + const data = response.json() if (data.success) { this.close() window.location.reload() From 13a08bc382bb6a7516e03c45338ecbbe4d7d32f1 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Mon, 18 Nov 2024 17:01:38 -0800 Subject: [PATCH 41/48] update the invitation routes --- .../evaluator_invitations_controller.rb | 28 --- ...controller.rb => evaluators_controller.rb} | 58 +++--- ...luators_helper.rb => evaluators_helper.rb} | 2 +- .../delete_evaluator_modal_controller.js | 17 +- app/services/evaluator_management_service.rb | 42 ++--- .../_delete_evaluator_modal.html.erb | 10 -- .../index.html.erb | 4 +- app/views/phases/_phases_table.html.erb | 2 +- config/locales/en.yml | 5 +- config/routes.rb | 5 +- ...lper_spec.rb => evaluators_helper_spec.rb} | 10 +- spec/requests/evaluator_invitations_spec.rb | 24 --- ..._evaluators_spec.rb => evaluators_spec.rb} | 170 +++++++++++------- .../evaluator_management_service_spec.rb | 15 +- 14 files changed, 191 insertions(+), 201 deletions(-) delete mode 100644 app/controllers/evaluator_invitations_controller.rb rename app/controllers/{manage_evaluators_controller.rb => evaluators_controller.rb} (64%) rename app/helpers/{manage_evaluators_helper.rb => evaluators_helper.rb} (94%) rename app/views/{manage_evaluators => evaluators}/_delete_evaluator_modal.html.erb (81%) rename app/views/{manage_evaluators => evaluators}/index.html.erb (94%) rename spec/helpers/{manage_evaluators_helper_spec.rb => evaluators_helper_spec.rb} (79%) delete mode 100644 spec/requests/evaluator_invitations_spec.rb rename spec/requests/{manage_evaluators_spec.rb => evaluators_spec.rb} (53%) diff --git a/app/controllers/evaluator_invitations_controller.rb b/app/controllers/evaluator_invitations_controller.rb deleted file mode 100644 index 3354e40b..00000000 --- a/app/controllers/evaluator_invitations_controller.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -class EvaluatorInvitationsController < ApplicationController - before_action -> { authorize_user('challenge_manager') } - before_action :set_phase - before_action :set_evaluator_invitation - - def resend - if @evaluator_invitation.update(last_invite_sent: Time.current) - # TODO: Implement sending the actual invitation email here - redirect_to phase_manage_evaluators_path(@phase), - notice: t('.success') - else - redirect_to phase_manage_evaluators_path(@phase), - alert: t('.failure') - end - end - - private - - def set_phase - @phase = Phase.where(challenge: current_user.challenge_manager_challenges).find(params[:phase_id]) - end - - def set_evaluator_invitation - @evaluator_invitation = @phase.evaluator_invitations.find(params[:id]) - end -end diff --git a/app/controllers/manage_evaluators_controller.rb b/app/controllers/evaluators_controller.rb similarity index 64% rename from app/controllers/manage_evaluators_controller.rb rename to app/controllers/evaluators_controller.rb index 5962b688..e15e4475 100644 --- a/app/controllers/manage_evaluators_controller.rb +++ b/app/controllers/evaluators_controller.rb @@ -1,13 +1,14 @@ # frozen_string_literal: true -class ManageEvaluatorsController < ApplicationController - include ManageEvaluatorsHelper - +class EvaluatorsController < ApplicationController before_action -> { authorize_user('challenge_manager') } - before_action :set_phase, only: [:index, :create, :destroy] + + # All routes are scoped to a Challenge Phase + before_action :set_challenge_phase def index - handle_existing_phases + @evaluator_invitations = @phase.evaluator_invitations + @existing_evaluators = @phase.evaluators end def create @@ -21,14 +22,30 @@ def create def destroy result = evaluator_service.remove_evaluator(params[:evaluator_type], params[:id]) - render_json_response(result) + + if result[:success] + flash[:notice] = result[:message] + render json: { success: true, message: result[:message] } + else + render json: { success: false, message: result[:message] }, status: :unprocessable_entity + end + end + + def resend_invite + @evaluator_invitation = @phase.evaluator_invitations.find(params[:id]) + if evaluator_service.resend_invitation(@evaluator_invitation) + redirect_to phase_evaluators_path(@phase), + notice: t('.success') + else + redirect_to phase_evaluators_path(@phase), + alert: t('.failure') + end end private - def set_phase - phase_id = params.dig(:evaluator_invitation, :phase_id) || params[:phase_id] - @phase = Phase.where(challenge: current_user.challenge_manager_challenges).find(phase_id) + def set_challenge_phase + @phase = Phase.where(challenge: current_user.challenge_manager_challenges).find(params[:phase_id]) @challenge = @phase.challenge end @@ -42,31 +59,14 @@ def evaluator_invitation_params ) end - def handle_existing_phases - fetch_evaluators_and_invitations - end - - def fetch_evaluators_and_invitations - @evaluator_invitations = @phase.evaluator_invitations - @existing_evaluators = @phase.evaluators - end - def handle_invitation_result(result) if result[:success] - redirect_to phase_manage_evaluators_path(@phase), notice: result[:message] + redirect_to phase_evaluators_path(@phase), notice: result[:message] else flash.now[:alert] = result[:message] - handle_existing_phases + @evaluator_invitations = @phase.evaluator_invitations + @existing_evaluators = @phase.evaluators render :index end end - - def render_json_response(result) - if result[:success] - flash[:notice] = result[:message] - render json: { success: true, message: result[:message] } - else - render json: { success: false, message: result[:message] }, status: :unprocessable_entity - end - end end diff --git a/app/helpers/manage_evaluators_helper.rb b/app/helpers/evaluators_helper.rb similarity index 94% rename from app/helpers/manage_evaluators_helper.rb rename to app/helpers/evaluators_helper.rb index a8058708..f39199e9 100644 --- a/app/helpers/manage_evaluators_helper.rb +++ b/app/helpers/evaluators_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module ManageEvaluatorsHelper +module EvaluatorsHelper def user_status(evaluator) if evaluator.is_a?(User) evaluator.status == 'active' ? "Available" : "Awaiting Approval" diff --git a/app/javascript/controllers/delete_evaluator_modal_controller.js b/app/javascript/controllers/delete_evaluator_modal_controller.js index eac5fee0..0f4f7431 100644 --- a/app/javascript/controllers/delete_evaluator_modal_controller.js +++ b/app/javascript/controllers/delete_evaluator_modal_controller.js @@ -43,16 +43,16 @@ export default class extends Controller { deleteEvaluator(forceDelete = false) { const csrfToken = document.querySelector('meta[name="csrf-token"]').content - fetch(`/phases/${this.phaseIdValue}/manage_evaluators/${this.evaluatorIdValue}`, { + fetch(`/phases/${this.phaseIdValue}/evaluators/${this.evaluatorIdValue}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ evaluator_type: this.evaluatorTypeValue, phase_id: this.phaseIdValue, force_delete: forceDelete }) }) .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok') - } - const data = response.json() + if (!response.ok) { throw new Error('Network response was not ok') } + return response.json() + }) + .then(data => { if (data.success) { this.close() window.location.reload() @@ -60,13 +60,12 @@ export default class extends Controller { throw new Error(data.message || 'Failed to remove evaluator') } }) - .catch(error => { - alert(error.message || 'An error occurred while removing the evaluator') - }) + .catch(error => { alert(error.message || 'An error occurred while removing the evaluator') }) } resetModal() { - this.modalDescriptionTarget.textContent = 'Deleting an evaluator from the challenge will remove the evaluator from any submissions of this challenge that the evaluator is assigned to. It will also delete any of their completed or in progress evaluations for those submissions.' + this.modalDescriptionTarget.textContent = 'Deleting an evaluator from the challenge will remove the evaluator from any submissions of this ' + + 'challenge that the evaluator is assigned to. It will also delete any of their completed or in progress evaluations for those submissions.' this.confirmButtonTarget.textContent = 'Yes' } } diff --git a/app/services/evaluator_management_service.rb b/app/services/evaluator_management_service.rb index a026b781..f590202d 100644 --- a/app/services/evaluator_management_service.rb +++ b/app/services/evaluator_management_service.rb @@ -28,7 +28,17 @@ def self.accept_evaluator_invitation(user) ChallengePhasesEvaluator.create(challenge: invite.challenge, phase: invite.phase, user:) invite.destroy end - { success: true, message: I18n.t('manage_evaluators.accept_evaluator_invitation.success') } + { success: true, message: I18n.t('evaluators.accept_evaluator_invitation.success') } + end + + # TODO: Implement sending the actual invitation email here + def resend_invitation(invitation) + if invitation.update(last_invite_sent: Time.current) + { success: true, + message: I18n.t('evaluators.process_evaluator_invitation.invitation_resent', email: invitation.email) } + else + { success: false } + end end private @@ -37,7 +47,7 @@ def add_existing_user_as_evaluator(user) if @phase.evaluators.include?(user) return { success: true, - message: I18n.t('manage_evaluators.process_evaluator_invitation.already_added', + message: I18n.t('evaluators.process_evaluator_invitation.already_added', email: user.email) } end @@ -45,7 +55,7 @@ def add_existing_user_as_evaluator(user) unless User::VALID_EVALUATOR_ROLES.include?(user.role) return { success: false, - message: I18n.t('manage_evaluators.process_evaluator_invitation.invalid_role', + message: I18n.t('evaluators.process_evaluator_invitation.invalid_role', email: user.email) } end @@ -55,13 +65,13 @@ def add_existing_user_as_evaluator(user) if cpe.persisted? { success: true, - message: I18n.t('manage_evaluators.process_evaluator_invitation.add_success', + message: I18n.t('evaluators.process_evaluator_invitation.add_success', email: user.email) } else { success: false, - message: I18n.t('manage_evaluators.process_evaluator_invitation.add_failure', + message: I18n.t('evaluators.process_evaluator_invitation.add_failure', email: user.email) } end @@ -83,7 +93,7 @@ def create_new_invitation(invitation_params) { success: true, message: I18n.t( - 'manage_evaluators.process_evaluator_invitation.invitation_sent', + 'evaluators.process_evaluator_invitation.invitation_sent', email: invitation_params[:email] ) } @@ -95,24 +105,16 @@ def create_new_invitation(invitation_params) end end - def resend_invitation(invitation) - invitation.update(last_invite_sent: Time.current) - { - success: true, - message: I18n.t('manage_evaluators.process_evaluator_invitation.invitation_resent', email: invitation.email) - } - end - def remove_user_evaluator(evaluator_id) evaluator = User.find(evaluator_id) cpe = ChallengePhasesEvaluator.find_by(challenge: @challenge, phase: @phase, user: evaluator) if cpe.destroy - { success: true, message: I18n.t('manage_evaluators.remove_user_evaluator.success') } + { success: true, message: I18n.t('evaluators.remove_user_evaluator.success') } else - { success: false, message: I18n.t('manage_evaluators.remove_user_evaluator.failure') } + { success: false, message: I18n.t('evaluators.remove_user_evaluator.failure') } end rescue ActiveRecord::RecordNotFound - { success: false, message: I18n.t('manage_evaluators.remove_user_evaluator.evaluator_not_found') } + { success: false, message: I18n.t('evaluators.remove_user_evaluator.evaluator_not_found') } rescue StandardError => e { success: false, message: "Error: #{e.message}" } end @@ -120,12 +122,12 @@ def remove_user_evaluator(evaluator_id) def remove_evaluator_invitation(invitation_id) invitation = @challenge.evaluator_invitations.find_by!(id: invitation_id, phase: @phase) if invitation.destroy - { success: true, message: I18n.t('manage_evaluators.remove_evaluator_invitation.success') } + { success: true, message: I18n.t('evaluators.remove_evaluator_invitation.success') } else - { success: false, message: I18n.t('manage_evaluators.remove_evaluator_invitation.failure') } + { success: false, message: I18n.t('evaluators.remove_evaluator_invitation.failure') } end rescue ActiveRecord::RecordNotFound - { success: false, message: I18n.t('manage_evaluators.remove_evaluator_invitation.invitation_not_found') } + { success: false, message: I18n.t('evaluators.remove_evaluator_invitation.invitation_not_found') } rescue StandardError => e { success: false, message: "Error: #{e.message}" } end diff --git a/app/views/manage_evaluators/_delete_evaluator_modal.html.erb b/app/views/evaluators/_delete_evaluator_modal.html.erb similarity index 81% rename from app/views/manage_evaluators/_delete_evaluator_modal.html.erb rename to app/views/evaluators/_delete_evaluator_modal.html.erb index 7df629b7..bdaeccd6 100644 --- a/app/views/manage_evaluators/_delete_evaluator_modal.html.erb +++ b/app/views/evaluators/_delete_evaluator_modal.html.erb @@ -34,15 +34,5 @@
    - diff --git a/app/views/manage_evaluators/index.html.erb b/app/views/evaluators/index.html.erb similarity index 94% rename from app/views/manage_evaluators/index.html.erb rename to app/views/evaluators/index.html.erb index 4127f5ea..7e5a6ab0 100644 --- a/app/views/manage_evaluators/index.html.erb +++ b/app/views/evaluators/index.html.erb @@ -11,7 +11,7 @@

    Create and manage a list of evaluators for the challenge.

    Add Evaluators

    Evaluators will not be assigned to submissions until you assign in the submission detail view.

    - <%= form_with(model: EvaluatorInvitation.new, url: phase_manage_evaluators_path(@phase), method: :post, local: true) do |form| %> + <%= form_with(model: EvaluatorInvitation.new, url: phase_evaluators_path(@phase), method: :post, local: true) do |form| %> <%= form.hidden_field :challenge_id, value: @challenge.id %> <%= form.hidden_field :phase_id, value: @phase.id %> <%= form.hidden_field :last_invite_sent, value: Time.current %> @@ -99,7 +99,7 @@
    Challenge
    Invite Sent - <%= button_to resend_phase_evaluator_invitation_path(@phase, invitation), method: :post, class: 'usa-button usa-button--unstyled margin-left-2', style: 'white-space: nowrap;', form: { class: 'display-inline' } do %> + <%= button_to resend_invite_phase_evaluator_path(@phase, invitation), method: :post, class: 'usa-button usa-button--unstyled margin-left-2', style: 'white-space: nowrap;', form: { class: 'display-inline' } do %> Resend <% end %>
    diff --git a/app/views/phases/_phases_table.html.erb b/app/views/phases/_phases_table.html.erb index 3e31e29d..63a232df 100644 --- a/app/views/phases/_phases_table.html.erb +++ b/app/views/phases/_phases_table.html.erb @@ -37,7 +37,7 @@
    - <%= link_to phase_manage_evaluators_path(phase), class: "usa-button font-body-2xs text-no-wrap width-full margin-bottom-105" do %> + <%= link_to phase_evaluators_path(phase), class: "usa-button font-body-2xs text-no-wrap width-full margin-bottom-105" do %> Manage Evaluators <% end %> <% unless phase.submissions.empty? %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 00f864c9..094678db 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -37,7 +37,7 @@ en: please_try_again: "Please try again." session_expired_alert: "Your session has expired. Please log in again." evaluation_criterion_unique_title_in_form: "must be unique within the same form." - manage_evaluators: + evaluators: index: no_phases_alert: "This challenge has no phases. Please add at least one phase before managing evaluators." remove_user_evaluator: @@ -57,8 +57,7 @@ en: invitation_resent: "An invitation to this challenge has already been sent to %{email}. Invitation has been resent." accept_evaluator_invitation: success: "Evaluator created and added to challenge phase successfully." - evaluator_invitations: - resend: + resend_invite: success: "Invitation resent successfully." failure: "Failed to resend invitation." evaluation_criterion_unique_title_in_form_error: "Evaluation criteria title must be unique within the same form." diff --git a/config/routes.rb b/config/routes.rb index 8111cef7..6192eeca 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -22,10 +22,9 @@ member do get :submissions end - resources :manage_evaluators, only: [:index, :create, :destroy] - resources :evaluator_invitations, only: [] do + resources :evaluators, only: [:index, :create, :destroy] do member do - post 'resend' + post 'resend_invite' end end end diff --git a/spec/helpers/manage_evaluators_helper_spec.rb b/spec/helpers/evaluators_helper_spec.rb similarity index 79% rename from spec/helpers/manage_evaluators_helper_spec.rb rename to spec/helpers/evaluators_helper_spec.rb index 3ed2c91c..cd95919b 100644 --- a/spec/helpers/manage_evaluators_helper_spec.rb +++ b/spec/helpers/evaluators_helper_spec.rb @@ -1,8 +1,8 @@ -# spec/helpers/manage_evaluators_helper_spec.rb +# spec/helpers/evaluators_helper_spec.rb require 'rails_helper' -RSpec.describe ManageEvaluatorsHelper, type: :helper do +RSpec.describe EvaluatorsHelper, type: :helper do describe '#assigned_submissions_count' do let(:challenge) { create(:challenge) } let(:phase) { create(:phase, challenge: challenge) } @@ -12,7 +12,8 @@ it 'returns the correct count of assigned submissions' do create(:evaluator_submission_assignment, evaluator: evaluator, submission: submission) create(:evaluator_submission_assignment, evaluator: evaluator, submission: submission) - create(:evaluator_submission_assignment, evaluator: evaluator, submission: create(:submission, challenge: challenge, phase: phase)) + create(:evaluator_submission_assignment, evaluator: evaluator, + submission: create(:submission, challenge: challenge, phase: phase)) expect(helper.assigned_submissions_count(evaluator, challenge, phase)).to eq(3) end @@ -27,7 +28,8 @@ it 'only counts submissions for the specified challenge and phase' do create(:evaluator_submission_assignment, evaluator: evaluator, submission: submission) - create(:evaluator_submission_assignment, evaluator: evaluator, submission: create(:submission, challenge: create(:challenge), phase: create(:phase))) + create(:evaluator_submission_assignment, evaluator: evaluator, + submission: create(:submission, challenge: create(:challenge), phase: create(:phase))) expect(helper.assigned_submissions_count(evaluator, challenge, phase)).to eq(1) end diff --git a/spec/requests/evaluator_invitations_spec.rb b/spec/requests/evaluator_invitations_spec.rb deleted file mode 100644 index 3031a6eb..00000000 --- a/spec/requests/evaluator_invitations_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'rails_helper' - -RSpec.describe "EvaluatorInvitations", type: :request do - let(:user) { create_and_log_in_user(role: 'challenge_manager') } - let(:challenge) { create(:challenge) } - let(:phase) { create(:phase, challenge: challenge) } - let(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase) } - - before do - ChallengeManager.create(user: user, challenge: challenge) - log_in_user(user) - end - - describe 'POST #resend' do - it 'updates the last_invite_sent timestamp and redirects with a success message' do - expect do - post resend_phase_evaluator_invitation_path(phase, invitation) - end.to change { invitation.reload.last_invite_sent } - - expect(response).to redirect_to(phase_manage_evaluators_path(phase)) - expect(flash[:notice]).to eq('Invitation resent successfully.') - end - end -end diff --git a/spec/requests/manage_evaluators_spec.rb b/spec/requests/evaluators_spec.rb similarity index 53% rename from spec/requests/manage_evaluators_spec.rb rename to spec/requests/evaluators_spec.rb index fba4741c..73367f56 100644 --- a/spec/requests/manage_evaluators_spec.rb +++ b/spec/requests/evaluators_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' -RSpec.describe "ManageEvaluators", type: :request do - let(:user) { create_and_log_in_user(role: 'challenge_manager') } +RSpec.describe "Evaluators", type: :request do + let(:challenge_manager) { create_and_log_in_user(role: 'challenge_manager') } let(:challenge) { create(:challenge) } let(:phase) { create(:phase, challenge: challenge) } let(:evaluator) { create(:user, role: 'evaluator') } @@ -9,29 +9,33 @@ create(:evaluator_invitation, challenge: challenge, phase: phase, email: 'invitation@example.com') end - let(:evaluator_service) { instance_double(EvaluatorManagementService) } - - before do - allow(EvaluatorManagementService).to receive(:new).and_return(evaluator_service) - ChallengeManager.create(user: user, challenge: challenge) - log_in_user(user) - end + let(:evaluator_service_double) { instance_double(EvaluatorManagementService) } + let(:evaluator_service) { EvaluatorManagementService.new(challenge, phase) } describe 'GET #index' do before do - invitation # create existing invitation - challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase) + mock_evaluator_service + login_challenge_manager end - it 'assigns @evaluator_invitations and @existing_evaluators' do - get phase_manage_evaluators_path(phase) + it 'renders invitations and evaluators' do + invitation # create existing invitation + associate_challenge_manager_challenge + associate_evaluator_challenge_phase + get phase_evaluators_path(phase) - expect(assigns(:evaluator_invitations)).to eq([invitation]) - expect(assigns(:existing_evaluators)).to eq([evaluator]) + expect(response.body).to include(invitation.email) + expect(response.body).to include(evaluator.email) end end describe 'POST #create' do + before do + mock_evaluator_service + login_challenge_manager + associate_challenge_manager_challenge + end + context 'with valid params' do let(:valid_params) do { @@ -41,17 +45,17 @@ end it 'calls the EvaluatorManagementService to process the invitation' do - expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, - message: 'Invitation sent successfully.' }) - post phase_manage_evaluators_path(phase), params: valid_params - expect(response).to redirect_to(phase_manage_evaluators_path(phase)) + expect(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: true, + message: 'Invitation sent successfully.' }) + post phase_evaluators_path(phase), params: valid_params + expect(response).to redirect_to(phase_evaluators_path(phase)) end - it 'redirects to manage_evaluators path with success notice' do - allow(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, - message: 'Invitation sent successfully.' }) - post phase_manage_evaluators_path(phase), params: valid_params - expect(response).to redirect_to(phase_manage_evaluators_path(phase)) + it 'redirects to evaluators path with success notice' do + allow(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: true, + message: 'Invitation sent successfully.' }) + post phase_evaluators_path(phase), params: valid_params + expect(response).to redirect_to(phase_evaluators_path(phase)) expect(flash[:notice]).to eq('Invitation sent successfully.') end end @@ -64,10 +68,10 @@ end it 'does not create a new evaluator invitation' do - allow(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: false, - message: 'Invalid email' }) + allow(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: false, + message: 'Invalid email' }) expect do - post phase_manage_evaluators_path(phase), params: invalid_params + post phase_evaluators_path(phase), params: invalid_params end.not_to change(EvaluatorInvitation, :count) expect(response).to render_template(:index) end @@ -77,19 +81,19 @@ let(:existing_user) { create(:user, role: 'evaluator') } it 'adds the user as an evaluator without creating a new invitation' do - expect(evaluator_service).to receive(:process_evaluator_invitation).with( + expect(evaluator_service_double).to receive(:process_evaluator_invitation).with( existing_user.email, hash_including(phase_id: phase.id.to_s) ).and_return({ success: true, message: 'User added as an evaluator.' }) - post phase_manage_evaluators_path(phase), params: { + post phase_evaluators_path(phase), params: { evaluator_invitation: { email: existing_user.email, phase_id: phase.id } } - expect(response).to redirect_to(phase_manage_evaluators_path(phase)) + expect(response).to redirect_to(phase_evaluators_path(phase)) expect(flash[:notice]).to eq('User added as an evaluator.') end end @@ -102,10 +106,10 @@ end it 'does not create an additional ChallengePhaseEvaluator' do - expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: false, - message: 'User is already an evaluator for this phase.' }) + expect(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: false, + message: 'User is already an evaluator for this phase.' }) expect do - post phase_manage_evaluators_path(phase), params: { + post phase_evaluators_path(phase), params: { evaluator_invitation: { email: existing_user.email, phase_id: phase.id @@ -118,10 +122,10 @@ context 'when inviting a user who already has an invitation for the challenge phase' do it 'does not create an additional EvaluatorInvitation' do invitation # create existing invitation - expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, - message: 'Invitation resent.' }) + expect(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: true, + message: 'Invitation resent.' }) expect do - post phase_manage_evaluators_path(phase), params: { + post phase_evaluators_path(phase), params: { evaluator_invitation: { email: invitation.email, phase_id: phase.id @@ -135,17 +139,17 @@ let(:existing_user) { create(:user, role: 'solver', status: 'pending') } it 'adds the user as an evaluator without changing their role' do - expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, - message: 'User added as an evaluator.' }) + expect(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: true, + message: 'User added as an evaluator.' }) - post phase_manage_evaluators_path(phase), params: { + post phase_evaluators_path(phase), params: { evaluator_invitation: { email: existing_user.email, phase_id: phase.id } } - expect(response).to redirect_to(phase_manage_evaluators_path(phase)) + expect(response).to redirect_to(phase_evaluators_path(phase)) expect(flash[:notice]).to eq('User added as an evaluator.') existing_user.reload expect(existing_user.role).to eq('solver') @@ -156,17 +160,17 @@ let(:new_user_email) { 'new_user@example.com' } it 'creates an evaluator invitation' do - expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: true, - message: 'Invitation sent successfully.' }) + expect(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: true, + message: 'Invitation sent successfully.' }) - post phase_manage_evaluators_path(phase), params: { + post phase_evaluators_path(phase), params: { evaluator_invitation: { email: new_user_email, phase_id: phase.id } } - expect(response).to redirect_to(phase_manage_evaluators_path(phase)) + expect(response).to redirect_to(phase_evaluators_path(phase)) expect(flash[:notice]).to eq('Invitation sent successfully.') end end @@ -175,10 +179,10 @@ let(:existing_user) { create(:user, role: 'admin') } it 'does not add the user as an evaluator and returns an error' do - expect(evaluator_service).to receive(:process_evaluator_invitation).and_return({ success: false, - message: 'User does not have a valid evaluator role.' }) + expect(evaluator_service_double).to receive(:process_evaluator_invitation).and_return({ success: false, + message: 'User does not have a valid evaluator role.' }) - post phase_manage_evaluators_path(phase), params: { + post phase_evaluators_path(phase), params: { evaluator_invitation: { email: existing_user.email, phase_id: phase.id @@ -194,21 +198,23 @@ describe 'DELETE #destroy' do let(:phase) { create(:phase, challenge: challenge) } let!(:invitation) { create(:evaluator_invitation, challenge:, phase:) } - let(:evaluator_service) { instance_double(EvaluatorManagementService) } + let(:evaluator_service_double) { instance_double(EvaluatorManagementService) } before do - allow(EvaluatorManagementService).to receive(:new).and_return(evaluator_service) + mock_evaluator_service + login_challenge_manager + associate_challenge_manager_challenge end context 'when removing a user evaluator from a specific phase' do let(:evaluator) { create(:user, role: 'evaluator') } it 'removes the evaluator from their associated phase' do - expect(evaluator_service).to receive(:remove_evaluator).with('user', - evaluator.id.to_s).and_return({ success: true, - message: 'Evaluator removed successfully.' }) + expect(evaluator_service_double).to receive(:remove_evaluator).with('user', + evaluator.id.to_s).and_return({ success: true, + message: 'Evaluator removed successfully.' }) - delete phase_manage_evaluator_path(phase, evaluator), + delete phase_evaluator_path(phase, evaluator), params: { evaluator_type: 'user', phase_id: phase.id } expect(response).to have_http_status(:success) @@ -218,11 +224,11 @@ context 'when removing an evaluator invitation' do it 'removes the evaluator invitation' do - expect(evaluator_service).to receive(:remove_evaluator).with('invitation', - invitation.id.to_s).and_return({ success: true, - message: 'Invitation removed successfully.' }) + expect(evaluator_service_double).to receive(:remove_evaluator).with('invitation', + invitation.id.to_s).and_return({ success: true, + message: 'Invitation removed successfully.' }) - delete phase_manage_evaluator_path(phase, invitation), + delete phase_evaluator_path(phase, invitation), params: { evaluator_type: 'invitation', phase_id: phase.id } expect(response).to have_http_status(:success) @@ -232,15 +238,51 @@ context 'with invalid evaluator type' do it 'returns an error JSON response' do - expect(evaluator_service).to receive(:remove_evaluator).and_return({ success: false, - message: 'Invalid evaluator type' }) - delete phase_manage_evaluator_path(phase, 1), params: { evaluator_type: 'invalid', phase_id: phase.id } + allow(evaluator_service_double).to receive(:remove_evaluator).and_return({ success: false, + message: 'Invalid evaluator type' }) + delete phase_evaluator_path(phase, 1), params: { evaluator_type: 'invalid', phase_id: phase.id } expect(response).to have_http_status(:unprocessable_entity) - expect(JSON.parse(response.body)).to eq({ - 'success' => false, - 'message' => 'Invalid evaluator type' - }) + expect(response.parsed_body).to eq({ + 'success' => false, + 'message' => 'Invalid evaluator type' + }) end end end + + describe 'POST #resend_invite' do + before do + login_challenge_manager + associate_challenge_manager_challenge + end + + it 'updates the last_invite_sent timestamp and redirects with a success message' do + expect do + post resend_invite_phase_evaluator_path(phase, invitation) + end.to change { invitation.reload.last_invite_sent } + + expect(response).to redirect_to(phase_evaluators_path(phase)) + expect(flash[:notice]).to eq('Invitation resent successfully.') + end + end + + def login_user + log_in_user(user) + end + + def login_challenge_manager + log_in_user(challenge_manager) + end + + def mock_evaluator_service + allow(EvaluatorManagementService).to receive(:new).and_return(evaluator_service_double) + end + + def associate_challenge_manager_challenge + ChallengeManager.create(user: challenge_manager, challenge: challenge) + end + + def associate_evaluator_challenge_phase + challenge.challenge_phases_evaluators.create(user: evaluator, phase: phase) + end end diff --git a/spec/services/evaluator_management_service_spec.rb b/spec/services/evaluator_management_service_spec.rb index ff052c75..7abeb4bd 100644 --- a/spec/services/evaluator_management_service_spec.rb +++ b/spec/services/evaluator_management_service_spec.rb @@ -96,10 +96,10 @@ let!(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase, email: evaluator.email) } it 'processes all invitations for the user' do - expect { + expect do EvaluatorManagementService.accept_evaluator_invitation(evaluator) - }.to change(ChallengePhasesEvaluator, :count).by(1) - .and change(EvaluatorInvitation, :count).by(-1) + end.to change(ChallengePhasesEvaluator, :count).by(1). + and change(EvaluatorInvitation, :count).by(-1) end it 'returns a success message' do @@ -107,4 +107,13 @@ expect(result).to eq({ success: true, message: 'Evaluator created and added to challenge phase successfully.' }) end end + + describe '.resend_invitation' do + let(:evaluator) { create(:user, role: 'evaluator') } + let(:invitation) { create(:evaluator_invitation, challenge: challenge, phase: phase, email: evaluator.email) } + + it 'updates the invitation last_invite_sent' do + expect { service.resend_invitation(invitation) }.to change { invitation.reload.last_invite_sent } + end + end end From 73af9dec2414eff0c97fe2c759958665680d0c61 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Tue, 19 Nov 2024 08:16:50 -0800 Subject: [PATCH 42/48] missing status check --- app/controllers/evaluators_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/evaluators_controller.rb b/app/controllers/evaluators_controller.rb index e15e4475..6705f20e 100644 --- a/app/controllers/evaluators_controller.rb +++ b/app/controllers/evaluators_controller.rb @@ -33,7 +33,8 @@ def destroy def resend_invite @evaluator_invitation = @phase.evaluator_invitations.find(params[:id]) - if evaluator_service.resend_invitation(@evaluator_invitation) + result = evaluator_service.resend_invitation(@evaluator_invitation) + if result[:success] redirect_to phase_evaluators_path(@phase), notice: t('.success') else From 13dbc4c4daad71e3683cdfe02a8c8cc35fd75b8c Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Tue, 19 Nov 2024 08:17:01 -0800 Subject: [PATCH 43/48] rubocop specs --- spec/models/challenge_phases_evaluator_spec.rb | 2 +- spec/models/user_spec.rb | 17 ++++++++--------- spec/requests/evaluators_spec.rb | 10 +++++----- .../evaluator_management_service_spec.rb | 10 +++++----- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/spec/models/challenge_phases_evaluator_spec.rb b/spec/models/challenge_phases_evaluator_spec.rb index b7d1eec9..aba181ed 100644 --- a/spec/models/challenge_phases_evaluator_spec.rb +++ b/spec/models/challenge_phases_evaluator_spec.rb @@ -38,7 +38,7 @@ challenge = create(:challenge) phase = create(:phase, challenge: challenge) user = create(:user, role: 'evaluator') - cpe = create(:challenge_phases_evaluator, challenge: challenge, phase: phase, user: user) + create(:challenge_phases_evaluator, challenge: challenge, phase: phase, user: user) expect(challenge.evaluators).to include(user) expect(phase.evaluators).to include(user) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 9aefc071..fc48948e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -191,15 +191,15 @@ end it 'creates ChallengePhasesEvaluator records for all invitations when user is created' do - expect { + expect do create(:user, email: user_email, role: 'evaluator') - }.to change(ChallengePhasesEvaluator, :count).by(3) + end.to change { ChallengePhasesEvaluator.count }.by(3) end it 'destroys all EvaluatorInvitation records when user is created' do - expect { + expect do create(:user, email: user_email, role: 'evaluator') - }.to change(EvaluatorInvitation, :count).by(-3) + end.to change { EvaluatorInvitation.count }.by(-3) end it 'associates the new user with the correct challenges and phases' do @@ -212,17 +212,16 @@ context 'when there are no existing evaluator invitations' do it 'does not create any ChallengePhasesEvaluator records' do - expect { + expect do create(:user, email: user_email) - }.not_to change(ChallengePhasesEvaluator, :count) + end.not_to change { ChallengePhasesEvaluator.count } end it 'does not destroy any EvaluatorInvitation records' do - expect { + expect do create(:user, email: user_email) - }.not_to change(EvaluatorInvitation, :count) + end.not_to change { EvaluatorInvitation.count } end end - end end diff --git a/spec/requests/evaluators_spec.rb b/spec/requests/evaluators_spec.rb index 73367f56..59b6f7b8 100644 --- a/spec/requests/evaluators_spec.rb +++ b/spec/requests/evaluators_spec.rb @@ -72,7 +72,7 @@ message: 'Invalid email' }) expect do post phase_evaluators_path(phase), params: invalid_params - end.not_to change(EvaluatorInvitation, :count) + end.not_to change { EvaluatorInvitation.count } expect(response).to render_template(:index) end end @@ -115,7 +115,7 @@ phase_id: phase.id } } - end.not_to change(ChallengePhasesEvaluator, :count) + end.not_to change { ChallengePhasesEvaluator.count } end end @@ -131,7 +131,7 @@ phase_id: phase.id } } - end.not_to change(EvaluatorInvitation, :count) + end.not_to change { EvaluatorInvitation.count } end end @@ -218,7 +218,7 @@ params: { evaluator_type: 'user', phase_id: phase.id } expect(response).to have_http_status(:success) - expect(JSON.parse(response.body)).to eq({ 'success' => true, 'message' => 'Evaluator removed successfully.' }) + expect(response.parsed_body).to eq({ 'success' => true, 'message' => 'Evaluator removed successfully.' }) end end @@ -232,7 +232,7 @@ params: { evaluator_type: 'invitation', phase_id: phase.id } expect(response).to have_http_status(:success) - expect(JSON.parse(response.body)).to eq({ 'success' => true, 'message' => 'Invitation removed successfully.' }) + expect(response.parsed_body).to eq({ 'success' => true, 'message' => 'Invitation removed successfully.' }) end end diff --git a/spec/services/evaluator_management_service_spec.rb b/spec/services/evaluator_management_service_spec.rb index 7abeb4bd..82faca8c 100644 --- a/spec/services/evaluator_management_service_spec.rb +++ b/spec/services/evaluator_management_service_spec.rb @@ -4,7 +4,7 @@ let(:user) { create_and_log_in_user(role: 'challenge_manager') } let(:challenge) { create(:challenge) } let(:phase) { create(:phase, challenge: challenge) } - let(:service) { EvaluatorManagementService.new(challenge, phase) } + let(:service) { described_class.new(challenge, phase) } describe '#process_evaluator_invitation' do context 'with an existing user' do @@ -97,13 +97,13 @@ it 'processes all invitations for the user' do expect do - EvaluatorManagementService.accept_evaluator_invitation(evaluator) - end.to change(ChallengePhasesEvaluator, :count).by(1). - and change(EvaluatorInvitation, :count).by(-1) + described_class.accept_evaluator_invitation(evaluator) + end.to change { ChallengePhasesEvaluator.count }.by(1). + and change { EvaluatorInvitation.count }.by(-1) end it 'returns a success message' do - result = EvaluatorManagementService.accept_evaluator_invitation(evaluator) + result = described_class.accept_evaluator_invitation(evaluator) expect(result).to eq({ success: true, message: 'Evaluator created and added to challenge phase successfully.' }) end end From 7cecef36b43fcafc0f38eb303bc03badaa250fce Mon Sep 17 00:00:00 2001 From: Emma Whamond Date: Tue, 19 Nov 2024 14:51:44 -0600 Subject: [PATCH 44/48] Bullet failure: Remove unnecessary eager loaded :submissions not used in view --- app/controllers/phases_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/phases_controller.rb b/app/controllers/phases_controller.rb index 537ffc0c..a5d85943 100644 --- a/app/controllers/phases_controller.rb +++ b/app/controllers/phases_controller.rb @@ -5,7 +5,7 @@ class PhasesController < ApplicationController before_action :set_phase, except: [:index] def index - @challenges = current_user.challenge_manager_challenges.includes([phases: [:evaluation_form, :submissions]]) + @challenges = current_user.challenge_manager_challenges.includes([phases: [:evaluation_form]]) end def submissions From 4064d4f289db2dae8d57f8a746b1aa8d2a4d7b55 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Tue, 19 Nov 2024 14:53:07 -0800 Subject: [PATCH 45/48] adjust error message --- app/services/evaluator_management_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/evaluator_management_service.rb b/app/services/evaluator_management_service.rb index f590202d..58c4e2d9 100644 --- a/app/services/evaluator_management_service.rb +++ b/app/services/evaluator_management_service.rb @@ -37,7 +37,7 @@ def resend_invitation(invitation) { success: true, message: I18n.t('evaluators.process_evaluator_invitation.invitation_resent', email: invitation.email) } else - { success: false } + { success: false, message: I18n.t('evaluators.resend_invite.failure') } end end From 0df811a2676a4a483061fe29dd40bf3f3b20a4a1 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Tue, 19 Nov 2024 14:59:26 -0800 Subject: [PATCH 46/48] inline another render --- app/controllers/evaluators_controller.rb | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/app/controllers/evaluators_controller.rb b/app/controllers/evaluators_controller.rb index 6705f20e..6e49075d 100644 --- a/app/controllers/evaluators_controller.rb +++ b/app/controllers/evaluators_controller.rb @@ -17,7 +17,14 @@ def create evaluator_invitation_params ) - handle_invitation_result(result) + if result[:success] + redirect_to phase_evaluators_path(@phase), notice: result[:message] + else + flash.now[:alert] = result[:message] + @evaluator_invitations = @phase.evaluator_invitations + @existing_evaluators = @phase.evaluators + render :index + end end def destroy @@ -59,15 +66,4 @@ def evaluator_invitation_params :first_name, :last_name, :email, :challenge_id, :phase_id, :last_invite_sent ) end - - def handle_invitation_result(result) - if result[:success] - redirect_to phase_evaluators_path(@phase), notice: result[:message] - else - flash.now[:alert] = result[:message] - @evaluator_invitations = @phase.evaluator_invitations - @existing_evaluators = @phase.evaluators - render :index - end - end end From 380c7ce6d7bc36479c2a251971785b1f25de8cfe Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Tue, 19 Nov 2024 15:18:44 -0800 Subject: [PATCH 47/48] rename helper, remove duplicate helper method --- .../{manage_submissions_helper.rb => submissions_helper.rb} | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) rename app/helpers/{manage_submissions_helper.rb => submissions_helper.rb} (62%) diff --git a/app/helpers/manage_submissions_helper.rb b/app/helpers/submissions_helper.rb similarity index 62% rename from app/helpers/manage_submissions_helper.rb rename to app/helpers/submissions_helper.rb index 4900dc18..75e9cf9b 100644 --- a/app/helpers/manage_submissions_helper.rb +++ b/app/helpers/submissions_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module ManageSubmissionsHelper +module SubmissionsHelper def eligible_for_evaluation?(submission) submission.judging_status.in?(%w[selected winner]) end @@ -8,8 +8,4 @@ def eligible_for_evaluation?(submission) def selected_to_advance?(submission) submission.judging_status.in?(%w[winner]) end - - def phase_has_recused_evaluator?(phase) - phase.evaluator_submission_assignments.recused.exists? - end end From e41dd2b2a8b9267979a6f90c588f65ba58039001 Mon Sep 17 00:00:00 2001 From: Stephen Chudleigh Date: Tue, 19 Nov 2024 15:53:08 -0800 Subject: [PATCH 48/48] remove unnecessary code --- .../controllers/delete_evaluator_modal_controller.js | 9 +-------- app/models/user.rb | 2 -- app/views/evaluators/_delete_evaluator_modal.html.erb | 4 ++-- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/app/javascript/controllers/delete_evaluator_modal_controller.js b/app/javascript/controllers/delete_evaluator_modal_controller.js index 0f4f7431..f9fdf64a 100644 --- a/app/javascript/controllers/delete_evaluator_modal_controller.js +++ b/app/javascript/controllers/delete_evaluator_modal_controller.js @@ -1,7 +1,7 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { - static targets = ["modal", "confirmButton", "modalDescription"] + static targets = ["modal"] static values = { challengeId: String, evaluatorId: String, @@ -27,7 +27,6 @@ export default class extends Controller { close() { this.modalTarget.close() - this.resetModal() } handleOutsideClick(event) { @@ -62,10 +61,4 @@ export default class extends Controller { }) .catch(error => { alert(error.message || 'An error occurred while removing the evaluator') }) } - - resetModal() { - this.modalDescriptionTarget.textContent = 'Deleting an evaluator from the challenge will remove the evaluator from any submissions of this ' + - 'challenge that the evaluator is assigned to. It will also delete any of their completed or in progress evaluations for those submissions.' - this.confirmButtonTarget.textContent = 'Yes' - } } diff --git a/app/models/user.rb b/app/models/user.rb index 6aaa1c08..5207461a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative '../services/evaluator_management_service' - # == Schema Information # # Table name: users diff --git a/app/views/evaluators/_delete_evaluator_modal.html.erb b/app/views/evaluators/_delete_evaluator_modal.html.erb index bdaeccd6..40f88fa2 100644 --- a/app/views/evaluators/_delete_evaluator_modal.html.erb +++ b/app/views/evaluators/_delete_evaluator_modal.html.erb @@ -11,14 +11,14 @@ Are you sure you want to delete an evaluator from this challenge?
    -