diff --git a/.annotation_safe_list.yml b/.annotation_safe_list.yml index 5abfd0ef65..61491c2fc8 100644 --- a/.annotation_safe_list.yml +++ b/.annotation_safe_list.yml @@ -13,6 +13,20 @@ auth.Group: ".. no_pii:": "This model has no PII" auth.Permission: ".. no_pii:": "This model has no PII" +badges.BadgePenalty: + ".. no_pii:": "This model has no PII" +badges.BadgeProgress: + ".. no_pii:": "This model has no PII" +badges.BadgeRequirement: + ".. no_pii:": "This model has no PII" +badges.CredlyOrganization: + ".. no_pii:": "This model has no PII" +badges.DataRule: + ".. no_pii:": "This model has no PII" +badges.Fulfillment: + ".. no_pii:": "This model has no PII" +badges.PenaltyDataRule: + ".. no_pii:": "This model has no PII" credentials.HistoricalProgramCompletionEmailConfiguration: ".. no_pii:": "This model has no PII" contenttypes.ContentType: diff --git a/credentials/apps/badges/admin.py b/credentials/apps/badges/admin.py index fee35b5141..bdf43756b5 100644 --- a/credentials/apps/badges/admin.py +++ b/credentials/apps/badges/admin.py @@ -47,10 +47,10 @@ class BadgeRequirementInline(admin.TabularInline): "event_type", "rules", "description", - "group", + "blend", ) readonly_fields = ("rules",) - ordering = ("group",) + ordering = ("blend",) form = BadgeRequirementForm formset = BadgeRequirementFormSet @@ -194,7 +194,7 @@ def get_fields(self, request, obj=None): return fields def get_readonly_fields(self, request, obj=None): - readonly_fields = super().get_readonly_fields(request, obj) + readonly_fields = list(super().get_readonly_fields(request, obj)) if not obj: return readonly_fields @@ -247,7 +247,7 @@ class CredlyBadgeTemplateAdmin(admin.ModelAdmin): "description": _( """ WARNING: avoid configuration updates on activated badges. - Active badge templates are continuously processed and learners may already have partial progress on them. + Active badge templates are continuously processed and learners may already have progress on them. Any changes in badge template requirements (including data rules) will affect learners' experience! """ ), @@ -318,10 +318,10 @@ def image(self, obj): image.short_description = _("icon") - def save_model(self, request, obj, form, change): # pylint: disable=unused-argument + def save_model(self, request, obj, form, change): pass - def save_formset(self, request, form, formset, change): # pylint: disable=unused-argument + def save_formset(self, request, form, formset, change): """ Check if template is active and has requirements. """ @@ -331,7 +331,7 @@ def save_formset(self, request, form, formset, change): # pylint: disable=unuse messages.set_level(request, messages.ERROR) messages.error(request, _("Active badge template must have at least one requirement.")) return HttpResponseRedirect(request.path) - form.instance.save() + return form.instance.save() class DataRulePenaltyInline(admin.TabularInline): @@ -368,14 +368,14 @@ class BadgeRequirementAdmin(admin.ModelAdmin): "template", "event_type", "template_link", - "group", + "blend", ] fields = [ "template_link", "event_type", "description", - "group", + "blend", ] def has_add_permission(self, request): @@ -455,15 +455,6 @@ def formfield_for_manytomany(self, db_field, request, **kwargs): kwargs["queryset"] = BadgeRequirement.objects.filter(template_id=template_id) return super().formfield_for_manytomany(db_field, request, **kwargs) - def template_link(self, instance): - """ - Interactive link to parent (badge template). - """ - url = reverse("admin:badges_credlybadgetemplate_change", args=[instance.template.pk]) - return format_html('{}', url, instance.template) - - template_link.short_description = _("badge template") - def response_change(self, request, obj): if "_save" in request.POST: return HttpResponseRedirect(reverse("admin:badges_credlybadgetemplate_change", args=[obj.template.pk])) diff --git a/credentials/apps/badges/admin_forms.py b/credentials/apps/badges/admin_forms.py index a27d1b891f..89b70be1d0 100644 --- a/credentials/apps/badges/admin_forms.py +++ b/credentials/apps/badges/admin_forms.py @@ -50,7 +50,7 @@ def clean(self): api_key = settings.BADGES_CONFIG["credly"]["ORGANIZATIONS"][str(uuid)] credly_api_client = CredlyAPIClient(uuid, api_key) - self._ensure_organization_exists(credly_api_client) + self.ensure_organization_exists(credly_api_client) return cleaned_data @@ -64,7 +64,7 @@ def save(self, commit=True): return instance - def _ensure_organization_exists(self, api_client): + def ensure_organization_exists(self, api_client): """ Try to fetch organization data by the configured Credly Organization ID. """ @@ -93,7 +93,7 @@ def clean(self): requirements = cleaned_data.get("requirements") if requirements and not all( - [requirement.template.id == cleaned_data.get("template").id for requirement in requirements] + requirement.template.id == cleaned_data.get("template").id for requirement in requirements ): raise forms.ValidationError(_("All requirements must belong to the same template.")) return cleaned_data @@ -143,7 +143,8 @@ def clean(self): return cleaned_data -class DataRuleFormSet(ParentMixin, forms.BaseInlineFormSet): ... +class DataRuleFormSet(ParentMixin, forms.BaseInlineFormSet): + pass class DataRuleForm(DataRuleExtensionsMixin, forms.ModelForm): @@ -158,7 +159,8 @@ class Meta: data_path = forms.ChoiceField() -class BadgeRequirementFormSet(ParentMixin, forms.BaseInlineFormSet): ... +class BadgeRequirementFormSet(ParentMixin, forms.BaseInlineFormSet): + pass class BadgeRequirementForm(forms.ModelForm): @@ -166,17 +168,18 @@ class Meta: model = BadgeRequirement fields = "__all__" - group = forms.ChoiceField() + blend = forms.ChoiceField() def __init__(self, *args, parent_instance=None, **kwargs): self.template = parent_instance super().__init__(*args, **kwargs) - self.fields["group"].choices = Choices(*[(chr(i), chr(i)) for i in range(65, 91)]) - self.fields["group"].initial = chr(65 + self.template.requirements.count()) + self.fields["blend"].choices = Choices(*[(chr(i), chr(i)) for i in range(65, 91)]) + self.fields["blend"].initial = chr(65 + self.template.requirements.count()) -class PenaltyDataRuleFormSet(ParentMixin, forms.BaseInlineFormSet): ... +class PenaltyDataRuleFormSet(ParentMixin, forms.BaseInlineFormSet): + pass class PenaltyDataRuleForm(DataRuleExtensionsMixin, forms.ModelForm): diff --git a/credentials/apps/badges/apps.py b/credentials/apps/badges/apps.py index 18c9da60eb..7ac5abae31 100644 --- a/credentials/apps/badges/apps.py +++ b/credentials/apps/badges/apps.py @@ -1,25 +1,15 @@ from django.apps import AppConfig -from .toggles import check_badges_enabled +from credentials.apps.badges.toggles import check_badges_enabled -class BadgesAppConfig(AppConfig): - """ - Extended application config with additional Badges-specific logic. - """ - - @property - def verbose_name(self): - return f"Badges: {self.plugin_label}" - - -class BadgesConfig(BadgesAppConfig): +class BadgesConfig(AppConfig): """ Core badges application configuration. """ - default = True name = "credentials.apps.badges" + plugin_label = "badges" verbose_name = "Badges" @check_badges_enabled @@ -29,9 +19,13 @@ def ready(self): Performs initial registrations for checks, signals, etc. """ - from . import signals # pylint: disable=unused-import,import-outside-toplevel - from .checks import badges_checks # pylint: disable=unused-import,import-outside-toplevel - from .signals.handlers import listen_to_badging_events + from credentials.apps.badges import signals # pylint: disable=unused-import,import-outside-toplevel + from credentials.apps.badges.checks import ( # pylint: disable=unused-import,import-outside-toplevel + badges_checks, + ) + from credentials.apps.badges.signals.handlers import ( # pylint: disable=import-outside-toplevel + listen_to_badging_events, + ) listen_to_badging_events() diff --git a/credentials/apps/badges/checks.py b/credentials/apps/badges/checks.py index 00b2bbcc7c..1cc7bdfc20 100644 --- a/credentials/apps/badges/checks.py +++ b/credentials/apps/badges/checks.py @@ -14,6 +14,7 @@ def badges_checks(*args, **kwargs): Raises compatibility Errors upon: - BADGES_CONFIG['events'] is empty + - Credly settings are not properly configured Returns: List of any Errors. diff --git a/credentials/apps/badges/credly/api_client.py b/credentials/apps/badges/credly/api_client.py index 23f805de01..793f4c85ea 100644 --- a/credentials/apps/badges/credly/api_client.py +++ b/credentials/apps/badges/credly/api_client.py @@ -70,7 +70,7 @@ def perform_request(self, method, url_suffix, data=None): """ url = urljoin(self.base_api_url, url_suffix) logger.debug(f"Credly API: {method.upper()} {url}") - response = requests.request(method.upper(), url, headers=self._get_headers(), json=data) + response = requests.request(method.upper(), url, headers=self._get_headers(), json=data, timeout=10) self._raise_for_error(response) return response.json() diff --git a/credentials/apps/badges/credly/exceptions.py b/credentials/apps/badges/credly/exceptions.py index 6752803f69..7fbb2a4374 100644 --- a/credentials/apps/badges/credly/exceptions.py +++ b/credentials/apps/badges/credly/exceptions.py @@ -10,12 +10,8 @@ class CredlyError(BadgesError): Credly backend generic error. """ - pass - class CredlyAPIError(CredlyError): """ Credly API errors. """ - - pass diff --git a/credentials/apps/badges/credly/webhooks.py b/credentials/apps/badges/credly/webhooks.py index 1b9a115e37..b103ffe681 100644 --- a/credentials/apps/badges/credly/webhooks.py +++ b/credentials/apps/badges/credly/webhooks.py @@ -59,14 +59,19 @@ def post(self, request): return Response(status=status.HTTP_204_NO_CONTENT) + @staticmethod + def _get_badge_template_from_data(data): + badge_template = data.get("data", {}).get("badge_template", {}) + return badge_template + @staticmethod def handle_badge_template_created_event(request, data): """ Create a new badge template. """ - # TODO: dry it - badge_template = data.get("data", {}).get("badge_template", {}) - owner = data.get("data", {}).get("badge_template", {}).get("owner", {}) + + badge_template = CredlyWebhook._get_badge_template_from_data(data) + owner = badge_template.get("owner", {}) organization = get_object_or_404(CredlyOrganization, uuid=owner.get("id")) @@ -87,9 +92,9 @@ def handle_badge_template_changed_event(request, data): """ Change the badge template. """ - # TODO: dry it - badge_template = data.get("data", {}).get("badge_template", {}) - owner = data.get("data", {}).get("badge_template", {}).get("owner", {}) + + badge_template = CredlyWebhook._get_badge_template_from_data(data) + owner = badge_template.get("owner", {}) organization = get_object_or_404(CredlyOrganization, uuid=owner.get("id")) @@ -117,6 +122,6 @@ def handle_badge_template_deleted_event(request, data): Deletes the badge template by provided uuid. """ CredlyBadgeTemplate.objects.filter( - uuid=data.get("data", {}).get("badge_template", {}).get("id"), + uuid=CredlyWebhook._get_badge_template_from_data(data).get("id"), site=get_current_site(request), ).delete() diff --git a/credentials/apps/badges/exceptions.py b/credentials/apps/badges/exceptions.py index c5950e24f7..fea8e2632b 100644 --- a/credentials/apps/badges/exceptions.py +++ b/credentials/apps/badges/exceptions.py @@ -8,20 +8,14 @@ class BadgesError(Exception): Badges generic exception. """ - pass - class BadgesProcessingError(BadgesError): """ Exception raised for errors that occur during badge processing. """ - pass - class StopEventProcessing(BadgesProcessingError): """ Exception raised to stop processing an event due to a specific condition. """ - - pass diff --git a/credentials/apps/badges/issuers.py b/credentials/apps/badges/issuers.py index d11f74ed0d..bdc91c0488 100644 --- a/credentials/apps/badges/issuers.py +++ b/credentials/apps/badges/issuers.py @@ -36,7 +36,7 @@ def issue_credential( attributes=None, date_override=None, request=None, - lms_user_id=None, # pylint: disable=unused-argument + lms_user_id=None, ): """ Issue a credential to the user. @@ -86,6 +86,15 @@ def award(self, *, username, credential_id): return user_credential def revoke(self, credential_id, username): + """ + Revokes a badge. + + Changes user credential status to REVOKED, for a given user. + Notifies about the revoked badge (public signal). + + Returns: UserCredential + """ + credential = self.get_credential(credential_id) user_credential = self.issue_credential(credential, username, status=UserCredentialStatus.REVOKED) @@ -130,6 +139,10 @@ def issue_credly_badge(self, *, user_credential): user_credential.save() def revoke_credly_badge(self, credential_id, user_credential): + """ + Requests Credly service for external badge revoking based on internal user credential (CredlyBadge). + """ + credential = self.get_credential(credential_id) credly_api = CredlyAPIClient(credential.organization.uuid) revoke_data = { @@ -165,6 +178,16 @@ def award(self, *, username, credential_id): return credly_badge def revoke(self, credential_id, username): + """ + Revokes a Credly badge. + + - Changes user credential status to REVOKED, for a given user; + - Notifies about the revoked badge (public signal); + - Revokes external Credly badge (Credly API); + + Returns: (CredlyBadge) user credential + """ + user_credential = super().revoke(credential_id, username) if user_credential.propagated: self.revoke_credly_badge(credential_id, user_credential) diff --git a/credentials/apps/badges/management/commands/sync_organization_badge_templates.py b/credentials/apps/badges/management/commands/sync_organization_badge_templates.py index 29da77e488..b55e968abb 100644 --- a/credentials/apps/badges/management/commands/sync_organization_badge_templates.py +++ b/credentials/apps/badges/management/commands/sync_organization_badge_templates.py @@ -21,8 +21,11 @@ def handle(self, *args, **options): Sync badge templates for a specific organization or all organizations. Usage: - ./manage.py sync_organization_badge_templates --site_id 1 - ./manage.py sync_organization_badge_templates --site_id 1 --organization_id c117c179-81b1-4f7e-a3a1-e6ae30568c13 + site_id=1 + org_id=c117c179-81b1-4f7e-a3a1-e6ae30568c13 + + ./manage.py sync_organization_badge_templates --site_id $site_id + ./manage.py sync_organization_badge_templates --site_id $site_id --organization_id $org_id """ DEFAULT_SITE_ID = 1 organizations_to_sync = [] @@ -40,7 +43,8 @@ def handle(self, *args, **options): else: organizations_to_sync = CredlyOrganization.get_all_organization_ids() logger.info( - f"Organization ID wasn't provided: syncing badge templates for all organizations - {organizations_to_sync}" + "Organization ID wasn't provided: syncing badge templates for all organizations - " + f"{organizations_to_sync}", ) for organization_id in organizations_to_sync: diff --git a/credentials/apps/badges/migrations/0022_rename_group_fulfillment_blend_and_more.py b/credentials/apps/badges/migrations/0022_rename_group_fulfillment_blend_and_more.py new file mode 100644 index 0000000000..fc193b3192 --- /dev/null +++ b/credentials/apps/badges/migrations/0022_rename_group_fulfillment_blend_and_more.py @@ -0,0 +1,64 @@ +# Generated by Django 4.1 on 2024-05-31 13:46 + +from django.db import migrations, models +import model_utils.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ("badges", "0021_alter_credlyorganization_api_key"), + ] + + operations = [ + migrations.RenameField( + model_name="fulfillment", + old_name="group", + new_name="blend", + ), + migrations.RemoveField( + model_name="badgerequirement", + name="group", + ), + migrations.AddField( + model_name="badgerequirement", + name="blend", + field=models.CharField( + blank=True, + help_text="Optional. Group requirements together using the same Group ID for interchangeable (OR processing logic).", + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="badgetemplate", + name="state", + field=model_utils.fields.StatusField( + choices=[("draft", "draft"), ("active", "active"), ("archived", "archived")], + default="draft", + help_text="Credly badge template state (auto-managed).", + max_length=100, + no_check_for_status=True, + ), + ), + migrations.AlterField( + model_name="credlybadge", + name="state", + field=model_utils.fields.StatusField( + choices=[ + ("created", "created"), + ("no_response", "no_response"), + ("error", "error"), + ("pending", "pending"), + ("accepted", "accepted"), + ("rejected", "rejected"), + ("revoked", "revoked"), + ("expired", "expired"), + ], + default="created", + help_text="Credly badge issuing state", + max_length=100, + no_check_for_status=True, + ), + ), + ] diff --git a/credentials/apps/badges/models.py b/credentials/apps/badges/models.py index 0f8b21aeab..83c46c9639 100644 --- a/credentials/apps/badges/models.py +++ b/credentials/apps/badges/models.py @@ -36,7 +36,9 @@ class CredlyOrganization(TimeStampedModel): uuid = models.UUIDField(unique=True, help_text=_("Put your Credly Organization ID here.")) api_key = models.CharField( - max_length=255, help_text=_("Credly API shared secret for Credly Organization."), blank=True + max_length=255, + help_text=_("Credly API shared secret for Credly Organization."), + blank=True, ) name = models.CharField( max_length=255, @@ -104,7 +106,7 @@ def save(self, *args, **kwargs): @property def groups(self): - return self.requirements.values_list("group", flat=True).distinct() + return self.requirements.values_list("blend", flat=True).distinct() @classmethod def by_uuid(cls, template_uuid): @@ -173,17 +175,19 @@ class BadgeRequirement(models.Model): max_length=255, choices=EVENT_TYPES, help_text=_( - 'Public signal type. Available events are configured in "BADGES_CONFIG" setting. The crucial aspect for event to carry UserData in its payload.' + 'Public signal type. Available events are configured in "BADGES_CONFIG" setting. ' + "The crucial aspect for event to carry UserData in its payload." ), ) description = models.TextField(null=True, blank=True, help_text=_("Provide more details if needed.")) - group = models.CharField( + blend = models.CharField( max_length=255, null=True, blank=True, help_text=_( - "Optional. Put requirements into the same arbitrary Group ID to make them interchangeable (OR processing logic applies)." + "Optional. Group requirements together using the same Group ID for interchangeable (OR processing logic)." ), + verbose_name=_("group"), ) def __str__(self): @@ -200,7 +204,7 @@ def fulfill(self, username: str): """ template_id = self.template.id progress = BadgeProgress.for_user(username=username, template_id=template_id) - fulfillment, created = Fulfillment.objects.get_or_create(progress=progress, requirement=self, group=self.group) + fulfillment, created = Fulfillment.objects.get_or_create(progress=progress, requirement=self, blend=self.blend) if created: notify_requirement_fulfilled( @@ -248,7 +252,7 @@ def is_group_fulfilled(cls, *, group: str, template: BadgeTemplate, username: st """ progress = BadgeProgress.for_user(username=username, template_id=template.id) - requirements = cls.objects.filter(template=template, group=group) + requirements = cls.objects.filter(template=template, blend=group) fulfilled_requirements = requirements.filter(fulfillments__progress=progress).count() return fulfilled_requirements > 0 @@ -418,6 +422,10 @@ def reset_requirements(self, username: str): @property def is_active(self): + """ + Checks if the penalty is active. + """ + return self.template.is_active @@ -442,6 +450,9 @@ def __str__(self): @property def is_active(self): + """ + Checks if the rule is active. + """ return self.penalty.template.is_active @@ -490,6 +501,10 @@ def ratio(self) -> float: @property def groups(self): + """ + Returns gorups and their statuses (fulfilled or not). + """ + return { group: BadgeRequirement.is_group_fulfilled(group=group, template=self.template, username=self.username) for group in self.template.groups @@ -516,6 +531,10 @@ def regress(self): notify_progress_incomplete(self, self.username, self.template.id) def reset(self): + """ + Resets the progress. + """ + Fulfillment.objects.filter(progress=self).delete() @@ -532,7 +551,13 @@ class Fulfillment(models.Model): null=True, related_name="fulfillments", ) - group = models.CharField(max_length=255, null=True, blank=True, help_text=_("Group ID for the requirement.")) + blend = models.CharField( + max_length=255, + null=True, + blank=True, + help_text=_("Group ID for the requirement."), + verbose_name=_("group"), + ) class CredlyBadge(UserCredential): diff --git a/credentials/apps/badges/processing/generic.py b/credentials/apps/badges/processing/generic.py index 8f54808c16..3ac0839841 100644 --- a/credentials/apps/badges/processing/generic.py +++ b/credentials/apps/badges/processing/generic.py @@ -65,7 +65,10 @@ def identify_user(*, event_type, event_payload): user_data = get_user_data(event_payload) if not user_data: - message = f"User data cannot be found (got: {user_data}): {event_payload}. Does event {event_type} include user data at all?" + message = ( + f"User data cannot be found (got: {user_data}): {event_payload}. " + f"Does event {event_type} include user data at all?" + ) raise BadgesProcessingError(message) user, __ = get_or_create_user_from_event_data(user_data) diff --git a/credentials/apps/badges/signals/handlers.py b/credentials/apps/badges/signals/handlers.py index 27e1ab5fc2..2044e08494 100644 --- a/credentials/apps/badges/signals/handlers.py +++ b/credentials/apps/badges/signals/handlers.py @@ -36,7 +36,7 @@ def listen_to_badging_events(): signal.connect(handle_badging_event, dispatch_uid=event_type) -def handle_badging_event(sender, signal, **kwargs): +def handle_badging_event(sender, signal, **kwargs): # pylint: disable=unused-argument """ Generic handler for incoming from the Event bus public signals. """ @@ -47,7 +47,7 @@ def handle_badging_event(sender, signal, **kwargs): @receiver(BADGE_REQUIREMENT_FULFILLED) -def handle_requirement_fulfilled(sender, username, **kwargs): # pylint: disable=unused-argument +def handle_requirement_fulfilled(sender, username, **kwargs): """ On user's Badge progression (completion). """ @@ -55,7 +55,7 @@ def handle_requirement_fulfilled(sender, username, **kwargs): # pylint: disable @receiver(BADGE_REQUIREMENT_REGRESSED) -def handle_requirement_regressed(sender, username, **kwargs): # pylint: disable=unused-argument +def handle_requirement_regressed(sender, username, **kwargs): """ On user's Badge regression (incompletion). """ diff --git a/credentials/apps/badges/signals/signals.py b/credentials/apps/badges/signals/signals.py index c7faf9eaef..db224ff549 100644 --- a/credentials/apps/badges/signals/signals.py +++ b/credentials/apps/badges/signals/signals.py @@ -71,7 +71,7 @@ def notify_progress_incomplete(sender, username, badge_template_id): ) -def notify_badge_awarded(user_credential): # pylint: disable=unused-argument +def notify_badge_awarded(user_credential): """ Emits a public signal about the badge template completion for user. @@ -82,7 +82,7 @@ def notify_badge_awarded(user_credential): # pylint: disable=unused-argument BADGE_AWARDED.send_event(badge=user_credential.as_badge_data()) -def notify_badge_revoked(user_credential): # pylint: disable=unused-argument +def notify_badge_revoked(user_credential): """ Emit public event about badge template regression. diff --git a/credentials/apps/badges/tests/test_admin_forms.py b/credentials/apps/badges/tests/test_admin_forms.py index db5152331e..6a5914e936 100644 --- a/credentials/apps/badges/tests/test_admin_forms.py +++ b/credentials/apps/badges/tests/test_admin_forms.py @@ -1,11 +1,17 @@ import uuid +from unittest.mock import MagicMock, patch from django import forms from django.contrib.sites.models import Site -from django.test import TestCase -from django.utils.translation import gettext as _ +from django.test import TestCase, override_settings -from credentials.apps.badges.admin_forms import BadgePenaltyForm +from credentials.apps.badges.admin_forms import ( + BadgePenaltyForm, + CredlyOrganizationAdminForm, + DataRuleExtensionsMixin, + ParentMixin, +) +from credentials.apps.badges.credly.exceptions import CredlyAPIError from credentials.apps.badges.models import BadgeRequirement, BadgeTemplate @@ -71,3 +77,171 @@ def test_clean_requirements_different_template(self): form.clean() self.assertEqual(str(cm.exception), "['All requirements must belong to the same template.']") + + @override_settings(BADGES_CONFIG={"credly": {"ORGANIZATIONS": {}}}) + def test_clean(self): + form = CredlyOrganizationAdminForm() + form.cleaned_data = { + "uuid": "test_uuid", + "api_key": "test_api_key", + } + + with patch( + "credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations" + ) as mock_get_orgs: + mock_get_orgs.return_value = {} + + with patch("credentials.apps.badges.admin_forms.CredlyAPIClient") as mock_client: + mock_client.return_value = MagicMock() + + form.clean() + + mock_get_orgs.assert_called_once() + mock_client.assert_called_once_with("test_uuid", "test_api_key") + + @override_settings(BADGES_CONFIG={"credly": {"ORGANIZATIONS": {"test_uuid": "test_api_key"}}}) + def test_clean_with_configured_organization(self): + form = CredlyOrganizationAdminForm() + form.cleaned_data = { + "uuid": "test_uuid", + "api_key": None, + } + + with patch( + "credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations" + ) as mock_get_orgs: + mock_get_orgs.return_value = {"test_uuid": "test_org"} + + with patch("credentials.apps.badges.admin_forms.CredlyAPIClient") as mock_client: + mock_client.return_value = MagicMock() + + form.clean() + + mock_get_orgs.assert_called_once() + mock_client.assert_called_once_with("test_uuid", "test_api_key") + + def test_clean_with_invalid_organization(self): + form = CredlyOrganizationAdminForm() + form.cleaned_data = { + "uuid": "invalid_uuid", + "api_key": "test_api_key", + } + + with patch( + "credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations" + ) as mock_get_orgs: + mock_get_orgs.return_value = {"test_uuid": "test_org"} + + with self.assertRaises(forms.ValidationError) as cm: + form.clean() + + self.assertIn("You specified an invalid authorization token.", str(cm.exception)) + + def test_clean_cannot_provide_api_key_for_configured_organization(self): + form = CredlyOrganizationAdminForm() + form.cleaned_data = { + "uuid": "test_uuid", + "api_key": "test_api_key", + } + + with patch( + "credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations" + ) as mock_get_orgs: + mock_get_orgs.return_value = {"test_uuid": "test_org"} + + with self.assertRaises(forms.ValidationError) as cm: + form.clean() + + self.assertEqual( + str(cm.exception), + '["You can\'t provide an API key for a configured organization."]', + ) + + def test_ensure_organization_exists(self): + form = CredlyOrganizationAdminForm() + api_client = MagicMock() + api_client.fetch_organization.return_value = {"data": {"org_id": "test_org_id"}} + + form.ensure_organization_exists(api_client) + + api_client.fetch_organization.assert_called_once() + self.assertEqual(form.api_data, {"org_id": "test_org_id"}) + + def test_ensure_organization_exists_with_error(self): + form = CredlyOrganizationAdminForm() + api_client = MagicMock() + api_client.fetch_organization.side_effect = CredlyAPIError("API Error") + + with self.assertRaises(forms.ValidationError) as cm: + form.ensure_organization_exists(api_client) + + api_client.fetch_organization.assert_called_once() + self.assertEqual(str(cm.exception), "['API Error']") + + +class TestParentMixin(ParentMixin): + pass + + +class ParentMixinTestCase(TestCase): + def setUp(self): + self.instance = MagicMock() + self.instance.some_attribute = "some_value" + + self.mixin = TestParentMixin() + self.mixin.instance = self.instance + + def test_get_form_kwargs_passes_parent_instance(self): + with patch.object( + TestParentMixin, + "get_form_kwargs", + return_value={"parent_instance": self.instance}, + ) as super_method: + result = self.mixin.get_form_kwargs(0) + + super_method.assert_called_once_with(0) + + self.assertIn("parent_instance", result) + self.assertEqual(result["parent_instance"], self.instance) + + +class TestForm(DataRuleExtensionsMixin, forms.Form): + data_path = forms.ChoiceField(choices=[]) + value = forms.CharField() + + +class DataRuleExtensionsMixinTestCase(TestCase): + def setUp(self): + self.parent_instance = MagicMock() + self.parent_instance.event_type = COURSE_PASSING_EVENT + + def test_init_sets_choices_based_on_event_type(self): + form = TestForm(parent_instance=self.parent_instance) + self.assertEqual( + form.fields["data_path"].choices, + [("is_passing", "is_passing"), ("course.course_key", "course.course_key")], + ) + + def test_clean_with_valid_boolean_value(self): + form = TestForm( + data={"data_path": "is_passing", "value": "True"}, + parent_instance=self.parent_instance, + ) + form.is_valid() + self.assertRaises(KeyError, lambda: form.errors["__all__"]) + + def test_clean_with_invalid_boolean_value(self): + form = TestForm( + data={"data_path": "is_passing", "value": "invalid"}, + parent_instance=self.parent_instance, + ) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors["__all__"], ["Value must be a boolean."]) + + def test_clean_with_non_boolean_data_path(self): + form = TestForm( + data={"data_path": "course.course_key", "value": "some_value"}, + parent_instance=self.parent_instance, + ) + form.is_valid() + self.assertRaises(KeyError, lambda: form.errors["__all__"]) diff --git a/credentials/apps/badges/tests/test_api_client.py b/credentials/apps/badges/tests/test_api_client.py new file mode 100644 index 0000000000..1d9c29a32a --- /dev/null +++ b/credentials/apps/badges/tests/test_api_client.py @@ -0,0 +1,99 @@ +from unittest import mock + +from attrs import asdict +from django.test import TestCase +from faker import Faker +from openedx_events.learning.data import BadgeData, BadgeTemplateData, UserData, UserPersonalData + +from credentials.apps.badges.credly.api_client import CredlyAPIClient +from credentials.apps.badges.credly.exceptions import CredlyError +from credentials.apps.badges.models import CredlyOrganization + + +class CredlyApiClientTestCase(TestCase): + def setUp(self): + fake = Faker() + self.api_client = CredlyAPIClient("test_organization_id", "test_api_key") + self.badge_data = BadgeData( + uuid=fake.uuid4(), + user=UserData( + id=1, + is_active=True, + pii=UserPersonalData(username="test_user", email="test_email@mail.com", name="Test User"), + ), + template=BadgeTemplateData( + uuid=fake.uuid4(), + name="Test Badge", + origin="Credly", + description="Test Badge Description", + image_url="https://test.com/image.png", + ), + ) + + def test_get_organization_nonexistent(self): + with mock.patch("credentials.apps.badges.credly.api_client.CredlyOrganization.objects.get") as mock_get: + mock_get.side_effect = CredlyOrganization.DoesNotExist + with self.assertRaises(CredlyError) as cm: + CredlyAPIClient("nonexistent_organization_id") + self.assertEqual( + str(cm.exception), + "CredlyOrganization with the uuid nonexistent_organization_id does not exist!", + ) + + def test_perform_request(self): + with mock.patch("credentials.apps.badges.credly.api_client.requests.request") as mock_request: + mock_response = mock.Mock() + mock_response.json.return_value = {"key": "value"} + mock_request.return_value = mock_response + result = self.api_client.perform_request("GET", "/api/endpoint") + mock_request.assert_called_once_with( + "GET", + "https://sandbox-api.credly.com/api/endpoint", + headers={ + "Accept": "application/json", + "Content-Type": "application/json", + "Authorization": "Basic dGVzdF9hcGlfa2V5", + }, + json=None, + timeout=10, + ) + self.assertEqual(result, {"key": "value"}) + + def test_fetch_organization(self): + with mock.patch.object(CredlyAPIClient, "perform_request") as mock_perform_request: + mock_perform_request.return_value = {"organization": "data"} + result = self.api_client.fetch_organization() + mock_perform_request.assert_called_once_with("get", "") + self.assertEqual(result, {"organization": "data"}) + + def test_fetch_badge_templates(self): + with mock.patch.object(CredlyAPIClient, "perform_request") as mock_perform_request: + mock_perform_request.return_value = {"badge_templates": ["template1", "template2"]} + result = self.api_client.fetch_badge_templates() + mock_perform_request.assert_called_once_with("get", "badge_templates/?filter=state::active") + self.assertEqual(result, {"badge_templates": ["template1", "template2"]}) + + def test_fetch_event_information(self): + event_id = "event123" + with mock.patch.object(CredlyAPIClient, "perform_request") as mock_perform_request: + mock_perform_request.return_value = {"event": "data"} + result = self.api_client.fetch_event_information(event_id) + mock_perform_request.assert_called_once_with("get", f"events/{event_id}/") + self.assertEqual(result, {"event": "data"}) + + def test_issue_badge(self): + issue_badge_data = self.badge_data + with mock.patch.object(CredlyAPIClient, "perform_request") as mock_perform_request: + mock_perform_request.return_value = {"badge": "issued"} + result = self.api_client.issue_badge(issue_badge_data) + mock_perform_request.assert_called_once_with("post", "badges/", asdict(issue_badge_data)) + self.assertEqual(result, {"badge": "issued"}) + + def test_revoke_badge(self): + badge_id = "badge123" + data = {"data": "value"} + with mock.patch.object(CredlyAPIClient, "perform_request") as mock_perform_request: + mock_perform_request.return_value = {"badge": "revoked"} + result = self.api_client.revoke_badge(badge_id, data) + mock_perform_request.assert_called_once_with("put", f"badges/{badge_id}/revoke/", data=data) + self.assertEqual(result, {"badge": "revoked"}) diff --git a/credentials/apps/badges/tests/test_issuers.py b/credentials/apps/badges/tests/test_issuers.py index 07a7dae208..45cc07fea4 100644 --- a/credentials/apps/badges/tests/test_issuers.py +++ b/credentials/apps/badges/tests/test_issuers.py @@ -1,16 +1,22 @@ from unittest import mock +from unittest.mock import patch import faker +from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.test import TestCase +from credentials.apps.badges.credly.api_client import CredlyAPIClient +from credentials.apps.badges.credly.exceptions import CredlyAPIError +from credentials.apps.badges.issuers import CredlyBadgeTemplateIssuer +from credentials.apps.badges.models import CredlyBadge, CredlyBadgeTemplate, CredlyOrganization from credentials.apps.credentials.constants import UserCredentialStatus -from ..issuers import CredlyBadgeTemplateIssuer -from ..models import CredlyBadge, CredlyBadgeTemplate, CredlyOrganization +User = get_user_model() -class CredlyBadgeTemplateIssuer(TestCase): + +class CredlyBadgeTemplateIssuerTestCase(TestCase): issued_credential_type = CredlyBadgeTemplate issued_user_credential_type = CredlyBadge issuer = CredlyBadgeTemplateIssuer @@ -29,6 +35,11 @@ def setUp(self): state="active", organization=credly_organization, ) + User.objects.create_user(username="test_user", email="test_email@fff.com", password="test_password") + + def _perform_request(self, method, endpoint, data=None): # pylint: disable=unused-argument + fake = faker.Faker() + return {"data": {"id": fake.uuid4(), "state": "issued"}} def test_create_user_credential_with_status_awared(self): # Call create_user_credential with valid arguments @@ -76,3 +87,51 @@ def test_create_user_credential_with_status_revoked(self): status=UserCredentialStatus.REVOKED, ).exists() ) + + @patch.object(CredlyAPIClient, "perform_request", _perform_request) + def test_issue_credly_badge(self): + # Create a test user credential + user_credential = self.issued_user_credential_type.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.badge_template), + credential_id=self.badge_template.id, + state=CredlyBadge.STATES.pending, + uuid=self.fake.uuid4(), + external_uuid=self.fake.uuid4(), + ) + + # Call the issue_credly_badge method + self.issuer().issue_credly_badge(user_credential=user_credential) + + # Check if the user credential is updated with the external UUID and state + self.assertIsNotNone(user_credential.external_uuid) + self.assertEqual(user_credential.state, "issued") + + # Check if the user credential is saved + user_credential.refresh_from_db() + self.assertIsNotNone(user_credential.external_uuid) + self.assertEqual(user_credential.state, "issued") + + def test_issue_credly_badge_with_error(self): + # Create a test user credential + user_credential = self.issued_user_credential_type.objects.create( + username="test_user", + credential_content_type=ContentType.objects.get_for_model(self.badge_template), + credential_id=self.badge_template.id, + state=CredlyBadge.STATES.pending, + uuid=self.fake.uuid4(), + external_uuid=self.fake.uuid4(), + ) + + # Mock the CredlyAPIClient and its issue_badge method to raise CredlyAPIError + with mock.patch("credentials.apps.badges.credly.api_client.CredlyAPIClient") as mock_credly_api_client: + mock_issue_badge = mock_credly_api_client.return_value.issue_badge + mock_issue_badge.side_effect = CredlyAPIError + + # Call the issue_credly_badge method and expect CredlyAPIError to be raised + with self.assertRaises(CredlyAPIError): + self.issuer().issue_credly_badge(user_credential=user_credential) + + # Check if the user credential state is updated to "error" + user_credential.refresh_from_db() + self.assertEqual(user_credential.state, "error") diff --git a/credentials/apps/badges/tests/test_models.py b/credentials/apps/badges/tests/test_models.py index 3be5212c5e..a160c12e8e 100644 --- a/credentials/apps/badges/tests/test_models.py +++ b/credentials/apps/badges/tests/test_models.py @@ -1,11 +1,15 @@ import uuid +from unittest.mock import patch +from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.contrib.sites.models import Site from django.test import TestCase +from faker import Faker from openedx_events.learning.data import BadgeData, BadgeTemplateData, UserData, UserPersonalData from credentials.apps.badges.models import ( + BadgePenalty, BadgeProgress, BadgeRequirement, BadgeTemplate, @@ -102,7 +106,6 @@ def setUp(self): organization=self.organization, uuid=uuid.uuid4(), name="test_template", state="draft", site=self.site ) - def test_multiple_requirements_for_badgetemplate(self): self.requirement1 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", @@ -114,41 +117,54 @@ def test_multiple_requirements_for_badgetemplate(self): description="Test description", ) self.requirement3 = BadgeRequirement.objects.create( - template=self.badge_template, - event_type="org.openedx.learning.ccx.course.passing.status.updated.v1", - description="Test description", - ) - - requirements = BadgeRequirement.objects.filter(template=self.badge_template) - - self.assertEqual(requirements.count(), 3) - self.assertIn(self.requirement1, requirements) - self.assertIn(self.requirement2, requirements) - self.assertIn(self.requirement3, requirements) - - def test_multiple_requirements_for_credlybadgetemplate(self): - self.requirement1 = BadgeRequirement.objects.create( template=self.credlybadge_template, event_type="org.openedx.learning.ccx.course.passing.status.updated.v1", description="Test description", ) - self.requirement2 = BadgeRequirement.objects.create( + self.requirement4 = BadgeRequirement.objects.create( template=self.credlybadge_template, event_type="org.openedx.learning.ccx.course.passing.status.updated.v1", description="Test description", ) - self.requirement3 = BadgeRequirement.objects.create( - template=self.credlybadge_template, + + self.requirement = BadgeRequirement.objects.create( + template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", ) - requirements = BadgeRequirement.objects.filter(template=self.credlybadge_template) + def test_multiple_requirements_for_badgetemplate(self): + requirements = BadgeRequirement.objects.filter(template=self.badge_template) self.assertEqual(requirements.count(), 3) self.assertIn(self.requirement1, requirements) self.assertIn(self.requirement2, requirements) + + def test_multiple_requirements_for_credlybadgetemplate(self): + requirements = BadgeRequirement.objects.filter(template=self.credlybadge_template) + + self.assertEqual(requirements.count(), 2) self.assertIn(self.requirement3, requirements) + self.assertIn(self.requirement4, requirements) + + def test_fulfill(self): + username = "test_user" + template_id = self.badge_template.id + progress = BadgeProgress.objects.create(username=username, template=self.badge_template) + with patch("credentials.apps.badges.models.notify_requirement_fulfilled") as mock_notify: + created = self.requirement.fulfill(username) + fulfillment = Fulfillment.objects.get( + progress=progress, requirement=self.requirement, blend=self.requirement.blend + ) + + self.assertTrue(created) + self.assertTrue(mock_notify.called) + mock_notify.assert_called_with( + sender=self.requirement, + username=username, + badge_template_id=template_id, + fulfillment_id=fulfillment.id, + ) class RequirementFulfillmentCheckTestCase(TestCase): @@ -184,21 +200,21 @@ def setUp(self): self.badge_requirement1 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", - group="group1", + blend="group1", ) self.badge_requirement2 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.ccx.course.passing.status.updated.v1", - group="group1", + blend="group1", ) self.badge_requirement3 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1" ) def test_requirement_group(self): - group = self.badge_template.requirements.filter(group="group1") - self.assertEqual(group.count(), 2) - self.assertIsNone(self.badge_requirement3.group) + groups = self.badge_template.requirements.filter(blend="group1") + self.assertEqual(groups.count(), 2) + self.assertIsNone(self.badge_requirement3.blend) class BadgeTemplateUserProgressTestCase(TestCase): @@ -217,19 +233,19 @@ def setUp(self): template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="A", + blend="A", ) self.requirement2 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="B", + blend="B", ) self.requirement3 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.ccx.course.passing.status.updated.v1", description="Test description", - group="C", + blend="C", ) def test_user_progress_success(self): @@ -297,39 +313,39 @@ def setUp(self): template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="A", + blend="A", ) self.requirement2 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="B", + blend="B", ) self.group_requirement1 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="test-group1", + blend="test-group1", ) self.group_requirement2 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="test-group1", + blend="test-group1", ) self.group_requirement3 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="test-group2", + blend="test-group2", ) self.group_requirement4 = BadgeRequirement.objects.create( template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1", description="Test description", - group="test-group2", + blend="test-group2", ) self.progress = BadgeProgress.objects.create(username="test_user", template=self.badge_template) @@ -410,3 +426,117 @@ def test_as_badge_data(self): ) actual_badge_data = self.badge.as_badge_data() self.assertEqual(actual_badge_data, expected_badge_data) + + +class BadgePenaltyTestCase(TestCase): + def setUp(self): + self.fake = Faker() + self.badge_template = BadgeTemplate.objects.create( + uuid=self.fake.uuid4(), + name="test_template", + state="draft", + site=Site.objects.create(domain="test_domain", name="test_name"), + is_active=True, + ) + self.badge_requirement = BadgeRequirement.objects.create( + template=self.badge_template, + event_type="org.openedx.learning.course.passing.status.updated.v1", + description="Test description", + ) + self.badge_penalty = BadgePenalty.objects.create( + template=self.badge_template, + event_type="org.openedx.learning.student.registration.completed.v1", + ) + self.badge_penalty.requirements.add(self.badge_requirement) + + def test_apply_rules_with_empty_rules(self): + data = {"key": "value"} + self.assertFalse(self.badge_penalty.apply_rules(data)) + + def test_apply_rules_with_non_empty_rules(self): + data = {"key": "value"} + self.badge_penalty.rules.create(data_path="key", operator="eq", value="value") + self.assertTrue(self.badge_penalty.apply_rules(data)) + + def test_reset_requirements(self): + username = "test-username" + with patch("credentials.apps.badges.models.BadgeRequirement.reset") as mock_reset: + self.badge_penalty.reset_requirements(username) + mock_reset.assert_called_once_with(username) + + def test_is_active(self): + self.assertTrue(self.badge_penalty.is_active) + + +class IsGroupFulfilledTestCase(TestCase): + def setUp(self): + self.site = Site.objects.create(domain="test_domain", name="test_name") + self.badge_template = BadgeTemplate.objects.create( + uuid=uuid.uuid4(), name="test_template", state="draft", site=self.site + ) + self.badge_requirement1 = BadgeRequirement.objects.create( + template=self.badge_template, + event_type="org.openedx.learning.course.passing.status.updated.v1", + blend="group1", + ) + self.badge_requirement2 = BadgeRequirement.objects.create( + template=self.badge_template, + event_type="org.openedx.learning.ccx.course.passing.status.updated.v1", + blend="group1", + ) + self.badge_requirement3 = BadgeRequirement.objects.create( + template=self.badge_template, event_type="org.openedx.learning.course.passing.status.updated.v1" + ) + self.username = "test_user" + + def test_is_group_fulfilled_with_fulfilled_requirements(self): + progress = BadgeProgress.objects.create(username=self.username, template=self.badge_template) + Fulfillment.objects.create(progress=progress, requirement=self.badge_requirement1) + + is_fulfilled = BadgeRequirement.is_group_fulfilled( + group="group1", template=self.badge_template, username=self.username + ) + + self.assertTrue(is_fulfilled) + + def test_is_group_fulfilled_with_unfulfilled_requirements(self): + is_fulfilled = BadgeRequirement.is_group_fulfilled( + group="group1", template=self.badge_template, username=self.username + ) + + self.assertFalse(is_fulfilled) + + def test_is_group_fulfilled_with_invalid_group(self): + is_fulfilled = BadgeRequirement.is_group_fulfilled( + group="invalid_group", template=self.badge_template, username=self.username + ) + + self.assertFalse(is_fulfilled) + + +class CredlyOrganizationTestCase(TestCase): + def setUp(self): + self.fake = Faker() + self.uuid = self.fake.uuid4() + self.organization = CredlyOrganization.objects.create( + uuid=self.uuid, api_key="test-api-key", name="test_organization" + ) + + def test_str_representation(self): + self.assertEqual(str(self.organization), "test_organization") + + def test_get_all_organization_ids(self): + organization_ids = [str(uuid) for uuid in CredlyOrganization.get_all_organization_ids()] + self.assertEqual(organization_ids, [self.uuid]) + + def test_get_preconfigured_organizations(self): + preconfigured_organizations = CredlyOrganization.get_preconfigured_organizations() + self.assertEqual(preconfigured_organizations, settings.BADGES_CONFIG["credly"].get("ORGANIZATIONS", {})) + + def test_is_preconfigured(self): + with patch( + "credentials.apps.badges.models.CredlyOrganization.get_preconfigured_organizations" + ) as mock_get_preconfigured: + mock_get_preconfigured.return_value = {str(self.uuid): "Test Organization"} + self.assertTrue(self.organization.is_preconfigured) + mock_get_preconfigured.assert_called_once() diff --git a/credentials/apps/badges/tests/test_services.py b/credentials/apps/badges/tests/test_services.py index ae5e9dfbc1..e23af3b8d5 100644 --- a/credentials/apps/badges/tests/test_services.py +++ b/credentials/apps/badges/tests/test_services.py @@ -1,4 +1,5 @@ import uuid +from unittest.mock import MagicMock, patch from django.contrib.auth import get_user_model from django.contrib.sites.models import Site @@ -6,6 +7,7 @@ from opaque_keys.edx.keys import CourseKey from openedx_events.learning.data import CourseData, CoursePassingStatusData, UserData, UserPersonalData +from credentials.apps.badges.exceptions import BadgesProcessingError from credentials.apps.badges.models import ( BadgePenalty, BadgeProgress, @@ -17,7 +19,7 @@ Fulfillment, PenaltyDataRule, ) -from credentials.apps.badges.processing.generic import identify_user +from credentials.apps.badges.processing.generic import identify_user, process_event from credentials.apps.badges.processing.progression import discover_requirements, process_requirements from credentials.apps.badges.processing.regression import discover_penalties, process_penalties from credentials.apps.badges.signals import BADGE_PROGRESS_COMPLETE @@ -279,13 +281,13 @@ def test_process_one_of_grouped_requirements_penalty(self): template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="Test course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_b = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="Test course passing award description", - group="a_or_b", + blend="a_or_b", ) DataRule.objects.create( requirement=requirement_a, @@ -320,13 +322,13 @@ def test_process_mixed_penalty(self): template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="Test course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_b = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="Test course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_c = BadgeRequirement.objects.create( template=self.badge_template, @@ -418,13 +420,13 @@ def test_course_a_or_b_completion(self): template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_b = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B course passing award description", - group="a_or_b", + blend="a_or_b", ) DataRule.objects.create( requirement=requirement_a, @@ -448,19 +450,19 @@ def test_course_a_or_b_or_c_completion(self): template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B or C course passing award description", - group="a_or_b_or_c", + blend="a_or_b_or_c", ) requirement_b = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B or C course passing award description", - group="a_or_b_or_c", + blend="a_or_b_or_c", ) requirement_c = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B or C course passing award description", - group="a_or_b_or_c", + blend="a_or_b_or_c", ) DataRule.objects.create( requirement=requirement_a, @@ -491,7 +493,7 @@ def test_course_a_or_completion(self): template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or course passing award description", - group="a_or", + blend="a_or", ) DataRule.objects.create( requirement=requirement, @@ -508,13 +510,13 @@ def test_course_a_or_b_and_c_completion(self): template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_b = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_c = BadgeRequirement.objects.create( template=self.badge_template, @@ -560,25 +562,25 @@ def test_course_a_or_b_and_c_or_d_completion(self): template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_b = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="A or B course passing award description", - group="a_or_b", + blend="a_or_b", ) requirement_c = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="C or D course passing award description", - group="c_or_d", + blend="c_or_d", ) requirement_d = BadgeRequirement.objects.create( template=self.badge_template, event_type=COURSE_PASSING_EVENT, description="C or D course passing award description", - group="c_or_d", + blend="c_or_d", ) DataRule.objects.create( requirement=requirement_a, @@ -631,3 +633,90 @@ class TestIdentifyUser(TestCase): def test_identify_user(self): username = identify_user(event_type=COURSE_PASSING_EVENT, event_payload=COURSE_PASSING_DATA) self.assertEqual(username, "test_username") + + def test_identify_user_not_found(self): + event_type = "unknown_event_type" + event_payload = None + + with self.assertRaises(BadgesProcessingError) as cm: + identify_user(event_type="unknown_event_type", event_payload=event_payload) + + self.assertEqual( + str(cm.exception), + f"User data cannot be found (got: None): {event_payload}. " + f"Does event {event_type} include user data at all?", + ) + + +def mock_progress_regress(*args, **kwargs): + return None + + +class TestProcessEvent(TestCase): + def setUp(self): + self.organization = CredlyOrganization.objects.create( + uuid=uuid.uuid4(), api_key="test_api_key", name="test_organization" + ) + self.site = Site.objects.create(domain="test_domain", name="test_name") + self.badge_template = CredlyBadgeTemplate.objects.create( + uuid=uuid.uuid4(), + name="test_template", + state="draft", + site=self.site, + organization=self.organization, + is_active=True, + ) + DataRule.objects.create( + requirement=BadgeRequirement.objects.create(template=self.badge_template, event_type=COURSE_PASSING_EVENT), + data_path="is_passing", + operator="eq", + value="True", + ) + PenaltyDataRule.objects.create( + penalty=BadgePenalty.objects.create(template=self.badge_template, event_type=COURSE_PASSING_EVENT), + data_path="is_passing", + operator="eq", + value="False", + ) + self.sender = MagicMock() + self.sender.event_type = COURSE_PASSING_EVENT + + @patch.object(BadgeProgress, "progress", mock_progress_regress) + def test_process_event_passing(self): + event_payload = COURSE_PASSING_DATA + process_event(sender=self.sender, kwargs=event_payload) + self.assertTrue(BadgeProgress.for_user(username="test_username", template_id=self.badge_template.id).completed) + + def test_process_event_not_passing(self): + event_payload = CoursePassingStatusData( + is_passing=False, + course=CourseData(course_key=CourseKey.from_string("course-v1:edX+DemoX.1+2014"), display_name="A"), + user=UserData( + id=1, + is_active=True, + pii=UserPersonalData(username="test_username", email="test_email", name="John Doe"), + ), + ) + process_event(sender=self.sender, kwargs=event_payload) + self.assertFalse(BadgeProgress.for_user(username="test_username", template_id=self.badge_template.id).completed) + + @patch.object(BadgeProgress, "regress", mock_progress_regress) + def test_process_event_not_found(self): + sender = MagicMock() + sender.event_type = "unknown_event_type" + event_payload = None + + with patch("credentials.apps.badges.processing.generic.logger.error") as mock_event_not_found: + process_event(sender=sender, kwargs=event_payload) + mock_event_not_found.assert_called_once() + + def test_process_event_no_user_data(self): + event_payload = CoursePassingStatusData( + is_passing=True, + course=CourseData(course_key=CourseKey.from_string("course-v1:edX+DemoX.1+2014"), display_name="A"), + user=None, + ) + + with patch("credentials.apps.badges.processing.generic.logger.error") as mock_no_user_data: + process_event(sender=self.sender, kwargs=event_payload) + mock_no_user_data.assert_called_once() diff --git a/credentials/apps/badges/tests/test_signals.py b/credentials/apps/badges/tests/test_signals.py index 8971563f7c..0a79663b43 100644 --- a/credentials/apps/badges/tests/test_signals.py +++ b/credentials/apps/badges/tests/test_signals.py @@ -41,7 +41,7 @@ def test_progression_signal_emission_and_receiver_execution(self): self.assertTrue(user_credential.exists()) # Check if user credential status is 'awarded' - self.assertTrue(user_credential[0].status == "awarded") + self.assertEqual(user_credential[0].status, "awarded") def test_regression_signal_emission_and_receiver_execution(self): # Emit the signal @@ -64,4 +64,4 @@ def test_regression_signal_emission_and_receiver_execution(self): self.assertTrue(user_credential.exists()) # Check if user credential status is 'revoked' - self.assertTrue(user_credential[0].status == "revoked") + self.assertEqual(user_credential[0].status, "revoked") diff --git a/credentials/apps/badges/tests/test_utils.py b/credentials/apps/badges/tests/test_utils.py index f793b2373e..2d79f873c9 100644 --- a/credentials/apps/badges/tests/test_utils.py +++ b/credentials/apps/badges/tests/test_utils.py @@ -135,6 +135,15 @@ def test_badges_checks_non_empty_events(self, mock_get_badging_event_types): errors = badges_checks() self.assertEqual(len(errors), 0) + @patch("credentials.apps.badges.checks.credly_check") + def test_badges_checks_credly_not_configured(self, mock_credly_check): + mock_credly_check.return_value = False + errors = badges_checks() + self.assertEqual(len(errors), 1) + self.assertEqual(errors[0].msg, "Credly settings are not properly configured.") + self.assertEqual(errors[0].hint, "Make sure all required settings are present in BADGES_CONFIG['credly'].") + self.assertEqual(errors[0].id, "badges.E002") + class TestCredlyCheck(unittest.TestCase): def test_credly_configured(self): @@ -218,16 +227,16 @@ def test_get_credly_api_base_url_production(self): class TestGetEventTypeAttrTypeByKeypath(unittest.TestCase): def test_get_event_type_attr_type_by_keypath(self): - keypath = "course.course_key" - result = get_event_type_attr_type_by_keypath(COURSE_PASSING_EVENT, keypath) + key_path = "course.course_key" + result = get_event_type_attr_type_by_keypath(COURSE_PASSING_EVENT, key_path) self.assertEqual(result, CourseKey) def test_get_event_type_attr_type_by_keypath_bool(self): - keypath = "is_passing" - result = get_event_type_attr_type_by_keypath(COURSE_PASSING_EVENT, keypath) + key_path = "is_passing" + result = get_event_type_attr_type_by_keypath(COURSE_PASSING_EVENT, key_path) self.assertEqual(result, bool) def test_get_event_type_attr_type_by_keypath_not_found(self): - keypath = "course.id" - result = get_event_type_attr_type_by_keypath(COURSE_PASSING_EVENT, keypath) + key_path = "course.id" + result = get_event_type_attr_type_by_keypath(COURSE_PASSING_EVENT, key_path) self.assertIsNone(result) diff --git a/credentials/apps/badges/tests/test_webhooks.py b/credentials/apps/badges/tests/test_webhooks.py new file mode 100644 index 0000000000..db95b8d740 --- /dev/null +++ b/credentials/apps/badges/tests/test_webhooks.py @@ -0,0 +1,126 @@ +from unittest.mock import MagicMock, patch + +from django.test import TestCase +from django.test.client import RequestFactory +from faker import Faker + +from credentials.apps.badges.credly.api_client import CredlyAPIClient +from credentials.apps.badges.credly.webhooks import CredlyWebhook +from credentials.apps.badges.models import CredlyBadgeTemplate, CredlyOrganization + + +def mocked_handle_event(**kwargs): + return "test" + + +def get_organization(self, organization_id): # pylint: disable=unused-argument + organization = MagicMock(spec=CredlyOrganization) + organization.uuid = organization_id + organization.api_key = "test_api_key" + return organization + + +def perform_request(self, method, endpoint, data=None): # pylint: disable=unused-argument + return {"key": "value"} + + +class CredlyWebhookTestCase(TestCase): + def setUp(self): + self.rf = RequestFactory() + self.fake = Faker() + self.organization = CredlyOrganization.objects.create(uuid=self.fake.uuid4(), api_key="test_api_key") + + @patch.object(CredlyAPIClient, "_get_organization", get_organization) + @patch.object(CredlyAPIClient, "perform_request", perform_request) + def test_webhook_created_event(self): + with patch( + "credentials.apps.badges.credly.webhooks.CredlyWebhook.handle_badge_template_created_event" + ) as mock_handle: + req = self.rf.post( + "/credly/webhook/", + data={ + "id": self.fake.uuid4(), + "organization_id": self.organization.uuid, + "event_type": "badge_template.created", + "occurred_at": "2021-01-01T00:00:00Z", + }, + ) + res = CredlyWebhook.as_view()(req) + self.assertEqual(res.status_code, 204) + mock_handle.assert_called_once() + + @patch.object(CredlyAPIClient, "_get_organization", get_organization) + @patch.object(CredlyAPIClient, "perform_request", perform_request) + def test_webhook_changed_event(self): + with patch( + "credentials.apps.badges.credly.webhooks.CredlyWebhook.handle_badge_template_changed_event" + ) as mock_handle: + req = self.rf.post( + "/credly/webhook/", + data={ + "id": self.fake.uuid4(), + "organization_id": self.organization.uuid, + "event_type": "badge_template.changed", + "occurred_at": "2021-01-01T00:00:00Z", + }, + ) + res = CredlyWebhook.as_view()(req) + self.assertEqual(res.status_code, 204) + mock_handle.assert_called_once() + + @patch.object(CredlyAPIClient, "_get_organization", get_organization) + @patch.object(CredlyAPIClient, "perform_request", perform_request) + def test_webhook_deleted_event(self): + with patch( + "credentials.apps.badges.credly.webhooks.CredlyWebhook.handle_badge_template_deleted_event" + ) as mock_handle: + req = self.rf.post( + "/credly/webhook/", + data={ + "id": self.fake.uuid4(), + "organization_id": self.fake.uuid4(), + "event_type": "badge_template.deleted", + "occurred_at": "2021-01-01T00:00:00Z", + }, + ) + res = CredlyWebhook.as_view()(req) + self.assertEqual(res.status_code, 204) + mock_handle.assert_called_once() + + @patch.object(CredlyAPIClient, "_get_organization", get_organization) + @patch.object(CredlyAPIClient, "perform_request", perform_request) + def test_webhook_nonexistent_event(self): + with patch("credentials.apps.badges.credly.webhooks.logger.error") as mock_handle: + req = self.rf.post( + "/credly/webhookd/", + data={ + "id": self.fake.uuid4(), + "organization_id": self.fake.uuid4(), + "event_type": "unknown_event", + "occurred_at": "2021-01-01T00:00:00Z", + }, + ) + CredlyWebhook.as_view()(req) + mock_handle.assert_called_once() + + def test_handle_badge_template_deleted_event(self): + request_data = { + "organization_id": "test_organization_id", + "id": "test_event_id", + "event_type": "badge_template.deleted", + "data": { + "badge_template": { + "id": self.fake.uuid4(), + "owner": {"id": self.fake.uuid4()}, + "name": "Test Template", + "state": "active", + "description": "Test Description", + "image_url": "http://example.com/image.png", + } + }, + } + request = self.rf.post("/credly/webhook/", data=request_data) + + CredlyWebhook.handle_badge_template_deleted_event(request, request_data) + + self.assertEqual(CredlyBadgeTemplate.objects.count(), 0) diff --git a/credentials/apps/badges/toggles.py b/credentials/apps/badges/toggles.py index bd54217ded..b82d510d5f 100644 --- a/credentials/apps/badges/toggles.py +++ b/credentials/apps/badges/toggles.py @@ -32,5 +32,6 @@ def check_badges_enabled(func): def wrapper(*args, **kwargs): if is_badges_enabled(): return func(*args, **kwargs) + return None return wrapper diff --git a/credentials/apps/badges/utils.py b/credentials/apps/badges/utils.py index fc1ad8f6d2..719ae66ea5 100644 --- a/credentials/apps/badges/utils.py +++ b/credentials/apps/badges/utils.py @@ -1,4 +1,5 @@ import inspect +from typing import Union import attr from attrs import asdict @@ -29,7 +30,7 @@ def credly_check(): "CREDLY_SANDBOX_API_BASE_URL", "USE_SANDBOX", ) - return all([key in credly_settings.keys() for key in keys]) + return all(key in credly_settings.keys() for key in keys) def keypath(payload, keys_path): @@ -100,7 +101,7 @@ def get_user_data(data: attr.s) -> UserData: return None -def extract_payload(public_signal_kwargs: dict) -> attr.s: +def extract_payload(public_signal_kwargs: dict) -> Union[None, attr.s]: """ Extracts the event payload from the event data. @@ -113,6 +114,7 @@ def extract_payload(public_signal_kwargs: dict) -> attr.s: for value in public_signal_kwargs.values(): if attr.has(value): return value + return None def get_event_type_data(event_type: str) -> attr.s: @@ -169,13 +171,13 @@ def get_data_keypaths(data): return keypaths -def get_event_type_attr_type_by_keypath(event_type: str, keypath: str): +def get_event_type_attr_type_by_keypath(event_type: str, key_path: str): """ Extracts the attribute type for a given keypath in the event type. Parameters: - event_type: The event type to extract dataclass for. - - keypath: The keypath to extract attribute type for. + - key_path: The keypath to extract attribute type for. Returns: type: The attribute type for the given keypath in the event data. @@ -184,12 +186,12 @@ def get_event_type_attr_type_by_keypath(event_type: str, keypath: str): data = get_event_type_data(event_type) data_attrs = attr.fields(data) - def get_attr_type_by_keypath(data_attrs, keypath): + def get_attr_type_by_keypath(data_attrs, key_path): """ Extracts the attribute type for a given keypath in the dataclass. """ - keypath_parts = keypath.split(".") + keypath_parts = key_path.split(".") for attr_ in data_attrs: if attr_.name == keypath_parts[0]: if len(keypath_parts) == 1: @@ -198,4 +200,4 @@ def get_attr_type_by_keypath(data_attrs, keypath): return get_attr_type_by_keypath(attr.fields(attr_.type), ".".join(keypath_parts[1:])) return None - return get_attr_type_by_keypath(data_attrs, keypath) + return get_attr_type_by_keypath(data_attrs, key_path) diff --git a/credentials/conf/locale/eo/LC_MESSAGES/django.mo b/credentials/conf/locale/eo/LC_MESSAGES/django.mo index c1f43af035..66074e39aa 100644 Binary files a/credentials/conf/locale/eo/LC_MESSAGES/django.mo and b/credentials/conf/locale/eo/LC_MESSAGES/django.mo differ diff --git a/credentials/conf/locale/eo/LC_MESSAGES/django.po b/credentials/conf/locale/eo/LC_MESSAGES/django.po index cec0f618d8..190bb2654f 100644 --- a/credentials/conf/locale/eo/LC_MESSAGES/django.po +++ b/credentials/conf/locale/eo/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2024-05-27 10:13+0000\n" -"PO-Revision-Date: 2024-05-27 10:13:41.472759\n" +"POT-Creation-Date: 2023-06-13 08:00+0000\n" +"PO-Revision-Date: 2023-06-13 09:00+0000\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "Language: eo\n" @@ -41,15 +41,15 @@ msgstr "" msgid "" "\n" " WARNING: avoid configuration updates on activated badges.\n" -" Active badge templates are continuously processed and learners may already have partial progress on them.\n" +" Active badge templates are continuously processed and learners may already have progress on them.\n" " Any changes in badge template requirements (including data rules) will affect learners' experience!\n" " " msgstr "" "\n" " WÀRNÌNG: ävöïd çönfïgürätïön üpdätés ön äçtïvätéd ßädgés.\n" -" Àçtïvé ßädgé témplätés äré çöntïnüöüslý pröçésséd änd léärnérs mäý älréädý hävé pärtïäl prögréss ön thém.\n" +" Àçtïvé ßädgé témplätés äré çöntïnüöüslý pröçésséd änd léärnérs mäý älréädý hävé prögréss ön thém.\n" " Àný çhängés ïn ßädgé témpläté réqüïréménts (ïnçlüdïng dätä rülés) wïll äfféçt léärnérs' éxpérïénçé!\n" -" Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕ#" +" Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υ#" #: apps/badges/admin.py msgid "Active badge template cannot be deleted." @@ -171,11 +171,15 @@ msgstr "Prövïdé möré détäïls ïf néédéd. Ⱡ'σяєм ιρѕυм ∂ #: apps/badges/models.py msgid "" -"Optional. Put requirements into the same arbitrary Group ID to make them " -"interchangeable (OR processing logic applies)." +"Optional. Group requirements together using the same Group ID for " +"interchangeable (OR processing logic)." msgstr "" -"Öptïönäl. Püt réqüïréménts ïntö thé sämé ärßïträrý Gröüp ÌD tö mäké thém " -"ïntérçhängéäßlé (ÖR pröçéssïng lögïç äpplïés). Ⱡ'σяєм ιρѕυм#" +"Öptïönäl. Gröüp réqüïréménts tögéthér üsïng thé sämé Gröüp ÌD för " +"ïntérçhängéäßlé (ÖR pröçéssïng lögïç). Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт α#" + +#: apps/badges/models.py +msgid "group" +msgstr "gröüp Ⱡ'σяєм ιρѕ#" #: apps/badges/models.py msgid "" diff --git a/credentials/conf/locale/eo/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/eo/LC_MESSAGES/djangojs.mo index 3afa929937..96571b0e3f 100644 Binary files a/credentials/conf/locale/eo/LC_MESSAGES/djangojs.mo and b/credentials/conf/locale/eo/LC_MESSAGES/djangojs.mo differ diff --git a/credentials/conf/locale/eo/LC_MESSAGES/djangojs.po b/credentials/conf/locale/eo/LC_MESSAGES/djangojs.po index f5b4254501..69b8ed15c0 100644 --- a/credentials/conf/locale/eo/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/eo/LC_MESSAGES/djangojs.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2024-05-27 10:13+0000\n" -"PO-Revision-Date: 2024-05-27 10:13:41.467627\n" +"POT-Creation-Date: 2023-06-13 08:00+0000\n" +"PO-Revision-Date: 2023-06-13 09:00+0000\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "Language: eo\n" diff --git a/credentials/conf/locale/rtl/LC_MESSAGES/django.mo b/credentials/conf/locale/rtl/LC_MESSAGES/django.mo index ef95f7d353..cca532ada8 100644 Binary files a/credentials/conf/locale/rtl/LC_MESSAGES/django.mo and b/credentials/conf/locale/rtl/LC_MESSAGES/django.mo differ diff --git a/credentials/conf/locale/rtl/LC_MESSAGES/django.po b/credentials/conf/locale/rtl/LC_MESSAGES/django.po index 827b67d035..469f79ecd7 100644 --- a/credentials/conf/locale/rtl/LC_MESSAGES/django.po +++ b/credentials/conf/locale/rtl/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2024-05-27 10:13+0000\n" -"PO-Revision-Date: 2024-05-27 10:13:41.472759\n" +"POT-Creation-Date: 2023-06-13 08:00+0000\n" +"PO-Revision-Date: 2023-06-13 09:00+0000\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "Language: rtl\n" @@ -37,13 +37,13 @@ msgstr "Ᵽɹǝ-ɔønɟᴉƃnɹǝd ɟɹøɯ ʇɥǝ ǝnʌᴉɹønɯǝnʇ." msgid "" "\n" " WARNING: avoid configuration updates on activated badges.\n" -" Active badge templates are continuously processed and learners may already have partial progress on them.\n" +" Active badge templates are continuously processed and learners may already have progress on them.\n" " Any changes in badge template requirements (including data rules) will affect learners' experience!\n" " " msgstr "" "\n" " WȺɌNƗNǤ: ɐʌøᴉd ɔønɟᴉƃnɹɐʇᴉøn nddɐʇǝs øn ɐɔʇᴉʌɐʇǝd bɐdƃǝs.\n" -" Ⱥɔʇᴉʌǝ bɐdƃǝ ʇǝɯdlɐʇǝs ɐɹǝ ɔønʇᴉnnønslʎ dɹøɔǝssǝd ɐnd lǝɐɹnǝɹs ɯɐʎ ɐlɹǝɐdʎ ɥɐʌǝ dɐɹʇᴉɐl dɹøƃɹǝss øn ʇɥǝɯ.\n" +" Ⱥɔʇᴉʌǝ bɐdƃǝ ʇǝɯdlɐʇǝs ɐɹǝ ɔønʇᴉnnønslʎ dɹøɔǝssǝd ɐnd lǝɐɹnǝɹs ɯɐʎ ɐlɹǝɐdʎ ɥɐʌǝ dɹøƃɹǝss øn ʇɥǝɯ.\n" " Ⱥnʎ ɔɥɐnƃǝs ᴉn bɐdƃǝ ʇǝɯdlɐʇǝ ɹǝbnᴉɹǝɯǝnʇs (ᴉnɔlndᴉnƃ dɐʇɐ ɹnlǝs) ʍᴉll ɐɟɟǝɔʇ lǝɐɹnǝɹs' ǝxdǝɹᴉǝnɔǝ!\n" " " @@ -137,11 +137,15 @@ msgstr "Ᵽɹøʌᴉdǝ ɯøɹǝ dǝʇɐᴉls ᴉɟ nǝǝdǝd." #: apps/badges/models.py msgid "" -"Optional. Put requirements into the same arbitrary Group ID to make them " -"interchangeable (OR processing logic applies)." +"Optional. Group requirements together using the same Group ID for " +"interchangeable (OR processing logic)." msgstr "" -"Ødʇᴉønɐl. Ᵽnʇ ɹǝbnᴉɹǝɯǝnʇs ᴉnʇø ʇɥǝ sɐɯǝ ɐɹbᴉʇɹɐɹʎ Ǥɹønd ƗĐ ʇø ɯɐʞǝ ʇɥǝɯ " -"ᴉnʇǝɹɔɥɐnƃǝɐblǝ (ØɌ dɹøɔǝssᴉnƃ løƃᴉɔ ɐddlᴉǝs)." +"Ødʇᴉønɐl. Ǥɹønd ɹǝbnᴉɹǝɯǝnʇs ʇøƃǝʇɥǝɹ nsᴉnƃ ʇɥǝ sɐɯǝ Ǥɹønd ƗĐ ɟøɹ " +"ᴉnʇǝɹɔɥɐnƃǝɐblǝ (ØɌ dɹøɔǝssᴉnƃ løƃᴉɔ)." + +#: apps/badges/models.py +msgid "group" +msgstr "ƃɹønd" #: apps/badges/models.py msgid "" diff --git a/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.mo b/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.mo index a3736585dd..71d10356e7 100644 Binary files a/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.mo and b/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.mo differ diff --git a/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.po b/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.po index 44cfc17af7..011e6c3911 100644 --- a/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.po +++ b/credentials/conf/locale/rtl/LC_MESSAGES/djangojs.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2024-05-27 10:13+0000\n" -"PO-Revision-Date: 2024-05-27 10:13:41.467627\n" +"POT-Creation-Date: 2023-06-13 08:00+0000\n" +"PO-Revision-Date: 2023-06-13 09:00+0000\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "Language: rtl\n" diff --git a/credentials/settings/production.py b/credentials/settings/production.py index 79c4c5f674..74c644a561 100644 --- a/credentials/settings/production.py +++ b/credentials/settings/production.py @@ -63,5 +63,3 @@ for override, value in DB_OVERRIDES.items(): DATABASES["default"][override] = value - -add_plugins(__name__, PROJECT_TYPE, SettingsType.PRODUCTION) diff --git a/docs/_static/images/badges/badges-award-sequence.png b/docs/_static/images/badges/badges-award-sequence.png deleted file mode 100644 index 08e77bc76b..0000000000 Binary files a/docs/_static/images/badges/badges-award-sequence.png and /dev/null differ diff --git a/docs/_static/images/badges/badges-revoke-sequence.png b/docs/_static/images/badges/badges-revoke-sequence.png deleted file mode 100644 index eadceae5e9..0000000000 Binary files a/docs/_static/images/badges/badges-revoke-sequence.png and /dev/null differ diff --git a/docs/_static/images/badges/badges-setup-sequence.png b/docs/_static/images/badges/badges-setup-sequence.png deleted file mode 100644 index 69b1cb59e2..0000000000 Binary files a/docs/_static/images/badges/badges-setup-sequence.png and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 21d032a527..217d3d2bad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,19 +13,19 @@ "css-minimizer-webpack-plugin": "6.0.0", "file-loader": "6.2.0", "mini-css-extract-plugin": "2.9.0", - "sass": "1.77.2", + "sass": "1.77.4", "sass-loader": "14.2.1", "url-loader": "4.1.1", "webpack": "5.91.0", "webpack-bundle-tracker": "3.1.0" }, "devDependencies": { - "@babel/core": "7.24.5", - "@babel/eslint-parser": "7.24.5", + "@babel/core": "7.24.6", + "@babel/eslint-parser": "7.24.6", "@babel/plugin-proposal-object-rest-spread": "7.20.7", - "@babel/plugin-transform-modules-commonjs": "7.24.1", - "@babel/plugin-transform-object-assign": "7.24.1", - "@babel/preset-env": "7.24.5", + "@babel/plugin-transform-modules-commonjs": "7.24.6", + "@babel/plugin-transform-object-assign": "7.24.6", + "@babel/preset-env": "7.24.6", "@edx/eslint-config": "1.3.0", "babel-loader": "9.1.3", "eslint": "8.57.0", @@ -66,12 +66,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.6", "picocolors": "^1.0.0" }, "engines": { @@ -79,30 +80,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.6.tgz", + "integrity": "sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", - "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.6.tgz", + "integrity": "sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.24.5", - "@babel/helpers": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5", + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helpers": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/template": "^7.24.6", + "@babel/traverse": "^7.24.6", + "@babel/types": "^7.24.6", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -118,10 +121,11 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.5.tgz", - "integrity": "sha512-gsUcqS/fPlgAw1kOtpss7uhY6E9SFFANQ6EFX5GTvzUwaV0+sGaZWk6xq22MOdeT9wfxyokW3ceCUvOiRtZciQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.6.tgz", + "integrity": "sha512-Q1BfQX42zXHx732PLW0w4+Y3wJjoZKEMaatFUEAmQ7Z+jCXxinzeqX9bvv2Q8xNPes/H6F0I23oGkcgjaItmLw==", "dev": true, + "license": "MIT", "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", @@ -136,12 +140,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", - "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", + "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5", + "@babel/types": "^7.24.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -151,37 +156,40 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.6.tgz", + "integrity": "sha512-DitEzDfOMnd13kZnDqns1ccmftwJTS9DMkyn9pYTxulS7bZxUxpMly3Nf23QQ6NwA4UB8lAqjbqWtyvElEMAkg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.6.tgz", + "integrity": "sha512-+wnfqc5uHiMYtvRX7qu80Toef8BXeh4HHR1SPeonGb1SKPniNEd4a/nlaJJMv/OIEYvIVavvo0yR7u10Gqz0Iw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz", + "integrity": "sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", + "@babel/compat-data": "^7.24.6", + "@babel/helper-validator-option": "^7.24.6", "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -191,19 +199,20 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.5.tgz", - "integrity": "sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.24.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.6.tgz", + "integrity": "sha512-djsosdPJVZE6Vsw3kk7IPRWethP94WHGOhQTc67SNXE0ZzMhHgALw8iGmYS0TD1bbMM0VDROy43od7/hN6WYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-member-expression-to-functions": "^7.24.6", + "@babel/helper-optimise-call-expression": "^7.24.6", + "@babel/helper-replace-supers": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", "semver": "^6.3.1" }, "engines": { @@ -214,12 +223,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.6.tgz", + "integrity": "sha512-C875lFBIWWwyv6MHZUG9HmRrlTDgOsLWZfYR0nW69gaKJNe0/Mpxx5r0EID2ZdHQkdUmQo2t0uNckTL08/1BgA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-annotate-as-pure": "^7.24.6", "regexpu-core": "^5.3.1", "semver": "^6.3.1" }, @@ -247,74 +257,80 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz", + "integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz", + "integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz", + "integrity": "sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.5.tgz", - "integrity": "sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.6.tgz", + "integrity": "sha512-OTsCufZTxDUsv2/eDXanw/mUZHWOxSbEmC3pP8cgjcy5rgeVPWWMStnv274DV60JtHxTk0adT0QrCzC4M9NWGg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz", + "integrity": "sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.0" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", - "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz", + "integrity": "sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.24.3", - "@babel/helper-simple-access": "^7.24.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/helper-validator-identifier": "^7.24.5" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-module-imports": "^7.24.6", + "@babel/helper-simple-access": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -324,35 +340,38 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.6.tgz", + "integrity": "sha512-3SFDJRbx7KuPRl8XDUr8O7GAEB8iGyWPjLKJh/ywP/Iy9WOmEfMrsWbaZpvBu2HSYn4KQygIsz0O7m8y10ncMA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", - "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz", + "integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.6.tgz", + "integrity": "sha512-1Qursq9ArRZPAMOZf/nuzVW8HgJLkTB9y9LfP4lW2MVp4e9WkLJDovfKBxoDcCk6VuzIxyqWHyBoaCtSRP10yg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-wrap-function": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -362,14 +381,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", - "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.6.tgz", + "integrity": "sha512-mRhfPwDqDpba8o1F8ESxsEkJMQkUF8ZIWrAc0FtWhxnjfextxMWxr22RtFizxxSYLjVHDeMgVsRq8BBZR2ikJQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-member-expression-to-functions": "^7.24.6", + "@babel/helper-optimise-call-expression": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -379,103 +399,111 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", - "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz", + "integrity": "sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.6.tgz", + "integrity": "sha512-jhbbkK3IUKc4T43WadP96a27oYti9gEf1LdyGSP2rHGH77kwLwfhO7TgwnWvxxQVmke0ImmCSS47vcuxEMGD3Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", - "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", + "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz", + "integrity": "sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.6.tgz", + "integrity": "sha512-f1JLrlw/jbiNfxvdrfBgio/gRBk3yTAEJWirpAkiJG2Hb22E7cEYKHWo0dFPTv/niPovzIdPdEDetrv6tC6gPQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "@babel/helper-function-name": "^7.24.6", + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", - "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.6.tgz", + "integrity": "sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5" + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.6", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -485,10 +513,11 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", - "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", "dev": true, + "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -497,13 +526,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.5.tgz", - "integrity": "sha512-LdXRi1wEMTrHVR4Zc9F8OewC3vdm5h4QB6L71zy6StmYeqGi1b3ttIO8UC+BfZKcH9jdr4aI249rBkm+3+YvHw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.6.tgz", + "integrity": "sha512-bYndrJ6Ph6Ar+GaB5VAc0JPoP80bQCm4qon6JEzXfRl5QZyQ8Ur1K6k7htxWmPA5z+k7JQvaMUrtXlqclWYzKw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -513,12 +543,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", - "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.6.tgz", + "integrity": "sha512-iVuhb6poq5ikqRq2XWU6OQ+R5o9wF+r/or9CeUyovgptz0UlnK4/seOQ1Istu/XybYjAhQv1FRSSfHHufIku5Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -528,14 +559,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", - "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.6.tgz", + "integrity": "sha512-c8TER5xMDYzzFcGqOEp9l4hvB7dcbhcGjcLVwxWfe4P5DOafdwjsBJZKsmv+o3aXh7NhopvayQIovHrh2zSRUQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6", + "@babel/plugin-transform-optional-chaining": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -545,13 +577,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", - "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.6.tgz", + "integrity": "sha512-z8zEjYmwBUHN/pCF3NuWBhHQjJCrd33qAi8MgANfMrAvn72k2cImT8VjK9LJFu4ysOLJqhfkYYb3MvwANRUNZQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -655,12 +688,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", - "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.6.tgz", + "integrity": "sha512-BE6o2BogJKJImTmGpkmOic4V0hlRRxVtzqxiSPa8TIFxyhi4EFjHm08nq1M4STK4RytuLMgnSz0/wfflvGFNOg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -670,12 +704,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", - "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.6.tgz", + "integrity": "sha512-D+CfsVZousPXIdudSII7RGy52+dYRtbyKAZcvtQKq/NpsivyMVduepzcLqG5pMBugtMdedxdC8Ramdpcne9ZWQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -827,12 +862,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", - "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.6.tgz", + "integrity": "sha512-jSSSDt4ZidNMggcLx8SaKsbGNEfIl0PHx/4mFEulorE7bpYLbN0d3pDW3eJ7Y5Z3yPhy3L3NaPCYyTUY7TuugQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -842,14 +878,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", - "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.6.tgz", + "integrity": "sha512-VEP2o4iR2DqQU6KPgizTW2mnMx6BG5b5O9iQdrW9HesLkv8GIA8x2daXBQxw1MrsIkFQGA/iJ204CKoQ8UcnAA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-remap-async-to-generator": "^7.24.6", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -860,14 +897,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", - "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.6.tgz", + "integrity": "sha512-NTBA2SioI3OsHeIn6sQmhvXleSl9T70YY/hostQLveWs0ic+qvbA3fa0kwAwQ0OA/XGaAerNZRQGJyRfhbJK4g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-remap-async-to-generator": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -877,12 +915,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", - "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.6.tgz", + "integrity": "sha512-XNW7jolYHW9CwORrZgA/97tL/k05qe/HL0z/qqJq1mdWhwwCM6D4BJBV7wAz9HgFziN5dTOG31znkVIzwxv+vw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -892,12 +931,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.5.tgz", - "integrity": "sha512-sMfBc3OxghjC95BkYrYocHL3NaOplrcaunblzwXhGmlPwpmfsxr4vK+mBBt49r+S240vahmv+kUxkeKgs+haCw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.6.tgz", + "integrity": "sha512-S/t1Xh4ehW7sGA7c1j/hiOBLnEYCp/c2sEG4ZkL8kI1xX9tW2pqJTCHKtdhe/jHKt8nG0pFCrDHUXd4DvjHS9w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -907,13 +947,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", - "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.6.tgz", + "integrity": "sha512-j6dZ0Z2Z2slWLR3kt9aOmSIrBvnntWjMDN/TVcMPxhXMLmJVqX605CBRlcGI4b32GMbfifTEsdEjGjiE+j/c3A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -923,13 +964,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", - "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.6.tgz", + "integrity": "sha512-1QSRfoPI9RoLRa8Mnakc6v3e0gJxiZQTYrMfLn+mD0sz5+ndSzwymp2hDcYJTyT0MOn0yuWzj8phlIvO72gTHA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.4", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -940,18 +982,19 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.5.tgz", - "integrity": "sha512-gWkLP25DFj2dwe9Ck8uwMOpko4YsqyfZJrOmqqcegeDYEbp7rmn4U6UQZNj08UF6MaX39XenSpKRCvpDRBtZ7Q==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.24.5", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.6.tgz", + "integrity": "sha512-+fN+NO2gh8JtRmDSOB6gaCVo36ha8kfCW1nMq2Gc0DABln0VcHN4PrALDvF5/diLzIRKptC7z/d7Lp64zk92Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-replace-supers": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", "globals": "^11.1.0" }, "engines": { @@ -962,13 +1005,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", - "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.6.tgz", + "integrity": "sha512-cRzPobcfRP0ZtuIEkA8QzghoUpSB3X3qSH5W2+FzG+VjWbJXExtx0nbRqwumdBN1x/ot2SlTNQLfBCnPdzp6kg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/template": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/template": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -978,12 +1022,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.5.tgz", - "integrity": "sha512-SZuuLyfxvsm+Ah57I/i1HVjveBENYK9ue8MJ7qkc7ndoNjqquJiElzA7f5yaAXjyW2hKojosOTAQQRX50bPSVg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.6.tgz", + "integrity": "sha512-YLW6AE5LQpk5npNXL7i/O+U9CE4XsBCuRPgyjl1EICZYKmcitV+ayuuUGMJm2lC1WWjXYszeTnIxF/dq/GhIZQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -993,13 +1038,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", - "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.6.tgz", + "integrity": "sha512-rCXPnSEKvkm/EjzOtLoGvKseK+dS4kZwx1HexO3BtRtgL0fQ34awHn34aeSHuXtZY2F8a1X8xqBBPRtOxDVmcA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1009,12 +1055,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", - "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.6.tgz", + "integrity": "sha512-/8Odwp/aVkZwPFJMllSbawhDAO3UJi65foB00HYnK/uXvvCPm0TAXSByjz1mpRmp0q6oX2SIxpkUOpPFHk7FLA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1024,12 +1071,13 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", - "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.6.tgz", + "integrity": "sha512-vpq8SSLRTBLOHUZHSnBqVo0AKX3PBaoPs2vVzYVWslXDTDIpwAcCDtfhUcHSQQoYoUvcFPTdC8TZYXu9ZnLT/w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -1040,13 +1088,14 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", - "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.6.tgz", + "integrity": "sha512-EemYpHtmz0lHE7hxxxYEuTYOOBZ43WkDgZ4arQ4r+VX9QHuNZC+WH3wUWmRNvR8ECpTRne29aZV6XO22qpOtdA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1056,12 +1105,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", - "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.6.tgz", + "integrity": "sha512-inXaTM1SVrIxCkIJ5gqWiozHfFMStuGbGJAxZFBoHcRRdDP0ySLb3jH6JOwmfiinPwyMZqMBX+7NBDCO4z0NSA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -1072,13 +1122,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", - "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.6.tgz", + "integrity": "sha512-n3Sf72TnqK4nw/jziSqEl1qaWPbCRw2CziHH+jdRYvw4J6yeCzsj4jdw8hIntOEeDGTmHVe2w4MVL44PN0GMzg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1088,14 +1139,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", - "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.6.tgz", + "integrity": "sha512-sOajCu6V0P1KPljWHKiDq6ymgqB+vfo3isUS4McqW1DZtvSVU2v/wuMhmRmkg3sFoq6GMaUUf8W4WtoSLkOV/Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1105,12 +1157,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", - "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.6.tgz", + "integrity": "sha512-Uvgd9p2gUnzYJxVdBLcU0KurF8aVhkmVyMKW4MIY1/BByvs3EBpv45q01o7pRTVmTvtQq5zDlytP3dcUgm7v9w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -1121,12 +1174,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", - "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.6.tgz", + "integrity": "sha512-f2wHfR2HF6yMj+y+/y07+SLqnOSwRp8KYLpQKOzS58XLVlULhXbiYcygfXQxJlMbhII9+yXDwOUFLf60/TL5tw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1136,12 +1190,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", - "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.6.tgz", + "integrity": "sha512-EKaWvnezBCMkRIHxMJSIIylzhqK09YpiJtDbr2wsXTwnO0TxyjMUkaw4RlFIZMIS0iDj0KyIg7H7XCguHu/YDA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -1152,12 +1207,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", - "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.6.tgz", + "integrity": "sha512-9g8iV146szUo5GWgXpRbq/GALTnY+WnNuRTuRHWWFfWGbP9ukRL0aO/jpu9dmOPikclkxnNsjY8/gsWl6bmZJQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1167,13 +1223,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", - "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.6.tgz", + "integrity": "sha512-eAGogjZgcwqAxhyFgqghvoHRr+EYRQPFjUXrTYKBRb5qPnAVxOOglaxc4/byHqjvq/bqO2F3/CGwTHsgKJYHhQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1183,14 +1240,15 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", - "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.6.tgz", + "integrity": "sha512-JEV8l3MHdmmdb7S7Cmx6rbNEjRCgTQMZxllveHO0mx6uiclB0NflCawlQQ6+o5ZrwjUBYPzHm2XoK4wqGVUFuw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-simple-access": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1200,15 +1258,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", - "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.6.tgz", + "integrity": "sha512-xg1Z0J5JVYxtpX954XqaaAT6NpAY6LtZXvYFCJmGFJWwtlz2EmJoR8LycFRGNE8dBKizGWkGQZGegtkV8y8s+w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1218,13 +1277,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", - "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.6.tgz", + "integrity": "sha512-esRCC/KsSEUvrSjv5rFYnjZI6qv4R1e/iHQrqwbZIoRJqk7xCvEUiN7L1XrmW5QSmQe3n1XD88wbgDTWLbVSyg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1234,13 +1294,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.6.tgz", + "integrity": "sha512-6DneiCiu91wm3YiNIGDWZsl6GfTTbspuj/toTEqLh9d4cx50UIzSdg+T96p8DuT7aJOBRhFyaE9ZvTHkXrXr6Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1250,12 +1311,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", - "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.6.tgz", + "integrity": "sha512-f8liz9JG2Va8A4J5ZBuaSdwfPqN6axfWRK+y66fjKYbwf9VBLuq4WxtinhJhvp1w6lamKUwLG0slK2RxqFgvHA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1265,12 +1327,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", - "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.6.tgz", + "integrity": "sha512-+QlAiZBMsBK5NqrBWFXCYeXyiU1y7BQ/OYaiPAcQJMomn5Tyg+r5WuVtyEuvTbpV7L25ZSLfE+2E9ywj4FD48A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -1281,12 +1344,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", - "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.6.tgz", + "integrity": "sha512-6voawq8T25Jvvnc4/rXcWZQKKxUNZcKMS8ZNrjxQqoRFernJJKjE3s18Qo6VFaatG5aiX5JV1oPD7DbJhn0a4Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -1297,12 +1361,13 @@ } }, "node_modules/@babel/plugin-transform-object-assign": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.24.1.tgz", - "integrity": "sha512-I1kctor9iKtupb7jv7FyjApHCuKLBKCblVAeHVK9PB6FW7GI0ac6RtobC3MwwJy8CZ1JxuhQmnbrsqI5G8hAIg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.24.6.tgz", + "integrity": "sha512-VrcQRUBGFnn63I4gtzxOLEzXVTTYKACzTp7Kh7LPYV+ygWJPldUS9QFtNriPwTww9vQuteStX17ouZg1xNIwtg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1312,15 +1377,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.5.tgz", - "integrity": "sha512-7EauQHszLGM3ay7a161tTQH7fj+3vVM/gThlz5HpFtnygTxjrlvoeq7MPVA1Vy9Q555OB8SnAOsMkLShNkkrHA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.6.tgz", + "integrity": "sha512-OKmi5wiMoRW5Smttne7BwHM8s/fb5JFs+bVGNSeHWzwZkWXWValR1M30jyXo1s/RaqgwwhEC62u4rFH/FBcBPg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.5", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.5" + "@babel/plugin-transform-parameters": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1330,13 +1396,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", - "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.6.tgz", + "integrity": "sha512-N/C76ihFKlZgKfdkEYKtaRUtXZAgK7sOY4h2qrbVbVTXPrKGIi8aww5WGe/+Wmg8onn8sr2ut6FXlsbu/j6JHg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-replace-supers": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1346,12 +1413,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", - "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.6.tgz", + "integrity": "sha512-L5pZ+b3O1mSzJ71HmxSCmTVd03VOT2GXOigug6vDYJzE5awLI7P1g0wFcdmGuwSDSrQ0L2rDOe/hHws8J1rv3w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -1362,13 +1430,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.5.tgz", - "integrity": "sha512-xWCkmwKT+ihmA6l7SSTpk8e4qQl/274iNbSKRRS8mpqFR32ksy36+a+LWY8OXCCEefF8WFlnOHVsaDI2231wBg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.6.tgz", + "integrity": "sha512-cHbqF6l1QP11OkYTYQ+hhVx1E017O5ZcSPXk9oODpqhcAD1htsWG2NpHrrhthEO2qZomLK0FXS+u7NfrkF5aOQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -1379,12 +1448,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.5.tgz", - "integrity": "sha512-9Co00MqZ2aoky+4j2jhofErthm6QVLKbpQrvz20c3CH9KQCLHyNB+t2ya4/UrRpQGR+Wrwjg9foopoeSdnHOkA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.6.tgz", + "integrity": "sha512-ST7guE8vLV+vI70wmAxuZpIKzVjvFX9Qs8bl5w6tN/6gOypPWUmMQL2p7LJz5E63vEGrDhAiYetniJFyBH1RkA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1394,13 +1464,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", - "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.6.tgz", + "integrity": "sha512-T9LtDI0BgwXOzyXrvgLTT8DFjCC/XgWLjflczTLXyvxbnSR/gpv0hbmzlHE/kmh9nOvlygbamLKRo6Op4yB6aw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1410,14 +1481,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.5.tgz", - "integrity": "sha512-JM4MHZqnWR04jPMujQDTBVRnqxpLLpx2tkn7iPn+Hmsc0Gnb79yvRWOkvqFOx3Z7P7VxiRIR22c4eGSNj87OBQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.6.tgz", + "integrity": "sha512-Qu/ypFxCY5NkAnEhCF86Mvg3NSabKsh/TPpBVswEdkGl7+FbsYHy1ziRqJpwGH4thBdQHh8zx+z7vMYmcJ7iaQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.5", - "@babel/helper-plugin-utils": "^7.24.5", + "@babel/helper-annotate-as-pure": "^7.24.6", + "@babel/helper-create-class-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -1428,12 +1500,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", - "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.6.tgz", + "integrity": "sha512-oARaglxhRsN18OYsnPTpb8TcKQWDYNsPNmTnx5++WOAsUJ0cSC/FZVlIJCKvPbU4yn/UXsS0551CFKJhN0CaMw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1443,12 +1516,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", - "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.6.tgz", + "integrity": "sha512-SMDxO95I8WXRtXhTAc8t/NFQUT7VYbIWwJCJgEli9ml4MhqUMh4S6hxgH6SmAC3eAQNWCDJFxcFeEt9w2sDdXg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.6", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1459,12 +1533,13 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", - "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.6.tgz", + "integrity": "sha512-DcrgFXRRlK64dGE0ZFBPD5egM2uM8mgfrvTMOSB2yKzOtjpGegVYkzh3s1zZg1bBck3nkXiaOamJUqK3Syk+4A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1474,12 +1549,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", - "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.6.tgz", + "integrity": "sha512-xnEUvHSMr9eOWS5Al2YPfc32ten7CXdH7Zwyyk7IqITg4nX61oHj+GxpNvl+y5JHjfN3KXE2IV55wAWowBYMVw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1489,13 +1565,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", - "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.6.tgz", + "integrity": "sha512-h/2j7oIUDjS+ULsIrNZ6/TKG97FgmEk1PXryk/HQq6op4XUUUwif2f69fJrzK0wza2zjCS1xhXmouACaWV5uPA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1505,12 +1582,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", - "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.6.tgz", + "integrity": "sha512-fN8OcTLfGmYv7FnDrsjodYBo1DhPL3Pze/9mIIE2MGCT1KgADYIOD7rEglpLHZj8PZlC/JFX5WcD+85FLAQusw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1520,12 +1598,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", - "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.6.tgz", + "integrity": "sha512-BJbEqJIcKwrqUP+KfUIkxz3q8VzXe2R8Wv8TaNgO1cx+nNavxn/2+H8kp9tgFSOL6wYPPEgFvU6IKS4qoGqhmg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1535,12 +1614,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.5.tgz", - "integrity": "sha512-UTGnhYVZtTAjdwOTzT+sCyXmTn8AhaxOS/MjG9REclZ6ULHWF9KoCZur0HSGU7hk8PdBFKKbYe6+gqdXWz84Jg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.6.tgz", + "integrity": "sha512-IshCXQ+G9JIFJI7bUpxTE/oA2lgVLAIK8q1KdJNoPXOpvRaNjMySGuvLfBw/Xi2/1lLo953uE8hyYSDW3TSYig==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.5" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1550,12 +1630,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", - "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.6.tgz", + "integrity": "sha512-bKl3xxcPbkQQo5eX9LjjDpU2xYHeEeNQbOhj0iPvetSzA+Tu9q/o5lujF4Sek60CM6MgYvOS/DJuwGbiEYAnLw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1565,13 +1646,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", - "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.6.tgz", + "integrity": "sha512-8EIgImzVUxy15cZiPii9GvLZwsy7Vxc+8meSlR3cXFmBIl5W5Tn9LGBf7CDKkHj4uVfNXCJB8RsVfnmY61iedA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1581,13 +1663,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", - "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.6.tgz", + "integrity": "sha512-pssN6ExsvxaKU638qcWb81RrvvgZom3jDgU/r5xFZ7TONkZGFf4MhI2ltMb8OcQWhHyxgIavEU+hgqtbKOmsPA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1597,13 +1680,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", - "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.6.tgz", + "integrity": "sha512-quiMsb28oXWIDK0gXLALOJRXLgICLiulqdZGOaPPd0vRT7fQp74NtdADAVu+D8s00C+0Xs0MxVP0VKF/sZEUgw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6" }, "engines": { "node": ">=6.9.0" @@ -1613,27 +1697,28 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.5.tgz", - "integrity": "sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.6.tgz", + "integrity": "sha512-CrxEAvN7VxfjOG8JNF2Y/eMqMJbZPZ185amwGUBp8D9USK90xQmv7dLdFSa+VbD7fdIqcy/Mfv7WtzG8+/qxKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.24.6", + "@babel/helper-compilation-targets": "^7.24.6", + "@babel/helper-plugin-utils": "^7.24.6", + "@babel/helper-validator-option": "^7.24.6", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.6", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.1", - "@babel/plugin-syntax-import-attributes": "^7.24.1", + "@babel/plugin-syntax-import-assertions": "^7.24.6", + "@babel/plugin-syntax-import-attributes": "^7.24.6", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -1645,54 +1730,54 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.1", - "@babel/plugin-transform-async-generator-functions": "^7.24.3", - "@babel/plugin-transform-async-to-generator": "^7.24.1", - "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.5", - "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.4", - "@babel/plugin-transform-classes": "^7.24.5", - "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.5", - "@babel/plugin-transform-dotall-regex": "^7.24.1", - "@babel/plugin-transform-duplicate-keys": "^7.24.1", - "@babel/plugin-transform-dynamic-import": "^7.24.1", - "@babel/plugin-transform-exponentiation-operator": "^7.24.1", - "@babel/plugin-transform-export-namespace-from": "^7.24.1", - "@babel/plugin-transform-for-of": "^7.24.1", - "@babel/plugin-transform-function-name": "^7.24.1", - "@babel/plugin-transform-json-strings": "^7.24.1", - "@babel/plugin-transform-literals": "^7.24.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", - "@babel/plugin-transform-member-expression-literals": "^7.24.1", - "@babel/plugin-transform-modules-amd": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-modules-systemjs": "^7.24.1", - "@babel/plugin-transform-modules-umd": "^7.24.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.24.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", - "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.5", - "@babel/plugin-transform-object-super": "^7.24.1", - "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.5", - "@babel/plugin-transform-parameters": "^7.24.5", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.5", - "@babel/plugin-transform-property-literals": "^7.24.1", - "@babel/plugin-transform-regenerator": "^7.24.1", - "@babel/plugin-transform-reserved-words": "^7.24.1", - "@babel/plugin-transform-shorthand-properties": "^7.24.1", - "@babel/plugin-transform-spread": "^7.24.1", - "@babel/plugin-transform-sticky-regex": "^7.24.1", - "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.5", - "@babel/plugin-transform-unicode-escapes": "^7.24.1", - "@babel/plugin-transform-unicode-property-regex": "^7.24.1", - "@babel/plugin-transform-unicode-regex": "^7.24.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/plugin-transform-arrow-functions": "^7.24.6", + "@babel/plugin-transform-async-generator-functions": "^7.24.6", + "@babel/plugin-transform-async-to-generator": "^7.24.6", + "@babel/plugin-transform-block-scoped-functions": "^7.24.6", + "@babel/plugin-transform-block-scoping": "^7.24.6", + "@babel/plugin-transform-class-properties": "^7.24.6", + "@babel/plugin-transform-class-static-block": "^7.24.6", + "@babel/plugin-transform-classes": "^7.24.6", + "@babel/plugin-transform-computed-properties": "^7.24.6", + "@babel/plugin-transform-destructuring": "^7.24.6", + "@babel/plugin-transform-dotall-regex": "^7.24.6", + "@babel/plugin-transform-duplicate-keys": "^7.24.6", + "@babel/plugin-transform-dynamic-import": "^7.24.6", + "@babel/plugin-transform-exponentiation-operator": "^7.24.6", + "@babel/plugin-transform-export-namespace-from": "^7.24.6", + "@babel/plugin-transform-for-of": "^7.24.6", + "@babel/plugin-transform-function-name": "^7.24.6", + "@babel/plugin-transform-json-strings": "^7.24.6", + "@babel/plugin-transform-literals": "^7.24.6", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.6", + "@babel/plugin-transform-member-expression-literals": "^7.24.6", + "@babel/plugin-transform-modules-amd": "^7.24.6", + "@babel/plugin-transform-modules-commonjs": "^7.24.6", + "@babel/plugin-transform-modules-systemjs": "^7.24.6", + "@babel/plugin-transform-modules-umd": "^7.24.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.6", + "@babel/plugin-transform-new-target": "^7.24.6", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.6", + "@babel/plugin-transform-numeric-separator": "^7.24.6", + "@babel/plugin-transform-object-rest-spread": "^7.24.6", + "@babel/plugin-transform-object-super": "^7.24.6", + "@babel/plugin-transform-optional-catch-binding": "^7.24.6", + "@babel/plugin-transform-optional-chaining": "^7.24.6", + "@babel/plugin-transform-parameters": "^7.24.6", + "@babel/plugin-transform-private-methods": "^7.24.6", + "@babel/plugin-transform-private-property-in-object": "^7.24.6", + "@babel/plugin-transform-property-literals": "^7.24.6", + "@babel/plugin-transform-regenerator": "^7.24.6", + "@babel/plugin-transform-reserved-words": "^7.24.6", + "@babel/plugin-transform-shorthand-properties": "^7.24.6", + "@babel/plugin-transform-spread": "^7.24.6", + "@babel/plugin-transform-sticky-regex": "^7.24.6", + "@babel/plugin-transform-template-literals": "^7.24.6", + "@babel/plugin-transform-typeof-symbol": "^7.24.6", + "@babel/plugin-transform-unicode-escapes": "^7.24.6", + "@babel/plugin-transform-unicode-property-regex": "^7.24.6", + "@babel/plugin-transform-unicode-regex": "^7.24.6", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.6", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", @@ -1751,33 +1836,35 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", + "integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", - "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/types": "^7.24.5", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz", + "integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1786,13 +1873,14 @@ } }, "node_modules/@babel/types": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", - "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", + "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.1", - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-string-parser": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2271,9 +2359,9 @@ } }, "node_modules/@openedx/paragon": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.5.1.tgz", - "integrity": "sha512-GSDC28jlsfP8LPUoebXtkzw5cIxl44+9dhscvz2znZ7uMYjEbmp5waPR5rAJ4lKtNzFBZUX/mAiaNilhhZXu9Q==", + "version": "22.6.0", + "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.6.0.tgz", + "integrity": "sha512-O/XL4mx0LKXmZh2rIrKZL7yGXZqGmtTpPN7nSNEf9tC9qHCHhZ5lLx8muEJZL7TShOsO85SE5X9yOGFFrvwCsQ==", "license": "Apache-2.0", "workspaces": [ "example", @@ -2904,6 +2992,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -3469,6 +3558,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -3620,6 +3710,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -3628,7 +3719,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colord": { "version": "2.9.3", @@ -5637,6 +5729,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -9370,9 +9463,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { - "version": "1.77.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz", - "integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==", + "version": "1.77.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.4.tgz", + "integrity": "sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==", "license": "MIT", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -9830,6 +9923,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, diff --git a/package.json b/package.json index 8a1f305ad8..3fbf59e4bb 100644 --- a/package.json +++ b/package.json @@ -15,19 +15,19 @@ "css-minimizer-webpack-plugin": "6.0.0", "file-loader": "6.2.0", "mini-css-extract-plugin": "2.9.0", - "sass": "1.77.2", + "sass": "1.77.4", "sass-loader": "14.2.1", "url-loader": "4.1.1", "webpack": "5.91.0", "webpack-bundle-tracker": "3.1.0" }, "devDependencies": { - "@babel/core": "7.24.5", - "@babel/eslint-parser": "7.24.5", + "@babel/core": "7.24.6", + "@babel/eslint-parser": "7.24.6", "@babel/plugin-proposal-object-rest-spread": "7.20.7", - "@babel/plugin-transform-modules-commonjs": "7.24.1", - "@babel/plugin-transform-object-assign": "7.24.1", - "@babel/preset-env": "7.24.5", + "@babel/plugin-transform-modules-commonjs": "7.24.6", + "@babel/plugin-transform-object-assign": "7.24.6", + "@babel/preset-env": "7.24.6", "@edx/eslint-config": "1.3.0", "babel-loader": "9.1.3", "eslint": "8.57.0", diff --git a/requirements/all.txt b/requirements/all.txt index d1f9ae7030..50bb01a4a0 100644 --- a/requirements/all.txt +++ b/requirements/all.txt @@ -10,7 +10,6 @@ asgiref==3.8.1 # -r requirements/production.txt # django # django-cors-headers - # django-simple-history astroid==3.2.2 # via # -r requirements/dev.txt @@ -45,11 +44,11 @@ bleach==6.1.0 # via # -r requirements/dev.txt # -r requirements/production.txt -boto3==1.34.112 +boto3==1.34.116 # via # -r requirements/production.txt # django-ses -botocore==1.34.112 +botocore==1.34.116 # via # -r requirements/production.txt # boto3 @@ -112,7 +111,7 @@ coreschema==0.0.4 # -r requirements/dev.txt # -r requirements/production.txt # coreapi -coverage==7.5.1 +coverage==7.5.3 # via -r requirements/dev.txt cryptography==42.0.7 # via @@ -153,6 +152,7 @@ django==4.2.13 # django-filter # django-model-utils # django-ses + # django-simple-history # django-statici18n # django-storages # django-waffle @@ -188,7 +188,7 @@ django-crum==0.7.9 # -r requirements/production.txt # edx-django-utils # edx-toggles -django-debug-toolbar==4.3.0 +django-debug-toolbar==4.4.2 # via -r requirements/dev.txt django-extensions==3.2.3 # via @@ -212,7 +212,7 @@ django-rest-swagger==2.2.0 # -r requirements/production.txt django-ses==4.1.0 # via -r requirements/production.txt -django-simple-history==3.5.0 +django-simple-history==3.7.0 # via # -r requirements/dev.txt # -r requirements/production.txt @@ -274,7 +274,7 @@ edx-ccx-keys==1.3.0 # -r requirements/dev.txt # -r requirements/production.txt # openedx-events -edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.15 +edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.16 # via # -r requirements/dev.txt # -r requirements/production.txt @@ -338,7 +338,7 @@ exceptiongroup==1.2.1 # pytest factory-boy==3.3.0 # via -r requirements/dev.txt -faker==25.2.0 +faker==25.3.0 # via # -r requirements/dev.txt # factory-boy @@ -439,7 +439,7 @@ newrelic==9.10.0 # -r requirements/dev.txt # -r requirements/production.txt # edx-django-utils -nodeenv==1.8.0 +nodeenv==1.9.0 # via -r requirements/production.txt oauthlib==3.2.2 # via @@ -776,7 +776,7 @@ xss-utils==0.6.0 # via # -r requirements/dev.txt # -r requirements/production.txt -zipp==3.18.2 +zipp==3.19.0 # via # -r requirements/dev.txt # -r requirements/production.txt diff --git a/requirements/base.in b/requirements/base.in index b3fb1e9a4c..a36404f6f5 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -55,5 +55,5 @@ social-auth-app-django xss-utils # TODO Install in configuration -git+https://github.com/openedx/credentials-themes.git@0.4.15#egg=edx_credentials_themes==0.4.15 +git+https://github.com/openedx/credentials-themes.git@0.4.16#egg=edx_credentials_themes==0.4.16 git+https://github.com/openedx/event-bus-redis.git@v0.4.0 diff --git a/requirements/base.txt b/requirements/base.txt index 572cb240e3..0669b8cbdc 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -65,6 +65,7 @@ django==4.2.13 # django-extensions # django-filter # django-model-utils + # django-simple-history # django-statici18n # django-storages # django-waffle @@ -103,7 +104,7 @@ django-ratelimit==4.1.0 # via -r requirements/base.in django-rest-swagger==2.2.0 # via -r requirements/base.in -django-simple-history==3.5.0 +django-simple-history==3.7.0 # via -r requirements/base.in django-sortedm2m==3.1.1 # via -r requirements/base.in @@ -138,7 +139,7 @@ edx-auth-backends==4.3.0 # via -r requirements/base.in edx-ccx-keys==1.3.0 # via openedx-events -edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.15 +edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.16 # via -r requirements/base.in edx-django-release-util==1.4.0 # via -r requirements/base.in @@ -346,5 +347,5 @@ webencodings==0.5.1 # via bleach xss-utils==0.6.0 # via -r requirements/base.in -zipp==3.18.2 +zipp==3.19.0 # via importlib-metadata diff --git a/requirements/dev.txt b/requirements/dev.txt index 7559367af1..296d334d14 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -9,7 +9,6 @@ asgiref==3.8.1 # -r requirements/test.txt # django # django-cors-headers - # django-simple-history astroid==3.2.2 # via # -r requirements/test.txt @@ -89,7 +88,7 @@ coreschema==0.0.4 # via # -r requirements/test.txt # coreapi -coverage==7.5.1 +coverage==7.5.3 # via -r requirements/test.txt cryptography==42.0.7 # via @@ -124,6 +123,7 @@ django==4.2.13 # django-extensions # django-filter # django-model-utils + # django-simple-history # django-statici18n # django-storages # django-waffle @@ -155,7 +155,7 @@ django-crum==0.7.9 # -r requirements/test.txt # edx-django-utils # edx-toggles -django-debug-toolbar==4.3.0 +django-debug-toolbar==4.4.2 # via -r requirements/dev.in django-extensions==3.2.3 # via -r requirements/test.txt @@ -167,7 +167,7 @@ django-ratelimit==4.1.0 # via -r requirements/test.txt django-rest-swagger==2.2.0 # via -r requirements/test.txt -django-simple-history==3.5.0 +django-simple-history==3.7.0 # via -r requirements/test.txt django-sortedm2m==3.1.1 # via -r requirements/test.txt @@ -208,7 +208,7 @@ edx-ccx-keys==1.3.0 # via # -r requirements/test.txt # openedx-events -edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.15 +edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.16 # via -r requirements/test.txt edx-django-release-util==1.4.0 # via -r requirements/test.txt @@ -255,7 +255,7 @@ exceptiongroup==1.2.1 # pytest factory-boy==3.3.0 # via -r requirements/test.txt -faker==25.2.0 +faker==25.3.0 # via # -r requirements/test.txt # factory-boy @@ -603,7 +603,7 @@ webencodings==0.5.1 # bleach xss-utils==0.6.0 # via -r requirements/test.txt -zipp==3.18.2 +zipp==3.19.0 # via # -r requirements/test.txt # importlib-metadata diff --git a/requirements/docs.txt b/requirements/docs.txt index e6bf296787..4b249689fa 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -50,7 +50,7 @@ pygments==2.18.0 # sphinx pytz==2024.1 # via babel -requests==2.32.2 +requests==2.32.3 # via sphinx snowballstemmer==2.2.0 # via sphinx @@ -81,5 +81,5 @@ urllib3==1.26.18 # via # -c requirements/constraints.txt # requests -zipp==3.18.2 +zipp==3.19.0 # via importlib-metadata diff --git a/requirements/pip_tools.txt b/requirements/pip_tools.txt index 940cd782f3..535cf6d9a8 100644 --- a/requirements/pip_tools.txt +++ b/requirements/pip_tools.txt @@ -26,7 +26,7 @@ tomli==2.0.1 # pip-tools wheel==0.43.0 # via pip-tools -zipp==3.18.2 +zipp==3.19.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/production.txt b/requirements/production.txt index f47be983fb..72a6d44beb 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -31,9 +31,9 @@ backports-zoneinfo==0.2.1 ; python_version < "3.9" # djangorestframework bleach==6.1.0 # via -r requirements/base.txt -boto3==1.34.112 +boto3==1.34.116 # via django-ses -botocore==1.34.112 +botocore==1.34.116 # via # boto3 # s3transfer @@ -91,6 +91,7 @@ django==4.2.13 # django-filter # django-model-utils # django-ses + # django-simple-history # django-statici18n # django-storages # django-waffle @@ -134,7 +135,7 @@ django-rest-swagger==2.2.0 # via -r requirements/base.txt django-ses==4.1.0 # via -r requirements/production.in -django-simple-history==3.5.0 +django-simple-history==3.7.0 # via -r requirements/base.txt django-sortedm2m==3.1.1 # via -r requirements/base.txt @@ -175,7 +176,7 @@ edx-ccx-keys==1.3.0 # via # -r requirements/base.txt # openedx-events -edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.15 +edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.16 # via -r requirements/base.txt edx-django-release-util==1.4.0 # via -r requirements/base.txt @@ -270,7 +271,7 @@ newrelic==9.10.0 # -r requirements/base.txt # -r requirements/production.in # edx-django-utils -nodeenv==1.8.0 +nodeenv==1.9.0 # via -r requirements/production.in oauthlib==3.2.2 # via @@ -471,7 +472,7 @@ webencodings==0.5.1 # bleach xss-utils==0.6.0 # via -r requirements/base.txt -zipp==3.18.2 +zipp==3.19.0 # via # -r requirements/base.txt # importlib-metadata diff --git a/requirements/test.txt b/requirements/test.txt index 627108a1ab..62179972ef 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -9,7 +9,6 @@ asgiref==3.8.1 # -r requirements/base.txt # django # django-cors-headers - # django-simple-history astroid==3.2.2 # via # pylint @@ -81,7 +80,7 @@ coreschema==0.0.4 # via # -r requirements/base.txt # coreapi -coverage==7.5.1 +coverage==7.5.3 # via -r requirements/test.in cryptography==42.0.7 # via @@ -110,6 +109,7 @@ distlib==0.3.8 # django-extensions # django-filter # django-model-utils + # django-simple-history # django-statici18n # django-storages # django-waffle @@ -151,7 +151,7 @@ django-ratelimit==4.1.0 # via -r requirements/base.txt django-rest-swagger==2.2.0 # via -r requirements/base.txt -django-simple-history==3.5.0 +django-simple-history==3.7.0 # via -r requirements/base.txt django-sortedm2m==3.1.1 # via -r requirements/base.txt @@ -192,7 +192,7 @@ edx-ccx-keys==1.3.0 # via # -r requirements/base.txt # openedx-events -edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.15 +edx-credentials-themes @ git+https://github.com/openedx/credentials-themes.git@0.4.16 # via -r requirements/base.txt edx-django-release-util==1.4.0 # via -r requirements/base.txt @@ -236,7 +236,7 @@ exceptiongroup==1.2.1 # via pytest factory-boy==3.3.0 # via -r requirements/test.in -faker==25.2.0 +faker==25.3.0 # via factory-boy fastavro==1.9.4 # via @@ -555,7 +555,7 @@ webencodings==0.5.1 # bleach xss-utils==0.6.0 # via -r requirements/base.txt -zipp==3.18.2 +zipp==3.19.0 # via # -r requirements/base.txt # importlib-metadata