diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..031bc7d --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,58 @@ +# Instructions Copilot - Projet N3WT-SCHOOL + +## Objectif + +Corriger ou améliorer le projet N3WT-SCHOOL de manière minimaliste et fonctionnelle, sans dépendances inutiles. + +## Architecture du projet + +### Structure + +- **Backend** : Python Django (dossier `Back-End/`) +- **Frontend** : NextJS (dossier `Front-End/`) +- **Tests frontend** : `Front-End/src/test/` +- **Code frontend** : `Front-End/src/` + +## Gestion des tickets + +### Règles générales + +- Chaque **nouvelle fonctionnalité** ou **correction** nécessite un ticket Gitea +- **Exemptions** : modifications documentaires, refactoring, chore, style + +### Cycle de vie d'un ticket + +1. **Création** → label `etat/En Pause` +2. **Affectation** → label `etat/En Cours` +3. **Développement terminé** → label `etat/Codé` +4. **Tests validés** → label `etat/Testé` + +### Gestion des branches + +- **Base** : branche `develop` +- **Nomenclature** : `--` (ex: `feat-ma_super_feat-1234`) +- **Types** : feat, fix, docs, style, refactor, test, chore + +## Exigences qualité + +Pour le front-end, les exigences de qualité sont les suivantes : + +- **Linting** : Utiliser ESLint pour le code JavaScript/TypeScript +- **Formatage** : Utiliser Prettier pour le formatage du code +- **Tests** : Utiliser Jest pour les tests unitaires et d'intégration +- Référence : [frontend guideline](./instructions/frontend.instruction.md) + +### Tests + +- Tests unitaires obligatoires pour chaque nouvelle fonctionnalité +- Localisation : `Front-End/src/test/` + +### Documentation + +- Documentation en français pour les nouvelles fonctionnalités (si applicable) +- Référence : [documentation guidelines](./instructions/documentation.instruction.md) + +## Références + +- **Tickets** : [issues guidelines](./instructions/issues.instruction.md) +- **Commits** : [commit guidelines](./instructions/general-commit.instruction.md) diff --git a/.github/instructions/documentation.instruction.md b/.github/instructions/documentation.instruction.md new file mode 100644 index 0000000..cfc35e4 --- /dev/null +++ b/.github/instructions/documentation.instruction.md @@ -0,0 +1,6 @@ +La documentation doit être en français et claire pour les utilisateurs francophones. +Toutes la documentation doit être dans le dossier docs/ +Seul les fichiers README.md, CHANGELOG.md doivent être à la racine. +La documentation doit être conscise et pertinente, sans répétitions inutiles entre les documents. +Tout ce qui concerne la gestion de projet, roadmap ne doit pas apparaître dans la documentation. +Tout ce qui concerne les modifications de type chore n'a pas besoin d'être documenté. diff --git a/.github/instructions/frontend.instruction.md b/.github/instructions/frontend.instruction.md new file mode 100644 index 0000000..e69de29 diff --git a/.github/instructions/general-commit.instruction.md b/.github/instructions/general-commit.instruction.md new file mode 100644 index 0000000..e7279d4 --- /dev/null +++ b/.github/instructions/general-commit.instruction.md @@ -0,0 +1,28 @@ +### **Commit Guidelines (Conventionnel)** + +Les messages de commits se basent sur **Conventional Commits** (https://www.conventionalcommits.org/en/v1.0.0/) , pour une meilleure gestion des versions et une génération automatique du changelog. + +#### **Format standard** : + +``` +(): [#] +``` + +- **Types autorisés** : + + - `feat` : Nouvelle fonctionnalité. + - `fix` : Correction d’un bug. + - `docs` : Modifications liées à la documentation. + - `style` : Mise en forme du code (pas de changements fonctionnels). + - `refactor` : Refactorisation sans ajout de fonctionnalités ni correction de bugs. + - `test` : Ajout ou modification de tests. + - `chore` : Maintenance ou tâches diverses (ex. mise à jour des dépendances). + +- **Scope (optionnel)** : Précisez une partie spécifique du projet (`backend`, `frontend`, `API`, etc.). + +- **Exemples** : + ``` + feat(frontend): ajout de la gestion des utilisateurs dans le dashboard [#1] + fix(backend): correction du bug lié à l'authentification JWT [#1] + docs: mise à jour du README avec les nouvelles instructions d’installation [#2] + ``` diff --git a/.github/instructions/issues.instruction.md b/.github/instructions/issues.instruction.md new file mode 100644 index 0000000..1a2e3fe --- /dev/null +++ b/.github/instructions/issues.instruction.md @@ -0,0 +1,22 @@ +- Chaque nouveau ticket doit faire l'objet d'une analyse pour définir les modifications à effectuer. +- l'analyse doit être présente dans la description du ticket au format: + +# Description de la modification + +Définir la modification + +# Solution envisagée + +Définir l'implementation avec l'impact dans le code + +# Modification documentaire + +Définir les documents à modifier si il existe il peut ne pas y en avoir. + +# Tests + +Définir ici comment va être tester la fonctionnalité et les cas de test. + +- Une fois créer son label doit passer à `etat/En Pause` +- Les labels 'subject' sont ensuites ajouté en fonction du sujet de modification. +- le label 'workload' est défini en fonction de si la modification de code est longue (gros impact) à faire ou rapide (faible impact) diff --git a/.gitignore b/.gitignore index 1770d0d..3d1c146 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,4 @@ -Back-End/*/Configuration/application.json .venv/ -__pycache__/ +.env node_modules/ -Back-End/*/migrations/* -Back-End/documents -Back-End/data -Back-End/*.dmp -Back-End/staticfiles hardcoded-strings-report.md \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index e69de29..cc642e9 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -0,0 +1 @@ +cd $(dirname "$0")/../Front-End/ && npm run lint-light \ No newline at end of file diff --git a/.husky/prepare-commit-msg b/.husky/prepare-commit-msg index bfb96ca..2fa70b3 100644 --- a/.husky/prepare-commit-msg +++ b/.husky/prepare-commit-msg @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - node scripts/prepare-commit-msg.js "$1" "$2" \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..dcd35a8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ + +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: debug client-side", + "type": "chrome", + "request": "launch", + "webRoot": "${workspaceFolder}/Front-End/", + "url": "http://localhost:3000", + }, + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3faed76 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "eslint.workingDirectories": ["./Front-End"], + "giteaCopilotTools.owner": "n3wt-innov", + "giteaCopilotTools.repo": "n3wt-school", + "giteaCopilotTools.autoRefreshInterval": 15 +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..0ce1ae6 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,13 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Frontend Dev Server", + "type": "shell", + "command": "npm run dev", + "group": "build", + "isBackground": true, + "problemMatcher": [] + } + ] +} diff --git a/Back-End/.gitignore b/Back-End/.gitignore new file mode 100644 index 0000000..4a10846 --- /dev/null +++ b/Back-End/.gitignore @@ -0,0 +1,7 @@ +__pycache__ +/*/migrations/* +documents +data +*.dmp +staticfiles +/*/Configuration/application*.json \ No newline at end of file diff --git a/Back-End/Auth/migrations/0001_initial.py b/Back-End/Auth/migrations/0001_initial.py new file mode 100644 index 0000000..f4492ff --- /dev/null +++ b/Back-End/Auth/migrations/0001_initial.py @@ -0,0 +1,75 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('Establishment', '0001_initial'), + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='ProfileRole', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('role_type', models.IntegerField(choices=[(-1, 'NON DEFINI'), (0, 'ECOLE'), (1, 'ADMIN'), (2, 'PARENT')], default=-1)), + ('is_active', models.BooleanField(default=False)), + ('updated_date', models.DateTimeField(auto_now=True)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='profile_roles', to='Establishment.establishment')), + ], + ), + migrations.CreateModel( + name='Directeur', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('last_name', models.CharField(max_length=100)), + ('first_name', models.CharField(max_length=100)), + ('profile_role', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='directeur_profile', to='Auth.profilerole')), + ], + ), + migrations.CreateModel( + name='Profile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('email', models.EmailField(default='', max_length=255, unique=True, validators=[django.core.validators.EmailValidator()])), + ('roleIndexLoginDefault', models.IntegerField(default=0)), + ('code', models.CharField(blank=True, default='', max_length=200)), + ('datePeremption', models.CharField(blank=True, default='', max_length=200)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.AddField( + model_name='profilerole', + name='profile', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='roles', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/Back-End/Auth/migrations/__init__.py b/Back-End/Auth/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/Auth/models.py b/Back-End/Auth/models.py index 9792319..408d702 100644 --- a/Back-End/Auth/models.py +++ b/Back-End/Auth/models.py @@ -4,22 +4,37 @@ from django.utils.translation import gettext_lazy as _ from django.core.validators import EmailValidator class Profile(AbstractUser): - class Droits(models.IntegerChoices): - PROFIL_UNDEFINED = -1, _('NON DEFINI') - PROFIL_ECOLE = 0, _('ECOLE') - PROFIL_ADMIN = 1, _('ADMIN') - PROFIL_PARENT = 2, _('PARENT') - email = models.EmailField(max_length=255, unique=True, default="", validators=[EmailValidator()]) USERNAME_FIELD = 'email' - REQUIRED_FIELDS = ('password', ) - + roleIndexLoginDefault = models.IntegerField(default=0) code = models.CharField(max_length=200, default="", blank=True) datePeremption = models.CharField(max_length=200, default="", blank=True) - droit = models.IntegerField(choices=Droits, default=Droits.PROFIL_UNDEFINED) - estConnecte = models.BooleanField(default=False, blank=True) def __str__(self): - return self.email + " - " + str(self.droit) + return self.email + +class ProfileRole(models.Model): + class RoleType(models.IntegerChoices): + PROFIL_UNDEFINED = -1, _('NON DEFINI') + PROFIL_ECOLE = 0, _('ECOLE') + PROFIL_ADMIN = 1, _('ADMIN') + PROFIL_PARENT = 2, _('PARENT') + + profile = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name='roles') + role_type = models.IntegerField(choices=RoleType.choices, default=RoleType.PROFIL_UNDEFINED) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='profile_roles') + is_active = models.BooleanField(default=False) + updated_date = models.DateTimeField(auto_now=True) + + def __str__(self): + return f"{self.profile.email} - {self.get_role_type_display()}" + +class Directeur(models.Model): + profile_role = models.OneToOneField("ProfileRole", on_delete=models.CASCADE, related_name='directeur_profile') + last_name = models.CharField(max_length=100) + first_name = models.CharField(max_length=100) + + def __str__(self): + return f"{self.first_name} {self.last_name} ({self.profile_role.profile.email})" \ No newline at end of file diff --git a/Back-End/Auth/pagination.py b/Back-End/Auth/pagination.py new file mode 100644 index 0000000..23ef561 --- /dev/null +++ b/Back-End/Auth/pagination.py @@ -0,0 +1,20 @@ +from rest_framework.pagination import PageNumberPagination + +from N3wtSchool import settings + +class CustomProfilesPagination(PageNumberPagination): + page_size_query_param = 'page_size' + max_page_size = settings.NB_MAX_PAGE + page_size = settings.NB_RESULT_PROFILES_PER_PAGE + + def get_paginated_response(self, data): + return ({ + 'links': { + 'next': self.get_next_link(), + 'previous': self.get_previous_link() + }, + 'count': self.page.paginator.count, + 'page_size': self.page_size, + 'max_page_size' : self.max_page_size, + 'profilesRoles': data } + ) \ No newline at end of file diff --git a/Back-End/Auth/serializers.py b/Back-End/Auth/serializers.py index 8ae7737..6f1db3e 100644 --- a/Back-End/Auth/serializers.py +++ b/Back-End/Auth/serializers.py @@ -1,51 +1,160 @@ from rest_framework import serializers -from Auth.models import Profile -from django.core.exceptions import ValidationError +from Auth.models import Profile, ProfileRole +from Establishment.models import Establishment +from Subscriptions.models import Guardian, RegistrationForm +from School.models import Teacher +from N3wtSchool import settings +from django.utils import timezone +import pytz class ProfileSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) password = serializers.CharField(write_only=True) + roles = serializers.SerializerMethodField() class Meta: model = Profile - fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active'] + fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles', 'roleIndexLoginDefault'] extra_kwargs = {'password': {'write_only': True}} + def get_roles(self, obj): + roles = ProfileRole.objects.filter(profile=obj) + roles_data = [] + for role in roles: + # Récupérer l'ID de l'associated_person en fonction du type de rôle + if role.role_type == ProfileRole.RoleType.PROFIL_PARENT: + guardian = Guardian.objects.filter(profile_role=role).first() + id_associated_person = guardian.id if guardian else None + else: + teacher = Teacher.objects.filter(profile_role=role).first() + id_associated_person = teacher.id if teacher else None + + roles_data.append({ + 'id_associated_person': id_associated_person, + 'role_type': role.role_type, + 'establishment': role.establishment.id, + 'establishment_name': role.establishment.name, + 'is_active': role.is_active, + }) + return roles_data + def create(self, validated_data): user = Profile( username=validated_data['username'], email=validated_data['email'], - is_active=validated_data['is_active'], - droit=validated_data['droit'] + code=validated_data.get('code', ''), + datePeremption=validated_data.get('datePeremption', '') ) user.set_password(validated_data['password']) + user.full_clean() user.save() return user - def to_representation(self, instance): - ret = super().to_representation(instance) - ret['password'] = '********' - return ret - -class ProfilUpdateSerializer(serializers.ModelSerializer): - class Meta: - model = Profile - fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active'] - extra_kwargs = { - 'password': {'write_only': True, 'required': False} - } - def update(self, instance, validated_data): password = validated_data.pop('password', None) instance = super().update(instance, validated_data) - + if password: instance.set_password(password) - instance.save() + instance.full_clean() + instance.save() return instance def to_representation(self, instance): ret = super().to_representation(instance) ret['password'] = '********' + ret['roles'] = self.get_roles(instance) return ret + +class ProfileRoleSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(required=False) + profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=False) + profile_data = ProfileSerializer(write_only=True, required=False) + associated_profile_email = serializers.SerializerMethodField() + associated_person = serializers.SerializerMethodField() + updated_date_formatted = serializers.SerializerMethodField() + + class Meta: + model = ProfileRole + fields = ['id', 'role_type', 'establishment', 'is_active', 'profile', 'profile_data', 'associated_profile_email', 'associated_person', 'updated_date_formatted'] + + def create(self, validated_data): + profile_data = validated_data.pop('profile_data', None) + profile = validated_data.pop('profile', None) + + if profile_data: + profile_serializer = ProfileSerializer(data=profile_data) + profile_serializer.is_valid(raise_exception=True) + profile = profile_serializer.save() + elif profile: + profile = Profile.objects.get(id=profile.id) + + profile_role = ProfileRole.objects.create(profile=profile, **validated_data) + return profile_role + + def update(self, instance, validated_data): + profile_data = validated_data.pop('profile_data', None) + profile = validated_data.pop('profile', None) + + if profile_data: + profile_serializer = ProfileSerializer(instance.profile, data=profile_data) + profile_serializer.is_valid(raise_exception=True) + profile = profile_serializer.save() + elif profile: + profile = Profile.objects.get(id=profile.id) + + if profile: + instance.profile = profile + + instance.role_type = validated_data.get('role_type', instance.role_type) + instance.establishment_id = validated_data.get('establishment', instance.establishment.id) + instance.is_active = validated_data.get('is_active', instance.is_active) + instance.save() + return instance + + def get_associated_profile_email(self, obj): + if obj.profile: + return obj.profile.email + return None + + def get_associated_person(self, obj): + if obj.role_type == ProfileRole.RoleType.PROFIL_PARENT: + guardian = Guardian.objects.filter(profile_role=obj).first() + if guardian: + students = guardian.student_set.all() + students_list = [] + for student in students: + registration_form = RegistrationForm.objects.filter(student=student).first() + registration_status = registration_form.status if registration_form else None + students_list.append({ + "id": f"{student.id}", + "student_name": f"{student.last_name} {student.first_name}", + "registration_status": registration_status + }) + return { + "id": guardian.id, + "guardian_name": f"{guardian.last_name} {guardian.first_name}", + "students": students_list + } + else: + teacher = Teacher.objects.filter(profile_role=obj).first() + if teacher: + classes = teacher.schoolclass_set.all() + classes_list = [{"id": classe.id, "name": classe.atmosphere_name} for classe in classes] + specialities = teacher.specialities.all() + specialities_list = [{"name": speciality.name, "color_code": speciality.color_code} for speciality in specialities] + return { + "id": teacher.id, + "teacher_name": f"{teacher.last_name} {teacher.first_name}", + "classes": classes_list, + "specialities": specialities_list + } + return None + + def get_updated_date_formatted(self, obj): + utc_time = timezone.localtime(obj.updated_date) + local_tz = pytz.timezone(settings.TZ_APPLI) + local_time = utc_time.astimezone(local_tz) + + return local_time.strftime("%d-%m-%Y %H:%M") \ No newline at end of file diff --git a/Back-End/Auth/templates/GestionLogin/login.html b/Back-End/Auth/templates/GestionLogin/login.html deleted file mode 100644 index 46945c6..0000000 --- a/Back-End/Auth/templates/GestionLogin/login.html +++ /dev/null @@ -1,61 +0,0 @@ -{% load static %} - - - - - - - - Monteschool - - - -
-
- -
-

Authentification

- -
- - diff --git a/Back-End/Auth/templates/GestionLogin/new-password.html b/Back-End/Auth/templates/GestionLogin/new-password.html deleted file mode 100644 index 0c84b92..0000000 --- a/Back-End/Auth/templates/GestionLogin/new-password.html +++ /dev/null @@ -1,64 +0,0 @@ -{% load static %} - - - - - - - Monteschool - - -
-
- -
-

Nouveau Mot de Passe

- -
- - \ No newline at end of file diff --git a/Back-End/Auth/templates/GestionLogin/reset-password.html b/Back-End/Auth/templates/GestionLogin/reset-password.html deleted file mode 100644 index 299e0de..0000000 --- a/Back-End/Auth/templates/GestionLogin/reset-password.html +++ /dev/null @@ -1,37 +0,0 @@ -{% load static %} - - - - - - - - Monteschool - - -
-
- -
-

Réinitialiser Mot de Passe

- -
- - \ No newline at end of file diff --git a/Back-End/Auth/templates/GestionLogin/subscribe.html b/Back-End/Auth/templates/GestionLogin/subscribe.html deleted file mode 100644 index a216d13..0000000 --- a/Back-End/Auth/templates/GestionLogin/subscribe.html +++ /dev/null @@ -1,64 +0,0 @@ -{% load static %} - - - - - - - Monteschool - - -
-
- -
-

S'inscrire

- -
- - \ No newline at end of file diff --git a/Back-End/Auth/urls.py b/Back-End/Auth/urls.py index 2616d2c..a918c19 100644 --- a/Back-End/Auth/urls.py +++ b/Back-End/Auth/urls.py @@ -2,21 +2,21 @@ from django.urls import path, re_path from . import views import Auth.views -from Auth.views import ProfileView, ProfileListView, SessionView, LoginView, SubscribeView, NewPasswordView, ResetPasswordView +from Auth.views import ProfileRoleView, ProfileRoleSimpleView, ProfileSimpleView, ProfileView, SessionView, LoginView, RefreshJWTView, SubscribeView, NewPasswordView, ResetPasswordView urlpatterns = [ re_path(r'^csrf$', Auth.views.csrf, name='csrf'), re_path(r'^login$', LoginView.as_view(), name="login"), + re_path(r'^refreshJWT$', RefreshJWTView.as_view(), name="refresh_jwt"), re_path(r'^subscribe$', SubscribeView.as_view(), name='subscribe'), re_path(r'^newPassword$', NewPasswordView.as_view(), name='newPassword'), - re_path(r'^resetPassword/([a-zA-Z]+)$', ResetPasswordView.as_view(), name='resetPassword'), - re_path(r'^infoSession$', Auth.views.infoSession, name='infoSession'), + re_path(r'^resetPassword/(?P[a-zA-Z]+)$', ResetPasswordView.as_view(), name='resetPassword'), + re_path(r'^infoSession$', SessionView.as_view(), name='infoSession'), - re_path(r'^profiles$', ProfileListView.as_view(), name="profile"), - re_path(r'^profile$', ProfileView.as_view(), name="profile"), - re_path(r'^profile/([0-9]+)$', ProfileView.as_view(), name="profile"), + re_path(r'^profiles$', ProfileView.as_view(), name="profile"), + re_path(r'^profiles/(?P[0-9]+)$', ProfileSimpleView.as_view(), name="profile"), - # Test SESSION VIEW - re_path(r'^session$', SessionView.as_view(), name="session"), + re_path(r'^profileRoles$', ProfileRoleView.as_view(), name="profileRoles"), + re_path(r'^profileRoles/(?P[0-9]+)$', ProfileRoleSimpleView.as_view(), name="profileRoles"), ] \ No newline at end of file diff --git a/Back-End/Auth/views.py b/Back-End/Auth/views.py index cd5c385..ccf9478 100644 --- a/Back-End/Auth/views.py +++ b/Back-End/Auth/views.py @@ -4,47 +4,81 @@ from django.http.response import JsonResponse from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt, csrf_protect from django.utils.decorators import method_decorator from django.core.exceptions import ValidationError -from django.core.cache import cache from django.middleware.csrf import get_token from rest_framework.views import APIView from rest_framework.parsers import JSONParser from rest_framework import status +from Auth.pagination import CustomProfilesPagination -from datetime import datetime +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi + +from datetime import datetime, timedelta import jwt +from jwt.exceptions import ExpiredSignatureError, InvalidTokenError import json from . import validator -from .models import Profile +from .models import Profile, ProfileRole +from rest_framework.decorators import action, api_view +from django.db.models import Q -from Auth.serializers import ProfileSerializer, ProfilUpdateSerializer -from Subscriptions.models import RegistrationForm -from Subscriptions.signals import clear_cache -import Subscriptions.mailManager as mailer +from Auth.serializers import ProfileSerializer, ProfileRoleSerializer +from Subscriptions.models import RegistrationForm, Guardian +import N3wtSchool.mailManager as mailer import Subscriptions.util as util +import logging +from N3wtSchool import bdd, error, settings -from N3wtSchool import bdd, error +from rest_framework_simplejwt.authentication import JWTAuthentication +logger = logging.getLogger("AuthViews") + + +@swagger_auto_schema( + method='get', + operation_description="Obtenir un token CSRF", + responses={200: openapi.Response('Token CSRF', schema=openapi.Schema(type=openapi.TYPE_OBJECT, properties={ + 'csrfToken': openapi.Schema(type=openapi.TYPE_STRING) + }))} +) +@api_view(['GET']) def csrf(request): token = get_token(request) return JsonResponse({'csrfToken': token}) class SessionView(APIView): - - def post(self, request): + @swagger_auto_schema( + operation_description="Vérifier une session utilisateur", + manual_parameters=[openapi.Parameter('Authorization', openapi.IN_HEADER, type=openapi.TYPE_STRING, description='Bearer token')], + responses={ + 200: openapi.Response('Session valide', schema=openapi.Schema(type=openapi.TYPE_OBJECT, properties={ + 'user': openapi.Schema(type=openapi.TYPE_OBJECT, properties={ + 'id': openapi.Schema(type=openapi.TYPE_INTEGER), + 'email': openapi.Schema(type=openapi.TYPE_STRING), + 'roleIndexLoginDefault': openapi.Schema(type=openapi.TYPE_INTEGER), + 'roles': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Items(type=openapi.TYPE_OBJECT, properties={ + 'role_type': openapi.Schema(type=openapi.TYPE_STRING), + 'establishment': openapi.Schema(type=openapi.TYPE_STRING) + })) + }) + })), + 401: openapi.Response('Session invalide') + } + ) + def get(self, request): token = request.META.get('HTTP_AUTHORIZATION', '').split('Bearer ')[-1] - try: decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256']) - print(f'decode : {decoded_token}') - user_id = decoded_token.get('id') - user = Profile.objects.get(id=user_id) - + userid = decoded_token.get('user_id') + user = Profile.objects.get(id=userid) + roles = ProfileRole.objects.filter(profile=user).values('role_type', 'establishment__name') response_data = { 'user': { 'id': user.id, 'email': user.email, - 'role': user.droit, # Assure-toi que le champ 'droit' existe et contient le rôle + 'roleIndexLoginDefault': user.roleIndexLoginDefault, + 'roles': list(roles) } } return JsonResponse(response_data, status=status.HTTP_200_OK) @@ -53,208 +87,433 @@ class SessionView(APIView): except jwt.InvalidTokenError: return JsonResponse({"error": "Invalid token"}, status=status.HTTP_401_UNAUTHORIZED) -class ProfileListView(APIView): +class ProfileView(APIView): + @swagger_auto_schema( + operation_description="Obtenir la liste des profils", + responses={200: ProfileSerializer(many=True)} + ) def get(self, request): profilsList = bdd.getAllObjects(_objectName=Profile) profils_serializer = ProfileSerializer(profilsList, many=True) return JsonResponse(profils_serializer.data, safe=False) -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class ProfileView(APIView): - def get(self, request, _id): - profil=bdd.getObject(Profile, "id", _id) - profil_serializer=ProfileSerializer(profil) - return JsonResponse(profil_serializer.data, safe=False) - + @swagger_auto_schema( + operation_description="Créer un nouveau profil", + request_body=ProfileSerializer, + responses={ + 200: ProfileSerializer, + 400: 'Données invalides' + } + ) def post(self, request): - profil_data=JSONParser().parse(request) - print(f'{profil_data}') + profil_data = JSONParser().parse(request) profil_serializer = ProfileSerializer(data=profil_data) if profil_serializer.is_valid(): - profil_serializer.save() - + profil = profil_serializer.save() return JsonResponse(profil_serializer.data, safe=False) - - return JsonResponse(profil_serializer.errors, safe=False) - - def put(self, request, _id): - data=JSONParser().parse(request) - profil = Profile.objects.get(id=_id) - profil_serializer = ProfilUpdateSerializer(profil, data=data) - if profil_serializer.is_valid(): - profil_serializer.save() - return JsonResponse("Updated Successfully", safe=False) - - return JsonResponse(profil_serializer.errors, safe=False) - - def delete(self, request, _id): - return bdd.delete_object(Profile, _id) - -def infoSession(request): - profilCache = cache.get('session_cache') - if profilCache: - return JsonResponse({"cacheSession":True,"typeProfil":profilCache.droit, "username":profilCache.email}, safe=False) - else: - return JsonResponse({"cacheSession":False,"typeProfil":Profile.Droits.PROFIL_UNDEFINED, "username":""}, safe=False) + return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') +class ProfileSimpleView(APIView): + @swagger_auto_schema( + operation_description="Obtenir un profil par son ID", + responses={200: ProfileSerializer} + ) + def get(self, request, id): + profil = bdd.getObject(Profile, "id", id) + profil_serializer = ProfileSerializer(profil) + return JsonResponse(profil_serializer.data, safe=False) + + @swagger_auto_schema( + operation_description="Mettre à jour un profil", + request_body=ProfileSerializer, + responses={ + 200: 'Mise à jour réussie', + 400: 'Données invalides' + } + ) + def put(self, request, id): + data = JSONParser().parse(request) + profil = Profile.objects.get(id=id) + profil_serializer = ProfileSerializer(profil, data=data) + if profil_serializer.is_valid(): + profil_serializer.save() + return JsonResponse(profil_serializer.data, safe=False) + + return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + @swagger_auto_schema( + operation_description="Supprimer un profil", + responses={200: 'Suppression réussie'} + ) + def delete(self, request, id): + return bdd.delete_object(Profile, id) + +@method_decorator(csrf_exempt, name='dispatch') class LoginView(APIView): - - def get(self, request): - return JsonResponse({ - 'errorFields':'', - 'errorMessage':'', - 'profil':0, - }, safe=False) - + @swagger_auto_schema( + operation_description="Connexion utilisateur", + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['email', 'password'], + properties={ + 'email': openapi.Schema(type=openapi.TYPE_STRING), + 'password': openapi.Schema(type=openapi.TYPE_STRING) + } + ), + responses={ + 200: openapi.Response('Connexion réussie', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'token': openapi.Schema(type=openapi.TYPE_STRING), + 'refresh': openapi.Schema(type=openapi.TYPE_STRING) + } + )), + 400: openapi.Response('Connexion échouée', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT), + 'errorMessage': openapi.Schema(type=openapi.TYPE_STRING) + } + )) + } + ) def post(self, request): - data=JSONParser().parse(request) + data = JSONParser().parse(request) validatorAuthentication = validator.ValidatorAuthentication(data=data) retour = error.returnMessage[error.WRONG_ID] validationOk, errorFields = validatorAuthentication.validate() user = None + if validationOk: user = authenticate( email=data.get('email'), password=data.get('password'), ) if user is not None: - if user.is_active: - login(request, user) - user.estConnecte = True - user.save() - clear_cache() - retour = '' - else: - retour = error.returnMessage[error.PROFIL_INACTIVE] + # Vérifier si l'utilisateur a un role actif + has_active_role = ProfileRole.objects.filter(profile=user, is_active=True).first() + if not has_active_role: + return JsonResponse({"errorMessage": "Profil inactif"}, status=status.HTTP_401_UNAUTHORIZED) + + login(request, user) + user.save() + retour = '' + access_token, refresh_token = makeToken(user) + + return JsonResponse({ + 'token': access_token, + 'refresh': refresh_token + }, safe=False) - # Génération du token JWT - # jwt_token = jwt.encode({ - # 'id': user.id, - # 'email': user.email, - # 'role': "admin" - # }, settings.SECRET_KEY, algorithm='HS256') else: retour = error.returnMessage[error.WRONG_ID] - return JsonResponse({ - 'errorFields':errorFields, - 'errorMessage':retour, - 'profil':user.id if user else -1, - 'droit':user.droit if user else -1, - #'jwtToken':jwt_token if profil != -1 else '' - }, safe=False) + 'errorFields': errorFields, + 'errorMessage': retour, + }, safe=False, status=status.HTTP_400_BAD_REQUEST) + +def makeToken(user): + """ + Fonction pour créer un token JWT pour l'utilisateur donné. + """ + try: + # Récupérer tous les rôles de l'utilisateur actifs + roles_qs = ProfileRole.objects.filter(profile=user, is_active=True).select_related('establishment') + roles = [] + for role in roles_qs: + logo_url = "" + if role.establishment.logo: + # Construit l'URL complète pour le logo + logo_url = f"{role.establishment.logo.url}" + roles.append({ + "role_type": role.role_type, + "establishment__id": role.establishment.id, + "establishment__name": role.establishment.name, + "establishment__evaluation_frequency": role.establishment.evaluation_frequency, + "establishment__total_capacity": role.establishment.total_capacity, + "establishment__api_docuseal": role.establishment.api_docuseal, + "establishment__logo": logo_url, + }) + + # Générer le JWT avec la bonne syntaxe datetime + access_payload = { + 'user_id': user.id, + 'email': user.email, + 'roleIndexLoginDefault': user.roleIndexLoginDefault, + 'roles': roles, + 'type': 'access', + 'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'], + 'iat': datetime.utcnow(), + } + + access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM']) + # Générer le Refresh Token (exp: 7 jours) + refresh_payload = { + 'user_id': user.id, + 'type': 'refresh', + 'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'], + 'iat': datetime.utcnow(), + } + refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM']) + return access_token, refresh_token + except Exception as e: + logger.error(f"Erreur lors de la création du token: {str(e)}") + return None + +class RefreshJWTView(APIView): + @swagger_auto_schema( + operation_description="Rafraîchir le token d'accès", + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['refresh'], + properties={ + 'refresh': openapi.Schema(type=openapi.TYPE_STRING) + } + ), + responses={ + 200: openapi.Response('Token rafraîchi avec succès', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'token': openapi.Schema(type=openapi.TYPE_STRING), + 'refresh': openapi.Schema(type=openapi.TYPE_STRING), + } + )), + 400: openapi.Response('Échec du rafraîchissement', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'errorMessage': openapi.Schema(type=openapi.TYPE_STRING) + } + )) + } + ) + @method_decorator(csrf_exempt, name='dispatch') + def post(self, request): + data = JSONParser().parse(request) + refresh_token = data.get("refresh") + logger.info(f"Token reçu: {refresh_token[:20]}...") # Ne pas logger le token complet pour la sécurité + + if not refresh_token: + return JsonResponse({'errorMessage': 'Refresh token manquant'}, status=400) + + try: + # Décoder le Refresh Token + logger.info("Tentative de décodage du token") + logger.info(f"Algorithme utilisé: {settings.SIMPLE_JWT['ALGORITHM']}") + + # Vérifier le format du token avant décodage + token_parts = refresh_token.split('.') + if len(token_parts) != 3: + logger.error("Format de token invalide - pas 3 parties") + return JsonResponse({'errorMessage': 'Format de token invalide'}, status=400) + + payload = jwt.decode( + refresh_token, + settings.SIMPLE_JWT['SIGNING_KEY'], + algorithms=[settings.SIMPLE_JWT['ALGORITHM']] # Noter le passage en liste + ) + + logger.info(f"Token décodé avec succès. Type: {payload.get('type')}") + # Vérifier s'il s'agit bien d'un Refresh Token + if payload.get('type') != 'refresh': + return JsonResponse({'errorMessage': 'Token invalide'}, status=400) + + # Récupérer les informations utilisateur + user = Profile.objects.get(id=payload['user_id']) + if not user: + return JsonResponse({'errorMessage': 'Utilisateur non trouvé'}, status=404) + + new_access_token, new_refresh_token = makeToken(user) + + return JsonResponse({'token': new_access_token, 'refresh': new_refresh_token}, status=200) + + except ExpiredSignatureError as e: + logger.error(f"Token expiré: {str(e)}") + return JsonResponse({'errorMessage': 'Refresh token expiré'}, status=400) + except InvalidTokenError as e: + logger.error(f"Token invalide: {str(e)}") + return JsonResponse({'errorMessage': f'Token invalide: {str(e)}'}, status=400) + except Exception as e: + logger.error(f"Erreur inattendue: {str(e)}") + return JsonResponse({'errorMessage': f'Erreur inattendue: {str(e)}'}, status=400) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') class SubscribeView(APIView): - - def get(self, request): - return JsonResponse({ - 'message':'', - 'errorFields':'', - 'errorMessage':'' - }, safe=False) - + @swagger_auto_schema( + operation_description="Inscription utilisateur", + manual_parameters=[ + openapi.Parameter( + 'establishment_id', openapi.IN_QUERY, + description="ID de l'établissement", + type=openapi.TYPE_INTEGER, + required=True + ) + ], + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['email', 'password1', 'password2'], + properties={ + 'email': openapi.Schema(type=openapi.TYPE_STRING), + 'password1': openapi.Schema(type=openapi.TYPE_STRING), + 'password2': openapi.Schema(type=openapi.TYPE_STRING) + } + ), + responses={ + 200: openapi.Response('Inscription réussie', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING), + 'errorMessage': openapi.Schema(type=openapi.TYPE_STRING), + 'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT), + 'id': openapi.Schema(type=openapi.TYPE_INTEGER) + } + )) + } + ) def post(self, request): - retourErreur = error.returnMessage[error.BAD_URL] + retourErreur = '' retour = '' - newProfilConnection=JSONParser().parse(request) - + newProfilConnection = JSONParser().parse(request) + establishment_id = newProfilConnection['establishment_id'] + validatorSubscription = validator.ValidatorSubscription(data=newProfilConnection) validationOk, errorFields = validatorSubscription.validate() - if validationOk: - # On vérifie que l'email existe : si ce n'est pas le cas, on retourne une erreur profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email')) - if profil == None: + if profil is None: retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS] else: - if profil.is_active: - retourErreur=error.returnMessage[error.PROFIL_ACTIVE] - return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields, "id":profil.id}, safe=False) + # Vérifier si le profil a déjà un rôle actif pour l'établissement donné + active_roles = ProfileRole.objects.filter(profile=profil, establishment=establishment_id, is_active=True) + if active_roles.exists(): + retourErreur = error.returnMessage[error.PROFIL_ACTIVE] + return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False) else: try: profil.set_password(newProfilConnection.get('password1')) - profil.is_active = True profil.full_clean() profil.save() - clear_cache() + + # Récupérer le ProfileRole existant pour l'établissement et le profil + profile_role = ProfileRole.objects.filter(profile=profil, establishment=establishment_id).first() + if profile_role: + profile_role.is_active = True + profile_role.save() + else: + # Si aucun ProfileRole n'existe, en créer un nouveau + role_data = { + 'profile': profil.id, + 'establishment': establishment_id, + 'is_active': True + } + role_serializer = ProfileRoleSerializer(data=role_data) + if role_serializer.is_valid(): + role_serializer.save() + else: + return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE] - retourErreur='' - return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields, "id":profil.id}, safe=False) + retourErreur = '' + return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False) except ValidationError as e: retourErreur = error.returnMessage[error.WRONG_MAIL_FORMAT] - return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields}, safe=False) - - return JsonResponse({'message':retour, 'errorMessage':retourErreur, "errorFields":errorFields, "id":-1}, safe=False) - + return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False) + return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": -1}, safe=False) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') class NewPasswordView(APIView): - def get(self, request): - return JsonResponse({ - 'message':'', - 'errorFields':'', - 'errorMessage':'' - }, safe=False) - + @swagger_auto_schema( + operation_description="Demande de nouveau mot de passe", + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['email'], + properties={ + 'email': openapi.Schema(type=openapi.TYPE_STRING) + } + ), + responses={ + 200: openapi.Response('Demande réussie', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING), + 'errorMessage': openapi.Schema(type=openapi.TYPE_STRING), + 'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT) + } + )) + } + ) def post(self, request): - retourErreur = error.returnMessage[error.BAD_URL] + retourErreur = '' retour = '' - newProfilConnection=JSONParser().parse(request) + newProfilConnection = JSONParser().parse(request) validatorNewPassword = validator.ValidatorNewPassword(data=newProfilConnection) validationOk, errorFields = validatorNewPassword.validate() if validationOk: - profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email')) - if profil == None: + if profil is None: retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS] else: - # Génération d'une URL provisoire pour modifier le mot de passe - profil.code = util.genereRandomCode(12) - profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS) - profil.save() - clear_cache() - retourErreur = '' - retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD]%(newProfilConnection.get('email')) - mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code) + try: + # Génération d'une URL provisoire pour modifier le mot de passe + profil.code = util.genereRandomCode(12) + profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS) + profil.save() + retourErreur = '' + retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD] % (newProfilConnection.get('email')) + mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code) + except ValidationError as e: + retourErreur = error.returnMessage[error.WRONG_MAIL_FORMAT] + return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False) - return JsonResponse({'message':retour, 'errorMessage':retourErreur, "errorFields":errorFields}, safe=False) + return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') class ResetPasswordView(APIView): - def get(self, request, _uuid): - return JsonResponse({ - 'message':'', - 'errorFields':'', - 'errorMessage':'' - }, safe=False) - - def post(self, request, _uuid): - retourErreur = error.returnMessage[error.BAD_URL] + @swagger_auto_schema( + operation_description="Réinitialisation du mot de passe", + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['password1', 'password2'], + properties={ + 'password1': openapi.Schema(type=openapi.TYPE_STRING), + 'password2': openapi.Schema(type=openapi.TYPE_STRING) + } + ), + responses={ + 200: openapi.Response('Réinitialisation réussie', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING), + 'errorMessage': openapi.Schema(type=openapi.TYPE_STRING), + 'errorFields': openapi.Schema(type=openapi.TYPE_OBJECT) + } + )) + } + ) + def post(self, request, code): + retourErreur = '' retour = '' - newProfilConnection=JSONParser().parse(request) + newProfilConnection = JSONParser().parse(request) validatorResetPassword = validator.ValidatorResetPassword(data=newProfilConnection) validationOk, errorFields = validatorResetPassword.validate() - - profil = bdd.getObject(Profile, "code", _uuid) + + profil = bdd.getObject(Profile, "code", code) if profil: if datetime.strptime(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'), '%d-%m-%Y %H:%M') > datetime.strptime(profil.datePeremption, '%d-%m-%Y %H:%M'): - retourErreur = error.returnMessage[error.EXPIRED_URL]%(_uuid) + retourErreur = error.returnMessage[error.EXPIRED_URL] elif validationOk: retour = error.returnMessage[error.PASSWORD_CHANGED] @@ -262,7 +521,149 @@ class ResetPasswordView(APIView): profil.code = '' profil.datePeremption = '' profil.save() - clear_cache() - retourErreur='' + retourErreur = '' - return JsonResponse({'message':retour, "errorMessage":retourErreur, "errorFields":errorFields}, safe=False) + return JsonResponse({'message': retour, "errorMessage": retourErreur, "errorFields": errorFields}, safe=False) + +class ProfileRoleView(APIView): + pagination_class = CustomProfilesPagination + @swagger_auto_schema( + operation_description="Obtenir la liste des profile_roles", + responses={200: ProfileRoleSerializer(many=True)} + ) + def get(self, request): + filter = request.GET.get('filter', '').strip() + page_size = request.GET.get('page_size', None) + establishment_id = request.GET.get('establishment_id', None) + + # Gestion du page_size + if page_size is not None: + try: + page_size = int(page_size) + except ValueError: + page_size = settings.NB_RESULT_PROFILES_PER_PAGE + + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + # Récupérer les ProfileRole en fonction du filtre + profiles_roles_List = ProfileRole.objects.filter(establishment_id=establishment_id) + + if filter == 'parents': + profiles_roles_List = profiles_roles_List.filter(role_type=ProfileRole.RoleType.PROFIL_PARENT) + elif filter == 'school': + profiles_roles_List = profiles_roles_List.filter( + Q(role_type=ProfileRole.RoleType.PROFIL_ECOLE) | + Q(role_type=ProfileRole.RoleType.PROFIL_ADMIN) + ) + else: + return JsonResponse({'error': 'Filtre invalide'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + # Trier les résultats par date de mise à jour + profiles_roles_List = profiles_roles_List.distinct().order_by('-updated_date') + + if not profiles_roles_List: + return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False) + + # Pagination + paginator = self.pagination_class() + page = paginator.paginate_queryset(profiles_roles_List, request) + + if page is not None: + profile_roles_serializer = ProfileRoleSerializer(page, many=True) + response_data = paginator.get_paginated_response(profile_roles_serializer.data) + return JsonResponse(response_data, safe=False) + + return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False) + + @swagger_auto_schema( + operation_description="Créer un nouveau profile_role", + request_body=ProfileRoleSerializer, + responses={ + 200: ProfileRoleSerializer, + 400: 'Données invalides' + } + ) + def post(self, request): + profile_role_data = JSONParser().parse(request) + profile_role_serializer = ProfileRoleSerializer(data=profile_role_data) + + if profile_role_serializer.is_valid(): + profile_role = profile_role_serializer.save() + return JsonResponse(profile_role_serializer.data, safe=False) + + return JsonResponse(profile_role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class ProfileRoleSimpleView(APIView): + @swagger_auto_schema( + operation_description="Obtenir un profile_role par son ID", + responses={200: ProfileRoleSerializer} + ) + def get(self, request, id): + profile_role = bdd.getObject(ProfileRole, "id", id) + profile_role_serializer = ProfileRoleSerializer(profile_role) + return JsonResponse(profile_role_serializer.data, safe=False) + + @swagger_auto_schema( + operation_description="Mettre à jour un profile_role", + request_body=ProfileRoleSerializer, + responses={ + 200: 'Mise à jour réussie', + 400: 'Données invalides' + } + ) + def put(self, request, id): + data = JSONParser().parse(request) + profile_role = ProfileRole.objects.get(id=id) + profile_role_serializer = ProfileRoleSerializer(profile_role, data=data) + if profile_role_serializer.is_valid(): + profile_role_serializer.save() + return JsonResponse(profile_role_serializer.data, safe=False) + + return JsonResponse(profile_role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + @swagger_auto_schema( + operation_description="Supprimer un profile_role", + responses={200: 'Suppression réussie'} + ) + def delete(self, request, id): + try: + # Récupérer le ProfileRole + profile_role = ProfileRole.objects.get(id=id) + profile = profile_role.profile + + # Vérifier si le ProfileRole est de type PARENT + if profile_role.role_type == ProfileRole.RoleType.PROFIL_PARENT: + guardian = Guardian.objects.filter(profile_role=profile_role).first() + if guardian: + # Vérifier si ce Guardian est rattaché à des élèves + for student in guardian.student_set.all(): + # Vérifier si l'élève n'a pas d'autres Guardians + other_guardians = student.guardians.exclude(id=guardian.id) + if not other_guardians.exists(): + return JsonResponse( + {"error": f"Impossible de supprimer ce profil car l'élève {student.first_name} {student.last_name} n'aura plus de responsable légal."}, + status=status.HTTP_400_BAD_REQUEST + ) + + # Supprimer le ProfileRole + profile_role.delete() + + # Vérifier si le profil n'a plus de rôles associés + if not ProfileRole.objects.filter(profile=profile).exists(): + profile.delete() + + return JsonResponse({'message': 'Suppression réussie'}, safe=False) + + except ProfileRole.DoesNotExist: + return JsonResponse( + {"error": "ProfileRole non trouvé."}, + status=status.HTTP_404_NOT_FOUND + ) + except Exception as e: + return JsonResponse( + {"error": f"Une erreur est survenue : {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) \ No newline at end of file diff --git a/Back-End/Common/__init__.py b/Back-End/Common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/Common/admin.py b/Back-End/Common/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Back-End/Common/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Back-End/Common/apps.py b/Back-End/Common/apps.py new file mode 100644 index 0000000..19006f2 --- /dev/null +++ b/Back-End/Common/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class CommonConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'Common' + + def ready(self): + import Common.signals \ No newline at end of file diff --git a/Back-End/Common/migrations/0001_initial.py b/Back-End/Common/migrations/0001_initial.py new file mode 100644 index 0000000..5725c9e --- /dev/null +++ b/Back-End/Common/migrations/0001_initial.py @@ -0,0 +1,63 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Cycle', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.IntegerField(unique=True)), + ('label', models.CharField(max_length=50)), + ], + ), + migrations.CreateModel( + name='Domain', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('cycle', models.IntegerField(choices=[(1, 'Cycle 1'), (2, 'Cycle 2'), (3, 'Cycle 3'), (4, 'Cycle 4')])), + ], + ), + migrations.CreateModel( + name='PaymentModeType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(max_length=50, unique=True)), + ('label', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='PaymentPlanType', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(max_length=50, unique=True)), + ('label', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='categories', to='Common.domain')), + ], + ), + migrations.CreateModel( + name='Level', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('cycle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='levels', to='Common.cycle')), + ], + ), + ] diff --git a/Back-End/Common/migrations/__init__.py b/Back-End/Common/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/Common/models.py b/Back-End/Common/models.py new file mode 100644 index 0000000..990f8da --- /dev/null +++ b/Back-End/Common/models.py @@ -0,0 +1,43 @@ +from django.db import models + +class Domain(models.Model): + name = models.CharField(max_length=255) + cycle = models.IntegerField(choices=[(1, 'Cycle 1'), (2, 'Cycle 2'), (3, 'Cycle 3'), (4, 'Cycle 4')]) + + def __str__(self): + return f"{self.name} (Cycle {self.cycle})" + +class Category(models.Model): + name = models.CharField(max_length=255) + domain = models.ForeignKey(Domain, on_delete=models.CASCADE, related_name='categories') + + def __str__(self): + return self.name + +class PaymentPlanType(models.Model): + code = models.CharField(max_length=50, unique=True) + label = models.CharField(max_length=255) + + def __str__(self): + return self.label + +class PaymentModeType(models.Model): + code = models.CharField(max_length=50, unique=True) + label = models.CharField(max_length=255) + + def __str__(self): + return self.label + +class Cycle(models.Model): + number = models.IntegerField(unique=True) + label = models.CharField(max_length=50) + + def __str__(self): + return f"Cycle {self.number} - {self.label}" + +class Level(models.Model): + name = models.CharField(max_length=50) + cycle = models.ForeignKey(Cycle, on_delete=models.CASCADE, related_name='levels') + + def __str__(self): + return self.name \ No newline at end of file diff --git a/Back-End/Common/serializers.py b/Back-End/Common/serializers.py new file mode 100644 index 0000000..7d4737f --- /dev/null +++ b/Back-End/Common/serializers.py @@ -0,0 +1,15 @@ +from rest_framework import serializers +from Common.models import ( + Domain, + Category +) + +class DomainSerializer(serializers.ModelSerializer): + class Meta: + model = Domain + fields = '__all__' + +class CategorySerializer(serializers.ModelSerializer): + class Meta: + model = Category + fields = '__all__' \ No newline at end of file diff --git a/Back-End/Common/signals.py b/Back-End/Common/signals.py new file mode 100644 index 0000000..7ffd67c --- /dev/null +++ b/Back-End/Common/signals.py @@ -0,0 +1,98 @@ +import json +import os +from django.db.models.signals import post_migrate +from django.dispatch import receiver +from Common.models import Domain, Category, PaymentModeType, PaymentPlanType, Cycle, Level +from School.models import Competency + +@receiver(post_migrate) +def common_post_migrate(sender, **kwargs): + if sender.name != "School": + return + + # Chemin absolu vers le répertoire Back-End + backend_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + # Chemins vers les fichiers JSON + json_files = [ + ("Cycle1.json", 1), + ("Cycle2.json", 2), + ("Cycle3.json", 3), + ("Cycle4.json", 4), + ] + + for file_name, cycle in json_files: + json_file_path = os.path.join(backend_dir, "competences", file_name) + + if not os.path.exists(json_file_path): + print(f"Fichier JSON introuvable : {json_file_path}") + continue + + with open(json_file_path, 'r', encoding='utf-8') as file: + data = json.load(file) + + for domain_data in data['domaines']: + # Vérifiez si le domaine existe déjà + domain, _ = Domain.objects.get_or_create(name=domain_data['nom'], cycle=cycle) + + for category_data in domain_data['categories']: + # Vérifiez si la catégorie existe déjà + category, _ = Category.objects.get_or_create(name=category_data['nom'], domain=domain) + + for competency_data in category_data['competences']: + # Vérifiez si la compétence existe déjà + competency, _ = Competency.objects.get_or_create( + name=competency_data['nom'], + end_of_cycle=competency_data.get('fin_cycle', False), + level=competency_data.get('niveau'), + category=category + ) + print(f"Données importées depuis : {json_file_path}") + + payment_mode_types = [ + {"code": "SEPA", "label": "Prélèvement SEPA"}, + {"code": "TRANSFER", "label": "Virement"}, + {"code": "CHECK", "label": "Chèque"}, + {"code": "CASH", "label": "Espèce"}, + ] + for mode in payment_mode_types: + PaymentModeType.objects.get_or_create(code=mode["code"], defaults={"label": mode["label"]}) + + payment_plan_types = [ + {"code": "ONE_TIME", "label": "1 fois"}, + {"code": "THREE_TIMES", "label": "3 fois"}, + {"code": "TEN_TIMES", "label": "10 fois"}, + {"code": "TWELVE_TIMES", "label": "12 fois"}, + ] + for plan in payment_plan_types: + PaymentPlanType.objects.get_or_create(code=plan["code"], defaults={"label": plan["label"]}) + + # Création des cycles + cycles_data = [ + {"number": 1, "label": "Cycle 1"}, + {"number": 2, "label": "Cycle 2"}, + {"number": 3, "label": "Cycle 3"}, + {"number": 4, "label": "Cycle 4"}, + ] + cycle_objs = {} + for cycle in cycles_data: + obj, _ = Cycle.objects.get_or_create(number=cycle["number"], defaults={"label": cycle["label"]}) + cycle_objs[cycle["number"]] = obj + + # Création des niveaux et association au cycle + levels_data = [ + {"name": "TPS", "cycle": 1}, + {"name": "PS", "cycle": 1}, + {"name": "MS", "cycle": 1}, + {"name": "GS", "cycle": 1}, + {"name": "CP", "cycle": 2}, + {"name": "CE1", "cycle": 2}, + {"name": "CE2", "cycle": 2}, + {"name": "CM1", "cycle": 3}, + {"name": "CM2", "cycle": 3}, + ] + for level in levels_data: + Level.objects.get_or_create( + name=level["name"], + defaults={"cycle": cycle_objs[level["cycle"]]} + ) \ No newline at end of file diff --git a/Back-End/Common/tests.py b/Back-End/Common/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/Back-End/Common/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Back-End/Common/urls.py b/Back-End/Common/urls.py new file mode 100644 index 0000000..4dd0ba9 --- /dev/null +++ b/Back-End/Common/urls.py @@ -0,0 +1,14 @@ +from django.urls import path, re_path + +from .views import ( + DomainListCreateView, DomainDetailView, + CategoryListCreateView, CategoryDetailView, +) + +urlpatterns = [ + re_path(r'^domains$', DomainListCreateView.as_view(), name="domain_list_create"), + re_path(r'^domains/(?P[0-9]+)$', DomainDetailView.as_view(), name="domain_detail"), + + re_path(r'^categories$', CategoryListCreateView.as_view(), name="category_list_create"), + re_path(r'^categories/(?P[0-9]+)$', CategoryDetailView.as_view(), name="category_detail"), +] \ No newline at end of file diff --git a/Back-End/Common/views.py b/Back-End/Common/views.py new file mode 100644 index 0000000..a8fa8e2 --- /dev/null +++ b/Back-End/Common/views.py @@ -0,0 +1,110 @@ +from django.http.response import JsonResponse +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from django.utils.decorators import method_decorator +from rest_framework.parsers import JSONParser +from rest_framework.views import APIView +from rest_framework import status +from .models import ( + Domain, + Category +) +from .serializers import ( + DomainSerializer, + CategorySerializer +) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DomainListCreateView(APIView): + def get(self, request): + domains = Domain.objects.all() + serializer = DomainSerializer(domains, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = DomainSerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DomainDetailView(APIView): + def get(self, request, id): + try: + domain = Domain.objects.get(id=id) + serializer = DomainSerializer(domain) + return JsonResponse(serializer.data, safe=False) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + domain = Domain.objects.get(id=id) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = DomainSerializer(domain, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + domain = Domain.objects.get(id=id) + domain.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Domain.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + +# Répète la même logique pour Category, Competency, EstablishmentCompetency + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CategoryListCreateView(APIView): + def get(self, request): + categories = Category.objects.all() + serializer = CategorySerializer(categories, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = CategorySerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CategoryDetailView(APIView): + def get(self, request, id): + try: + category = Category.objects.get(id=id) + serializer = CategorySerializer(category) + return JsonResponse(serializer.data, safe=False) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + category = Category.objects.get(id=id) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = CategorySerializer(category, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + category = Category.objects.get(id=id) + category.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Category.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) diff --git a/Back-End/Dockerfile b/Back-End/Dockerfile index 7f46014..25f4136 100644 --- a/Back-End/Dockerfile +++ b/Back-End/Dockerfile @@ -3,6 +3,7 @@ # The first instruction is what image we want to base our container on # We Use an official Python runtime as a parent image FROM python:3.12.7 +WORKDIR /Back-End # Allows docker to cache installed dependencies between builds COPY requirements.txt requirements.txt @@ -10,11 +11,5 @@ RUN pip install -r requirements.txt # Mounts the application code to the image COPY . . -WORKDIR /Back-End EXPOSE 8080 - -ENV DJANGO_SETTINGS_MODULE N3wtSchool.settings -ENV DJANGO_SUPERUSER_PASSWORD=admin -ENV DJANGO_SUPERUSER_USERNAME=admin -ENV DJANGO_SUPERUSER_EMAIL=admin@n3wtschool.com diff --git a/Back-End/DocuSeal/__init__.py b/Back-End/DocuSeal/__init__.py new file mode 100644 index 0000000..d64ffa4 --- /dev/null +++ b/Back-End/DocuSeal/__init__.py @@ -0,0 +1 @@ +# This file is intentionally left blank to make this directory a Python package. diff --git a/Back-End/DocuSeal/urls.py b/Back-End/DocuSeal/urls.py new file mode 100644 index 0000000..06a434b --- /dev/null +++ b/Back-End/DocuSeal/urls.py @@ -0,0 +1,9 @@ +from django.urls import path, re_path +from .views import generate_jwt_token, clone_template, remove_template, download_template + +urlpatterns = [ + re_path(r'generateToken$', generate_jwt_token, name='generate_jwt_token'), + re_path(r'cloneTemplate$', clone_template, name='clone_template'), + re_path(r'removeTemplate/(?P[0-9]+)$', remove_template, name='remove_template'), + re_path(r'downloadTemplate/(?P[\w-]+)$', download_template, name='download_template') +] diff --git a/Back-End/DocuSeal/views.py b/Back-End/DocuSeal/views.py new file mode 100644 index 0000000..9a21cb3 --- /dev/null +++ b/Back-End/DocuSeal/views.py @@ -0,0 +1,200 @@ +from django.conf import settings +from django.views.decorators.csrf import csrf_exempt +from rest_framework.decorators import api_view +from rest_framework.response import Response +from rest_framework import status +import jwt +import datetime +import requests +from Establishment.models import Establishment + +@csrf_exempt +@api_view(['POST']) +def generate_jwt_token(request): + # Récupérer l'établissement concerné (par ID ou autre info transmise) + establishment_id = request.data.get('establishment_id') + if not establishment_id: + return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST) + + try: + establishment = Establishment.objects.get(id=establishment_id) + except Establishment.DoesNotExist: + return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND) + + # Vérifier la clé API reçue dans le header + api_key = request.headers.get('X-Auth-Token') + if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal: + return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED) + + # Récupérer les données de la requête + user_email = request.data.get('user_email') + documents_urls = request.data.get('documents_urls', []) + template_id = request.data.get('id') + + if not user_email: + return Response({'error': 'User email is required'}, status=status.HTTP_400_BAD_REQUEST) + + # Utiliser la clé API de l'établissement comme secret JWT + jwt_secret = establishment.api_docuseal + jwt_algorithm = settings.DOCUSEAL_JWT['ALGORITHM'] + expiration_delta = settings.DOCUSEAL_JWT['EXPIRATION_DELTA'] + + payload = { + 'user_email': user_email, + 'documents_urls': documents_urls, + 'template_id': template_id, + 'exp': datetime.datetime.utcnow() + expiration_delta + } + + token = jwt.encode(payload, jwt_secret, algorithm=jwt_algorithm) + return Response({'token': token}, status=status.HTTP_200_OK) + +@csrf_exempt +@api_view(['POST']) +def clone_template(request): + # Récupérer l'établissement concerné + establishment_id = request.data.get('establishment_id') + print(f"establishment_id : {establishment_id}") + if not establishment_id: + return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST) + + try: + establishment = Establishment.objects.get(id=establishment_id) + except Establishment.DoesNotExist: + return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND) + + # Vérifier la clé API reçue dans le header + api_key = request.headers.get('X-Auth-Token') + if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal: + return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED) + + # Récupérer les données de la requête + document_id = request.data.get('templateId') + email = request.data.get('email') + is_required = request.data.get('is_required') + + # Vérifier les données requises + if not document_id: + return Response({'error': 'template ID is required'}, status=status.HTTP_400_BAD_REQUEST) + + # URL de l'API de DocuSeal pour cloner le template + clone_url = f'https://docuseal.com/api/templates/{document_id}/clone' + + # Faire la requête pour cloner le template + try: + response = requests.post(clone_url, headers={ + 'Content-Type': 'application/json', + 'X-Auth-Token': establishment.api_docuseal + }) + + if response.status_code != status.HTTP_200_OK: + return Response({'error': 'Failed to clone template'}, status=response.status_code) + + data = response.json() + + if is_required: + # URL de l'API de DocuSeal pour créer une submission + submission_url = f'https://docuseal.com/api/submissions' + + try: + clone_id = data['id'] + response = requests.post(submission_url, json={ + 'template_id': clone_id, + 'send_email': False, + 'submitters': [{'email': email}] + }, headers={ + 'Content-Type': 'application/json', + 'X-Auth-Token': establishment.api_docuseal + }) + + if response.status_code != status.HTTP_200_OK: + return Response({'error': 'Failed to create submission'}, status=response.status_code) + + data = response.json() + data[0]['id'] = clone_id + return Response(data[0], status=status.HTTP_200_OK) + + except requests.RequestException as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + else: + print(f'NOT REQUIRED -> on ne crée pas de submission') + return Response(data, status=status.HTTP_200_OK) + + except requests.RequestException as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +@csrf_exempt +@api_view(['DELETE']) +def remove_template(request, id): + # Récupérer l'établissement concerné + establishment_id = request.GET.get('establishment_id') + if not establishment_id: + return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST) + + try: + establishment = Establishment.objects.get(id=establishment_id) + except Establishment.DoesNotExist: + return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND) + + # Vérifier la clé API reçue dans le header + api_key = request.headers.get('X-Auth-Token') + if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal: + return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED) + + # URL de l'API de DocuSeal pour supprimer le template + + clone_url = f'https://docuseal.com/api/templates/{id}' + + try: + response = requests.delete(clone_url, headers={ + 'X-Auth-Token': establishment.api_docuseal + }) + + if response.status_code != status.HTTP_200_OK: + return Response({'error': 'Failed to remove template'}, status=response.status_code) + + data = response.json() + return Response(data, status=status.HTTP_200_OK) + + except requests.RequestException as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +@csrf_exempt +@api_view(['GET']) +def download_template(request, slug): + # Récupérer l'établissement concerné + establishment_id = request.GET.get('establishment_id') + if not establishment_id: + return Response({'error': 'establishment_id requis'}, status=status.HTTP_400_BAD_REQUEST) + + try: + establishment = Establishment.objects.get(id=establishment_id) + except Establishment.DoesNotExist: + return Response({'error': "Établissement introuvable"}, status=status.HTTP_404_NOT_FOUND) + + # Vérifier la clé API reçue dans le header + api_key = request.headers.get('X-Auth-Token') + if not api_key or not establishment.api_docuseal or api_key != establishment.api_docuseal: + return Response({'error': 'Clé API invalide'}, status=status.HTTP_401_UNAUTHORIZED) + + # Vérifier les données requises + if not slug: + return Response({'error': 'slug is required'}, status=status.HTTP_400_BAD_REQUEST) + + # URL de l'API de DocuSeal pour télécharger le template + download_url = f'https://docuseal.com/submitters/{slug}/download' + + try: + response = requests.get(download_url, headers={ + 'Content-Type': 'application/json', + 'X-Auth-Token': establishment.api_docuseal + }) + + if response.status_code != status.HTTP_200_OK: + return Response({'error': 'Failed to download template'}, status=response.status_code) + + data = response.json() + return Response(data, status=status.HTTP_200_OK) + + except requests.RequestException as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/Back-End/Establishment/__init__.py b/Back-End/Establishment/__init__.py new file mode 100644 index 0000000..95d2d94 --- /dev/null +++ b/Back-End/Establishment/__init__.py @@ -0,0 +1 @@ +default_app_config = 'Establishment.apps.EstablishmentConfig' diff --git a/Back-End/Establishment/admin.py b/Back-End/Establishment/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Back-End/Establishment/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Back-End/Establishment/apps.py b/Back-End/Establishment/apps.py new file mode 100644 index 0000000..ee46399 --- /dev/null +++ b/Back-End/Establishment/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class EstablishmentConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'Establishment' + + diff --git a/Back-End/Establishment/migrations/0001_initial.py b/Back-End/Establishment/migrations/0001_initial.py new file mode 100644 index 0000000..4ec0546 --- /dev/null +++ b/Back-End/Establishment/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Establishment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('address', models.CharField(max_length=255)), + ('total_capacity', models.IntegerField()), + ('establishment_type', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(choices=[(1, 'Maternelle'), (2, 'Primaire'), (3, 'Secondaire')]), size=None)), + ('evaluation_frequency', models.IntegerField(choices=[(1, 'Trimestre'), (2, 'Semestre'), (3, 'Année')], default=1)), + ('licence_code', models.CharField(blank=True, max_length=100)), + ('is_active', models.BooleanField(default=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/Back-End/Establishment/migrations/0002_establishment_api_docuseal.py b/Back-End/Establishment/migrations/0002_establishment_api_docuseal.py new file mode 100644 index 0000000..568376b --- /dev/null +++ b/Back-End/Establishment/migrations/0002_establishment_api_docuseal.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2025-05-30 07:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('Establishment', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='establishment', + name='api_docuseal', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/Back-End/Establishment/migrations/0003_establishment_logo.py b/Back-End/Establishment/migrations/0003_establishment_logo.py new file mode 100644 index 0000000..1d89e51 --- /dev/null +++ b/Back-End/Establishment/migrations/0003_establishment_logo.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.3 on 2025-05-31 09:56 + +import Establishment.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('Establishment', '0002_establishment_api_docuseal'), + ] + + operations = [ + migrations.AddField( + model_name='establishment', + name='logo', + field=models.FileField(blank=True, null=True, upload_to=Establishment.models.registration_logo_upload_to), + ), + ] diff --git a/Back-End/Establishment/migrations/__init__.py b/Back-End/Establishment/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/Establishment/models.py b/Back-End/Establishment/models.py new file mode 100644 index 0000000..27d6099 --- /dev/null +++ b/Back-End/Establishment/models.py @@ -0,0 +1,38 @@ +from django.db import models +from django.contrib.postgres.fields import ArrayField +from django.utils.translation import gettext_lazy as _ + +import os + +def registration_logo_upload_to(instance, filename): + ext = os.path.splitext(filename)[1] + return f"logos/school_{instance.pk}/logo{ext}" + +class StructureType(models.IntegerChoices): + MATERNELLE = 1, _('Maternelle') + PRIMAIRE = 2, _('Primaire') + SECONDAIRE = 3, _('Secondaire') + +class EvaluationFrequency(models.IntegerChoices): + TRIMESTER = 1, _("Trimestre") + SEMESTER = 2, _("Semestre") + YEAR = 3, _("Année") + +class Establishment(models.Model): + name = models.CharField(max_length=255) + address = models.CharField(max_length=255) + total_capacity = models.IntegerField() + establishment_type = ArrayField(models.IntegerField(choices=StructureType.choices)) + evaluation_frequency = models.IntegerField(choices=EvaluationFrequency.choices, default=EvaluationFrequency.TRIMESTER) + licence_code = models.CharField(max_length=100, blank=True) + is_active = models.BooleanField(default=True) + created_at = models.DateTimeField(auto_now_add=True) + api_docuseal = models.CharField(max_length=255, blank=True, null=True) + logo = models.FileField( + upload_to=registration_logo_upload_to, + null=True, + blank=True + ) + + def __str__(self): + return self.name \ No newline at end of file diff --git a/Back-End/Establishment/serializers.py b/Back-End/Establishment/serializers.py new file mode 100644 index 0000000..50909ca --- /dev/null +++ b/Back-End/Establishment/serializers.py @@ -0,0 +1,91 @@ +from rest_framework import serializers +from .models import Establishment +from School.models import SchoolClass, Teacher, Speciality, Fee, Discount, PaymentMode, PaymentPlan +from Subscriptions.models import RegistrationForm, RegistrationFileGroup +from Auth.models import Profile + +class EstablishmentSerializer(serializers.ModelSerializer): + profile_count = serializers.SerializerMethodField() + profiles = serializers.SerializerMethodField() + school_class_count = serializers.SerializerMethodField() + school_classes = serializers.SerializerMethodField() + teacher_count = serializers.SerializerMethodField() + teachers = serializers.SerializerMethodField() + speciality_count = serializers.SerializerMethodField() + specialities = serializers.SerializerMethodField() + fee_count = serializers.SerializerMethodField() + fees = serializers.SerializerMethodField() + discount_count = serializers.SerializerMethodField() + discounts = serializers.SerializerMethodField() + active_payment_mode_count = serializers.SerializerMethodField() + active_payment_modes = serializers.SerializerMethodField() + active_payment_plan_count = serializers.SerializerMethodField() + active_payment_plans = serializers.SerializerMethodField() + file_group_count = serializers.SerializerMethodField() + file_groups = serializers.SerializerMethodField() + registration_form_count = serializers.SerializerMethodField() + registration_forms = serializers.SerializerMethodField() + + class Meta: + model = Establishment + fields = '__all__' + + def get_profile_count(self, obj): + return Profile.objects.filter(roles__establishment=obj).distinct().count() + + def get_profiles(self, obj): + return list(Profile.objects.filter(roles__establishment=obj).distinct().values_list('email', flat=True)) + + def get_school_class_count(self, obj): + return SchoolClass.objects.filter(establishment=obj).distinct().count() + + def get_school_classes(self, obj): + return list(SchoolClass.objects.filter(establishment=obj).distinct().values_list('atmosphere_name', flat=True)) + + def get_teacher_count(self, obj): + return Teacher.objects.filter(profile_role__establishment=obj).distinct().count() + + def get_teachers(self, obj): + return list(Teacher.objects.filter(profile_role__establishment=obj).distinct().values_list('last_name', 'first_name')) + + def get_speciality_count(self, obj): + return Speciality.objects.filter(establishment=obj).distinct().count() + + def get_specialities(self, obj): + return list(Speciality.objects.filter(establishment=obj).distinct().values_list('name', flat=True)) + + def get_fee_count(self, obj): + return Fee.objects.filter(establishment=obj).distinct().count() + + def get_fees(self, obj): + return list(Fee.objects.filter(establishment=obj).distinct().values_list('name', flat=True)) + + def get_discount_count(self, obj): + return Discount.objects.filter(establishment=obj).distinct().count() + + def get_discounts(self, obj): + return list(Discount.objects.filter(establishment=obj).distinct().values_list('name', flat=True)) + + def get_active_payment_mode_count(self, obj): + return PaymentMode.objects.filter(establishment=obj).distinct().count() + + def get_active_payment_modes(self, obj): + return list(PaymentMode.objects.filter(establishment=obj).distinct().values_list('mode', flat=True)) + + def get_active_payment_plan_count(self, obj): + return PaymentPlan.objects.filter(establishment=obj).distinct().count() + + def get_active_payment_plans(self, obj): + return list(PaymentPlan.objects.filter(establishment=obj).distinct().values_list('plan_type', flat=True)) + + def get_file_group_count(self, obj): + return RegistrationFileGroup.objects.filter(establishment=obj).distinct().count() + + def get_file_groups(self, obj): + return list(RegistrationFileGroup.objects.filter(establishment=obj).distinct().values_list('name', flat=True)) + + def get_registration_form_count(self, obj): + return RegistrationForm.objects.filter(establishment=obj).distinct().count() + + def get_registration_forms(self, obj): + return list(RegistrationForm.objects.filter(establishment=obj).distinct().values_list('student__last_name', 'student__first_name')) \ No newline at end of file diff --git a/Back-End/Establishment/urls.py b/Back-End/Establishment/urls.py new file mode 100644 index 0000000..de40903 --- /dev/null +++ b/Back-End/Establishment/urls.py @@ -0,0 +1,7 @@ +from django.urls import path, re_path +from .views import EstablishmentListCreateView, EstablishmentDetailView + +urlpatterns = [ + re_path(r'^establishments$', EstablishmentListCreateView.as_view(), name='establishment_list_create'), + re_path(r'^establishments/(?P[0-9]+)$', EstablishmentDetailView.as_view(), name="establishment_detail"), +] \ No newline at end of file diff --git a/Back-End/Establishment/views.py b/Back-End/Establishment/views.py new file mode 100644 index 0000000..0f028cb --- /dev/null +++ b/Back-End/Establishment/views.py @@ -0,0 +1,132 @@ +from django.http.response import JsonResponse +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from django.utils.decorators import method_decorator +from rest_framework.parsers import JSONParser, MultiPartParser, FormParser +from rest_framework.views import APIView +from rest_framework import status +from .models import Establishment +from .serializers import EstablishmentSerializer +from N3wtSchool.bdd import delete_object, getAllObjects, getObject +from School.models import EstablishmentCompetency, Competency +from django.db.models import Q +from Auth.models import Profile, ProfileRole, Directeur +from Settings.models import SMTPSettings +import N3wtSchool.mailManager as mailer +import os +from N3wtSchool import settings + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentListCreateView(APIView): + def get(self, request): + establishments = getAllObjects(Establishment) + establishments_serializer = EstablishmentSerializer(establishments, many=True) + return JsonResponse(establishments_serializer.data, safe=False, status=status.HTTP_200_OK) + + def post(self, request): + establishment_data = JSONParser().parse(request) + try: + establishment, data = create_establishment_with_directeur(establishment_data) + # Création des EstablishmentCompetency pour chaque compétence existante + competencies = Competency.objects.filter( + Q(end_of_cycle=True) | ~Q(level=None) + ) + for competency in competencies: + EstablishmentCompetency.objects.get_or_create( + establishment=establishment, + competency=competency, + defaults={'is_required': True} + ) + return JsonResponse(data, safe=False, status=status.HTTP_201_CREATED) + except Exception as e: + return JsonResponse({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentDetailView(APIView): + parser_classes = [MultiPartParser, FormParser] + + def get(self, request, id=None): + try: + establishment = Establishment.objects.get(id=id) + establishment_serializer = EstablishmentSerializer(establishment) + return JsonResponse(establishment_serializer.data, safe=False) + except Establishment.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + """ + Met à jour un établissement existant. + Accepte les données en multipart/form-data pour permettre l'upload de fichiers (ex : logo). + """ + try: + establishment = Establishment.objects.get(id=id) + except Establishment.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + # Utilise request.data pour supporter multipart/form-data (fichiers et champs classiques) + establishment_serializer = EstablishmentSerializer(establishment, data=request.data, partial=True) + if establishment_serializer.is_valid(): + establishment_serializer.save() + return JsonResponse(establishment_serializer.data, safe=False, status=status.HTTP_200_OK) + return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + return delete_object(Establishment, id) + +def create_establishment_with_directeur(establishment_data): + # Extraction des sous-objets + # school_name = establishment_data.get("name") + directeur_data = establishment_data.pop("directeur", None) + smtp_settings_data = establishment_data.pop("smtp_settings", {}) + + # Vérification de la présence du directeur + if not directeur_data or not directeur_data.get("email"): + raise ValueError("Le champ 'directeur.email' est obligatoire.") + + directeur_email = directeur_data.get("email") + last_name = directeur_data.get("last_name", "") + first_name = directeur_data.get("first_name", "") + password = directeur_data.get("password", "Provisoire01!") + + # Création ou récupération du profil utilisateur + profile, created = Profile.objects.get_or_create( + email=directeur_email, + defaults={"username": directeur_email} + ) + if created or not profile.has_usable_password(): + profile.set_password(password) + profile.save() + + # Création de l'établissement + establishment_serializer = EstablishmentSerializer(data=establishment_data) + establishment_serializer.is_valid(raise_exception=True) + # base_dir = os.path.join(settings.MEDIA_ROOT, f"logo/school_{school_name}") + # os.makedirs(base_dir, exist_ok=True) + establishment = establishment_serializer.save() + + # Création ou récupération du ProfileRole ADMIN pour ce profil et cet établissement + profile_role, _ = ProfileRole.objects.get_or_create( + profile=profile, + establishment=establishment, + role_type=ProfileRole.RoleType.PROFIL_ADMIN, + defaults={"is_active": False} + ) + + # Création ou mise à jour du Directeur lié à ce ProfileRole + Directeur.objects.update_or_create( + profile_role=profile_role, + defaults={ + "last_name": last_name, + "first_name": first_name + } + ) + + # Création du SMTPSettings rattaché à l'établissement si des données sont fournies + if smtp_settings_data: + smtp_settings_data["establishment"] = establishment + SMTPSettings.objects.create(**smtp_settings_data) + + # Envoi du mail + mailer.sendRegistrationDirector(directeur_email, establishment.pk) + return establishment, establishment_serializer.data \ No newline at end of file diff --git a/Back-End/GestionEmail/__init__.py b/Back-End/GestionEmail/__init__.py new file mode 100644 index 0000000..0eec8a6 --- /dev/null +++ b/Back-End/GestionEmail/__init__.py @@ -0,0 +1 @@ +default_app_config = 'GestionEmail.apps.GestionEmailConfig' diff --git a/Back-End/GestionEmail/apps.py b/Back-End/GestionEmail/apps.py new file mode 100644 index 0000000..8ccc650 --- /dev/null +++ b/Back-End/GestionEmail/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + +class GestionEmailConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'GestionEmail' diff --git a/Back-End/GestionEmail/urls.py b/Back-End/GestionEmail/urls.py new file mode 100644 index 0000000..e9ffee7 --- /dev/null +++ b/Back-End/GestionEmail/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from .views import ( + SendEmailView, search_recipients +) + +urlpatterns = [ + path('send-email/', SendEmailView.as_view(), name='send_email'), + path('search-recipients/', search_recipients, name='search_recipients'), +] \ No newline at end of file diff --git a/Back-End/GestionEmail/views.py b/Back-End/GestionEmail/views.py new file mode 100644 index 0000000..8ccbabb --- /dev/null +++ b/Back-End/GestionEmail/views.py @@ -0,0 +1,119 @@ +from django.http.response import JsonResponse +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from django.db.models import Q +from Auth.models import Profile, ProfileRole + +import N3wtSchool.mailManager as mailer +from N3wtSchool import bdd +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from rest_framework.exceptions import NotFound +import uuid +import logging + +# Ajouter un logger pour debug +logger = logging.getLogger(__name__) + +class SendEmailView(APIView): + """ + API pour envoyer des emails aux parents et professeurs. + """ + def post(self, request): + # Ajouter du debug + logger.info(f"Request data received: {request.data}") + logger.info(f"Request content type: {request.content_type}") + + data = request.data + recipients = data.get('recipients', []) + cc = data.get('cc', []) + bcc = data.get('bcc', []) + subject = data.get('subject', 'Notification') + message = data.get('message', '') + establishment_id = data.get('establishment_id', '') + + # Debug des données reçues + logger.info(f"Recipients: {recipients} (type: {type(recipients)})") + logger.info(f"CC: {cc} (type: {type(cc)})") + logger.info(f"BCC: {bcc} (type: {type(bcc)})") + logger.info(f"Subject: {subject}") + logger.info(f"Message length: {len(message) if message else 0}") + logger.info(f"Establishment ID: {establishment_id}") + + if not recipients or not message: + logger.error("Recipients or message missing") + logger.error(f"Recipients empty: {not recipients}, Message empty: {not message}") + logger.error(f"Recipients value: '{recipients}', Message value: '{message}'") + return Response({'error': 'Les destinataires et le message sont requis.'}, status=status.HTTP_400_BAD_REQUEST) + + try: + # Récupérer la connexion SMTP + logger.info("Tentative de récupération de la connexion SMTP...") + connection = mailer.getConnection(establishment_id) + logger.info(f"Connexion SMTP récupérée: {connection}") + + # Envoyer l'email + logger.info("Tentative d'envoi de l'email...") + result = mailer.sendMail( + subject=subject, + message=message, + recipients=recipients, + cc=cc, + bcc=bcc, + attachments=[], + connection=connection + ) + logger.info(f"Email envoyé avec succès: {result}") + return result + except NotFound as e: + logger.error(f"NotFound error: {str(e)}") + return Response({'error': str(e)}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + logger.error(f"Exception during email sending: {str(e)}") + logger.error(f"Exception type: {type(e)}") + import traceback + logger.error(f"Traceback: {traceback.format_exc()}") + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +def search_recipients(request): + """ + API pour rechercher des destinataires en fonction d'un terme de recherche et d'un établissement. + """ + query = request.GET.get('q', '').strip() # Récupérer le terme de recherche depuis les paramètres GET + establishment_id = request.GET.get('establishment_id', None) # Récupérer l'ID de l'établissement + + if not query: + return JsonResponse([], safe=False) # Retourner une liste vide si aucun terme n'est fourni + + if not establishment_id: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + # Rechercher dans les champs pertinents (nom, prénom, email) et filtrer par establishment_id + profiles = Profile.objects.filter( + Q(first_name__icontains=query) | + Q(last_name__icontains=query) | + Q(email__icontains=query), + roles__establishment_id=establishment_id, # Utiliser 'roles' au lieu de 'profilerole' + roles__is_active=True # Filtrer uniquement les ProfileRole actifs + ).distinct() + + # Construire la réponse avec les rôles associés + results = [] + for profile in profiles: + profile_roles = ProfileRole.objects.filter( + profile=profile, + establishment_id=establishment_id, + is_active=True # Inclure uniquement les ProfileRole actifs + ).values( + 'id', 'role_type', 'establishment__name', 'is_active' + ) + results.append({ + 'id': profile.id, + 'first_name': profile.first_name, + 'last_name': profile.last_name, + 'email': profile.email, + 'roles': list(profile_roles) # Inclure tous les rôles actifs associés pour cet établissement + }) + + return JsonResponse(results, safe=False) diff --git a/Back-End/GestionMessagerie/consumers.py b/Back-End/GestionMessagerie/consumers.py new file mode 100644 index 0000000..ba32b84 --- /dev/null +++ b/Back-End/GestionMessagerie/consumers.py @@ -0,0 +1,627 @@ +import json +import logging +from uuid import UUID +from decimal import Decimal +from datetime import datetime +from channels.generic.websocket import AsyncWebsocketConsumer +from channels.db import database_sync_to_async +from django.utils import timezone +from .models import Conversation, ConversationParticipant, Message, UserPresence, MessageRead +from .serializers import MessageSerializer, ConversationSerializer +from Auth.models import Profile + +logger = logging.getLogger(__name__) + +def serialize_for_websocket(data): + """ + Convertit récursivement les objets non-sérialisables en JSON en types sérialisables + """ + if isinstance(data, dict): + return {key: serialize_for_websocket(value) for key, value in data.items()} + elif isinstance(data, list): + return [serialize_for_websocket(item) for item in data] + elif isinstance(data, UUID): + return str(data) + elif isinstance(data, Decimal): + return float(data) + elif isinstance(data, datetime): + return data.isoformat() + else: + return data + +class ChatConsumer(AsyncWebsocketConsumer): + """Consumer WebSocket pour la messagerie instantanée""" + + async def connect(self): + self.user_id = self.scope['url_route']['kwargs']['user_id'] + self.user_group_name = f'user_{self.user_id}' + + # Vérifier si l'utilisateur est authentifié + user = self.scope.get('user') + if not user or user.is_anonymous: + logger.warning(f"Tentative de connexion WebSocket non authentifiée pour user_id: {self.user_id}") + await self.close() + return + + # Vérifier que l'utilisateur connecté correspond à l'user_id de l'URL + if str(user.id) != str(self.user_id): + logger.warning(f"Tentative d'accès WebSocket avec user_id incorrect: {self.user_id} vs {user.id}") + await self.close() + return + + self.user = user + + # Rejoindre le groupe utilisateur + await self.channel_layer.group_add( + self.user_group_name, + self.channel_name + ) + + # Rejoindre les groupes des conversations de l'utilisateur + conversations = await self.get_user_conversations(self.user_id) + for conversation in conversations: + await self.channel_layer.group_add( + f'conversation_{conversation.id}', + self.channel_name + ) + + # Mettre à jour le statut de présence + presence = await self.update_user_presence(self.user_id, 'online') + + # Notifier les autres utilisateurs du changement de statut + if presence: + await self.broadcast_presence_update(self.user_id, 'online') + + # Envoyer les statuts de présence existants des autres utilisateurs connectés + await self.send_existing_user_presences() + + await self.accept() + + logger.info(f"User {self.user_id} connected to chat") + + async def send_existing_user_presences(self): + """Envoyer les statuts de présence existants des autres utilisateurs connectés""" + try: + # Obtenir toutes les conversations de cet utilisateur + conversations = await self.get_user_conversations(self.user_id) + + # Créer un set pour éviter les doublons d'utilisateurs + other_users = set() + + # Pour chaque conversation, récupérer les participants + for conversation in conversations: + participants = await self.get_conversation_participants(conversation.id) + for participant in participants: + if participant.id != self.user_id: + other_users.add(participant.id) + + # Envoyer le statut de présence pour chaque utilisateur + for user_id in other_users: + presence = await self.get_user_presence(user_id) + if presence: + await self.send(text_data=json.dumps({ + 'type': 'presence_update', + 'user_id': str(user_id), + 'status': presence.status + })) + + except Exception as e: + logger.error(f"Error sending existing user presences: {str(e)}") + + async def disconnect(self, close_code): + # Quitter tous les groupes + await self.channel_layer.group_discard( + self.user_group_name, + self.channel_name + ) + + if hasattr(self, 'user'): + conversations = await self.get_user_conversations(self.user_id) + for conversation in conversations: + await self.channel_layer.group_discard( + f'conversation_{conversation.id}', + self.channel_name + ) + + # Mettre à jour le statut de présence + presence = await self.update_user_presence(self.user_id, 'offline') + + # Notifier les autres utilisateurs du changement de statut + if presence: + await self.broadcast_presence_update(self.user_id, 'offline') + + logger.info(f"User {self.user_id} disconnected from chat") + + async def receive(self, text_data): + """Recevoir et traiter les messages du client""" + try: + text_data_json = json.loads(text_data) + message_type = text_data_json.get('type') + + if message_type == 'send_message': + await self.handle_send_message(text_data_json) + elif message_type == 'typing_start': + await self.handle_typing_start(text_data_json) + elif message_type == 'typing_stop': + await self.handle_typing_stop(text_data_json) + elif message_type == 'mark_as_read': + await self.handle_mark_as_read(text_data_json) + elif message_type == 'join_conversation': + await self.handle_join_conversation(text_data_json) + elif message_type == 'leave_conversation': + await self.handle_leave_conversation(text_data_json) + elif message_type == 'presence_update': + await self.handle_presence_update(text_data_json) + else: + logger.warning(f"Unknown message type: {message_type}") + await self.send(text_data=json.dumps({ + 'type': 'error', + 'message': f'Unknown message type: {message_type}' + })) + + except json.JSONDecodeError: + await self.send(text_data=json.dumps({ + 'type': 'error', + 'message': 'Invalid JSON format' + })) + except Exception as e: + logger.error(f"Error in receive: {str(e)}") + await self.send(text_data=json.dumps({ + 'type': 'error', + 'message': 'Internal server error' + })) + + async def handle_send_message(self, data): + """Gérer l'envoi d'un nouveau message""" + conversation_id = data.get('conversation_id') + content = data.get('content', '').strip() + message_type = data.get('message_type', 'text') + attachment = data.get('attachment') + + # Vérifier qu'on a soit du contenu, soit un fichier + if not conversation_id or (not content and not attachment): + await self.send(text_data=json.dumps({ + 'type': 'error', + 'message': 'Conversation ID and content or attachment are required' + })) + return + + # Vérifier que l'utilisateur peut envoyer dans cette conversation + can_send = await self.can_user_send_message(self.user_id, conversation_id) + if not can_send: + await self.send(text_data=json.dumps({ + 'type': 'error', + 'message': 'You cannot send messages to this conversation' + })) + return + + # Créer le message avec ou sans fichier + message = await self.create_message(conversation_id, self.user_id, content, message_type, attachment) + if not message: + await self.send(text_data=json.dumps({ + 'type': 'error', + 'message': 'Failed to create message' + })) + return + + # Sérialiser le message + message_data = await self.serialize_message(message) + + # Auto-marquer comme lu pour les utilisateurs connectés (présents dans la conversation) + await self.auto_mark_read_for_online_users(message, conversation_id) + + # Envoyer le message à tous les participants de la conversation + await self.channel_layer.group_send( + f'conversation_{conversation_id}', + { + 'type': 'chat_message', + 'message': message_data + } + ) + + async def handle_typing_start(self, data): + """Gérer le début de frappe""" + conversation_id = data.get('conversation_id') + if conversation_id: + await self.update_typing_status(self.user_id, conversation_id, True) + + # Récupérer le nom de l'utilisateur + user_name = await self.get_user_display_name(self.user_id) + + await self.channel_layer.group_send( + f'conversation_{conversation_id}', + { + 'type': 'typing_status', + 'user_id': str(self.user_id), + 'user_name': user_name, + 'is_typing': True, + 'conversation_id': str(conversation_id) + } + ) + + async def handle_typing_stop(self, data): + """Gérer l'arrêt de frappe""" + conversation_id = data.get('conversation_id') + if conversation_id: + await self.update_typing_status(self.user_id, conversation_id, False) + + # Récupérer le nom de l'utilisateur + user_name = await self.get_user_display_name(self.user_id) + + await self.channel_layer.group_send( + f'conversation_{conversation_id}', + { + 'type': 'typing_status', + 'user_id': str(self.user_id), + 'user_name': user_name, + 'is_typing': False, + 'conversation_id': str(conversation_id) + } + ) + + async def handle_mark_as_read(self, data): + """Marquer les messages comme lus""" + conversation_id = data.get('conversation_id') + if conversation_id: + await self.mark_conversation_as_read(self.user_id, conversation_id) + await self.channel_layer.group_send( + f'conversation_{conversation_id}', + { + 'type': 'messages_read', + 'user_id': str(self.user_id), + 'conversation_id': str(conversation_id) + } + ) + + async def handle_join_conversation(self, data): + """Rejoindre une conversation""" + conversation_id = data.get('conversation_id') + if conversation_id: + await self.channel_layer.group_add( + f'conversation_{conversation_id}', + self.channel_name + ) + + async def handle_leave_conversation(self, data): + """Quitter une conversation""" + conversation_id = data.get('conversation_id') + if conversation_id: + await self.channel_layer.group_discard( + f'conversation_{conversation_id}', + self.channel_name + ) + + async def handle_presence_update(self, data): + """Gérer les mises à jour de présence""" + status = data.get('status', 'online') + if status in ['online', 'offline', 'away']: + await self.update_user_presence(self.user_id, status) + await self.broadcast_presence_update(self.user_id, status) + + # Méthodes pour recevoir les messages des groupes + async def chat_message(self, event): + """Envoyer un message de chat au WebSocket""" + message_data = serialize_for_websocket(event['message']) + await self.send(text_data=json.dumps({ + 'type': 'new_message', + 'message': message_data + })) + + async def typing_status(self, event): + """Envoyer le statut de frappe""" + # Ne pas envoyer à l'expéditeur + if str(event['user_id']) != str(self.user_id): + await self.send(text_data=json.dumps({ + 'type': 'typing_status', + 'user_id': str(event['user_id']), + 'user_name': event.get('user_name', ''), + 'is_typing': event['is_typing'], + 'conversation_id': str(event['conversation_id']) + })) + + async def messages_read(self, event): + """Notifier que des messages ont été lus""" + if str(event['user_id']) != str(self.user_id): + await self.send(text_data=json.dumps({ + 'type': 'messages_read', + 'user_id': str(event['user_id']), + 'conversation_id': str(event['conversation_id']) + })) + + async def user_presence_update(self, event): + """Notifier d'un changement de présence""" + await self.send(text_data=json.dumps({ + 'type': 'presence_update', + 'user_id': str(event['user_id']), + 'status': event['status'] + })) + + async def new_conversation_notification(self, event): + """Notifier d'une nouvelle conversation""" + conversation = serialize_for_websocket(event['conversation']) + conversation_id = conversation['id'] + + # Rejoindre automatiquement le groupe de la nouvelle conversation + await self.channel_layer.group_add( + f'conversation_{conversation_id}', + self.channel_name + ) + + # Envoyer la notification au client + await self.send(text_data=json.dumps({ + 'type': 'new_conversation', + 'conversation': conversation + })) + + # Diffuser les présences des participants de cette nouvelle conversation + try: + participants = await self.get_conversation_participants(conversation_id) + for participant in participants: + # Ne pas diffuser sa propre présence à soi-même + if participant.id != self.user_id: + presence = await self.get_user_presence(participant.id) + if presence: + await self.send(text_data=json.dumps({ + 'type': 'presence_update', + 'user_id': str(participant.id), + 'status': presence.status + })) + except Exception as e: + logger.error(f"Error sending presence updates for new conversation: {str(e)}") + + async def broadcast_presence_update(self, user_id, status): + """Diffuser un changement de statut de présence à tous les utilisateurs connectés""" + try: + # Obtenir tous les utilisateurs qui ont des conversations avec cet utilisateur + user_conversations = await self.get_user_conversations(user_id) + + # Créer un set pour éviter les doublons d'utilisateurs + notified_users = set() + + # Pour chaque conversation, notifier tous les participants + for conversation in user_conversations: + participants = await self.get_conversation_participants(conversation.id) + for participant in participants: + if participant.id != user_id and participant.id not in notified_users: + notified_users.add(participant.id) + # Envoyer la notification au groupe utilisateur + await self.channel_layer.group_send( + f'user_{participant.id}', + { + 'type': 'user_presence_update', + 'user_id': user_id, + 'status': status + } + ) + + logger.info(f"Broadcasted presence update for user {user_id} ({status}) to {len(notified_users)} users") + + except Exception as e: + logger.error(f"Error broadcasting presence update: {str(e)}") + + # Méthodes d'accès aux données (database_sync_to_async) + @database_sync_to_async + def get_user(self, user_id): + try: + return Profile.objects.get(id=user_id) + except Profile.DoesNotExist: + return None + + @database_sync_to_async + def get_user_display_name(self, user_id): + """Obtenir le nom d'affichage d'un utilisateur""" + try: + user = Profile.objects.get(id=user_id) + if user.first_name and user.last_name: + return f"{user.first_name} {user.last_name}" + elif user.first_name: + return user.first_name + elif user.last_name: + return user.last_name + else: + return user.email or f"Utilisateur {user_id}" + except Profile.DoesNotExist: + return f"Utilisateur {user_id}" + + @database_sync_to_async + def get_user_conversations(self, user_id): + return list(Conversation.objects.filter( + participants__participant_id=user_id, + participants__is_active=True, + is_active=True + ).distinct()) + + @database_sync_to_async + def get_conversation_participants(self, conversation_id): + """Obtenir tous les participants d'une conversation""" + return list(Profile.objects.filter( + conversation_participants__conversation_id=conversation_id, + conversation_participants__is_active=True + )) + + @database_sync_to_async + def get_conversations_data(self, user_id): + try: + user = Profile.objects.get(id=user_id) + conversations = Conversation.objects.filter( + participants__participant=user, + participants__is_active=True, + is_active=True + ).distinct() + + serializer = ConversationSerializer(conversations, many=True, context={'user': user}) + return serializer.data + except Exception as e: + logger.error(f"Error getting conversations data: {str(e)}") + return [] + + @database_sync_to_async + def can_user_send_message(self, user_id, conversation_id): + return ConversationParticipant.objects.filter( + conversation_id=conversation_id, + participant_id=user_id, + is_active=True + ).exists() + + @database_sync_to_async + def create_message(self, conversation_id, sender_id, content, message_type, attachment=None): + try: + conversation = Conversation.objects.get(id=conversation_id) + sender = Profile.objects.get(id=sender_id) + + message_data = { + 'conversation': conversation, + 'sender': sender, + 'content': content, + 'message_type': message_type + } + + # Ajouter les informations du fichier si présent + if attachment: + message_data.update({ + 'file_url': attachment.get('fileUrl'), + 'file_name': attachment.get('fileName'), + 'file_size': attachment.get('fileSize'), + 'file_type': attachment.get('fileType'), + }) + # Si c'est un fichier, s'assurer que le type de message est correct + if attachment.get('fileType', '').startswith('image/'): + message_data['message_type'] = 'image' + else: + message_data['message_type'] = 'file' + + message = Message.objects.create(**message_data) + + # Mettre à jour l'activité de la conversation + conversation.last_activity = message.created_at + conversation.save(update_fields=['last_activity']) + + return message + except Exception as e: + logger.error(f"Error creating message: {str(e)}") + return None + + @database_sync_to_async + def serialize_message(self, message): + serializer = MessageSerializer(message) + return serialize_for_websocket(serializer.data) + + @database_sync_to_async + def get_user_presence(self, user_id): + """Récupérer la présence d'un utilisateur""" + try: + return UserPresence.objects.get(user_id=user_id) + except UserPresence.DoesNotExist: + return None + + @database_sync_to_async + def update_user_presence(self, user_id, status): + try: + user = Profile.objects.get(id=user_id) + presence, created = UserPresence.objects.get_or_create(user=user) + old_status = presence.status + presence.status = status + presence.save() + + # Si le statut a changé, notifier les autres utilisateurs + if old_status != status or created: + logger.info(f"User {user_id} presence changed from {old_status} to {status}") + + return presence + except Exception as e: + logger.error(f"Error updating user presence: {str(e)}") + return None + + @database_sync_to_async + def update_typing_status(self, user_id, conversation_id, is_typing): + try: + user = Profile.objects.get(id=user_id) + presence, created = UserPresence.objects.get_or_create(user=user) + if is_typing: + conversation = Conversation.objects.get(id=conversation_id) + presence.is_typing_in = conversation + else: + presence.is_typing_in = None + presence.save() + except Exception as e: + logger.error(f"Error updating typing status: {str(e)}") + + @database_sync_to_async + def mark_conversation_as_read(self, user_id, conversation_id): + """Marquer tous les messages non lus d'une conversation comme lus""" + try: + # Mettre à jour le last_read_at du participant + participant = ConversationParticipant.objects.get( + conversation_id=conversation_id, + participant_id=user_id + ) + current_time = timezone.now() + participant.last_read_at = current_time + participant.save(update_fields=['last_read_at']) + + # Créer des enregistrements MessageRead pour tous les messages non lus + # que l'utilisateur n'a pas encore explicitement lus + unread_messages = Message.objects.filter( + conversation_id=conversation_id, + created_at__lte=current_time, + is_deleted=False + ).exclude( + sender_id=user_id # Exclure ses propres messages + ).exclude( + read_by__participant_id=user_id # Exclure les messages déjà marqués comme lus + ) + + # Créer les enregistrements MessageRead en batch + message_reads = [ + MessageRead(message=message, participant_id=user_id, read_at=current_time) + for message in unread_messages + ] + + if message_reads: + MessageRead.objects.bulk_create(message_reads, ignore_conflicts=True) + logger.info(f"Marked {len(message_reads)} messages as read for user {user_id} in conversation {conversation_id}") + + except Exception as e: + logger.error(f"Error marking conversation as read: {str(e)}") + + @database_sync_to_async + def auto_mark_read_for_online_users(self, message, conversation_id): + """Auto-marquer comme lu pour les utilisateurs en ligne dans la conversation""" + try: + # Obtenir tous les participants de la conversation (synchrone) + participants = ConversationParticipant.objects.filter( + conversation_id=conversation_id, + is_active=True + ).exclude(participant_id=message.sender.id) + + # Obtenir l'heure de création du message + message_time = message.created_at + + # Préparer les enregistrements MessageRead à créer + message_reads = [] + + for participant_obj in participants: + participant = participant_obj.participant + + # Vérifier si l'utilisateur est en ligne (synchrone) + try: + presence = UserPresence.objects.filter(user=participant).first() + if presence and presence.status == 'online': + # Vérifier qu'il n'existe pas déjà un enregistrement MessageRead + if not MessageRead.objects.filter(message=message, participant=participant).exists(): + message_reads.append(MessageRead( + message=message, + participant=participant, + read_at=message_time + )) + except: + # En cas d'erreur de présence, ne pas marquer comme lu + continue + + # Créer les enregistrements MessageRead en batch + if message_reads: + MessageRead.objects.bulk_create(message_reads, ignore_conflicts=True) + logger.info(f"Auto-marked {len(message_reads)} messages as read for online users in conversation {conversation_id}") + + except Exception as e: + logger.error(f"Error in auto_mark_read_for_online_users: {str(e)}") diff --git a/Back-End/GestionMessagerie/middleware.py b/Back-End/GestionMessagerie/middleware.py new file mode 100644 index 0000000..68c6f24 --- /dev/null +++ b/Back-End/GestionMessagerie/middleware.py @@ -0,0 +1,108 @@ +import jwt +import logging +from urllib.parse import parse_qs +from django.conf import settings +from django.contrib.auth.models import AnonymousUser +from channels.middleware import BaseMiddleware +from channels.db import database_sync_to_async +from Auth.models import Profile + +logger = logging.getLogger(__name__) + +@database_sync_to_async +def get_user(user_id): + """Récupérer l'utilisateur de manière asynchrone""" + try: + return Profile.objects.get(id=user_id) + except Profile.DoesNotExist: + return AnonymousUser() + +class JWTAuthMiddleware(BaseMiddleware): + """Middleware pour l'authentification JWT dans les WebSockets""" + + def __init__(self, inner): + super().__init__(inner) + + def _check_cors_origin(self, scope): + """Vérifier si l'origine est autorisée pour les WebSockets""" + origin = None + + # Récupérer l'origine depuis les headers + for name, value in scope.get('headers', []): + if name == b'origin': + origin = value.decode('latin1') + break + + if not origin: + logger.warning("Aucune origine trouvée dans les headers WebSocket") + return False + + # Récupérer les origines autorisées depuis la configuration CORS + allowed_origins = getattr(settings, 'CORS_ALLOWED_ORIGINS', []) + + # Si CORS_ORIGIN_ALLOW_ALL est True, autoriser toutes les origines + if getattr(settings, 'CORS_ORIGIN_ALLOW_ALL', False): + logger.info(f"Origine WebSocket autorisée (CORS_ORIGIN_ALLOW_ALL): {origin}") + return True + + # Vérifier si l'origine est dans la liste des origines autorisées + if origin in allowed_origins: + logger.info(f"Origine WebSocket autorisée: {origin}") + return True + + logger.warning(f"Origine WebSocket non autorisée: {origin}. Origines autorisées: {allowed_origins}") + return False + + async def __call__(self, scope, receive, send): + # Vérifier les CORS pour les WebSockets + if not self._check_cors_origin(scope): + logger.error("Connexion WebSocket refusée: origine non autorisée") + # Fermer la connexion WebSocket avec un code d'erreur + await send({ + 'type': 'websocket.close', + 'code': 1008 # Policy Violation + }) + return + + # Extraire le token de l'URL + query_string = parse_qs(scope['query_string'].decode()) + token = query_string.get('token') + + if token: + token = token[0] + try: + # Décoder le token JWT + payload = jwt.decode( + token, + settings.SIMPLE_JWT['SIGNING_KEY'], + algorithms=[settings.SIMPLE_JWT['ALGORITHM']] + ) + + # Vérifier que c'est un token d'accès + if payload.get('type') != 'access': + logger.warning(f"Token type invalide: {payload.get('type')}") + scope['user'] = AnonymousUser() + else: + # Récupérer l'utilisateur + user_id = payload.get('user_id') + user = await get_user(user_id) + scope['user'] = user + logger.info(f"Utilisateur authentifié via JWT: {user.email if hasattr(user, 'email') else 'Unknown'}") + + except jwt.ExpiredSignatureError: + logger.warning("Token JWT expiré") + scope['user'] = AnonymousUser() + except jwt.InvalidTokenError as e: + logger.warning(f"Token JWT invalide: {str(e)}") + scope['user'] = AnonymousUser() + except Exception as e: + logger.error(f"Erreur lors de l'authentification JWT: {str(e)}") + scope['user'] = AnonymousUser() + else: + scope['user'] = AnonymousUser() + + return await super().__call__(scope, receive, send) + +def JWTAuthMiddlewareStack(inner): + """Stack middleware pour l'authentification JWT""" + return JWTAuthMiddleware(inner) diff --git a/Back-End/GestionMessagerie/migrations/0001_initial.py b/Back-End/GestionMessagerie/migrations/0001_initial.py new file mode 100644 index 0000000..914c079 --- /dev/null +++ b/Back-End/GestionMessagerie/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Messagerie', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('objet', models.CharField(blank=True, default='', max_length=200)), + ('corpus', models.CharField(blank=True, default='', max_length=200)), + ('date_envoi', models.DateTimeField(auto_now_add=True)), + ('is_read', models.BooleanField(default=False)), + ('conversation_id', models.CharField(blank=True, default='', max_length=100)), + ('destinataire', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='messages_recus', to=settings.AUTH_USER_MODEL)), + ('emetteur', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='messages_envoyes', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/Back-End/GestionMessagerie/migrations/0002_conversation_message_userpresence_and_more.py b/Back-End/GestionMessagerie/migrations/0002_conversation_message_userpresence_and_more.py new file mode 100644 index 0000000..abe623e --- /dev/null +++ b/Back-End/GestionMessagerie/migrations/0002_conversation_message_userpresence_and_more.py @@ -0,0 +1,87 @@ +# Generated by Django 5.1.3 on 2025-05-30 07:40 + +import django.db.models.deletion +import django.utils.timezone +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('GestionMessagerie', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Conversation', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(blank=True, max_length=255, null=True)), + ('conversation_type', models.CharField(choices=[('private', 'Privée'), ('group', 'Groupe')], default='private', max_length=10)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('last_activity', models.DateTimeField(default=django.utils.timezone.now)), + ('is_active', models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name='Message', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('content', models.TextField()), + ('message_type', models.CharField(choices=[('text', 'Texte'), ('file', 'Fichier'), ('image', 'Image'), ('system', 'Système')], default='text', max_length=10)), + ('file_url', models.URLField(blank=True, null=True)), + ('file_name', models.CharField(blank=True, max_length=255, null=True)), + ('file_size', models.BigIntegerField(blank=True, null=True)), + ('file_type', models.CharField(blank=True, max_length=100, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('is_edited', models.BooleanField(default=False)), + ('is_deleted', models.BooleanField(default=False)), + ('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='GestionMessagerie.conversation')), + ('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['created_at'], + }, + ), + migrations.CreateModel( + name='UserPresence', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('online', 'En ligne'), ('away', 'Absent'), ('busy', 'Occupé'), ('offline', 'Hors ligne')], default='offline', max_length=10)), + ('last_seen', models.DateTimeField(default=django.utils.timezone.now)), + ('is_typing_in', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='typing_users', to='GestionMessagerie.conversation')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='presence', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='ConversationParticipant', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('joined_at', models.DateTimeField(auto_now_add=True)), + ('last_read_at', models.DateTimeField(default=django.utils.timezone.now)), + ('is_active', models.BooleanField(default=True)), + ('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participants', to='GestionMessagerie.conversation')), + ('participant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conversation_participants', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('conversation', 'participant')}, + }, + ), + migrations.CreateModel( + name='MessageRead', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('read_at', models.DateTimeField(auto_now_add=True)), + ('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='read_by', to='GestionMessagerie.message')), + ('participant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='read_messages', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('message', 'participant')}, + }, + ), + ] diff --git a/Back-End/GestionMessagerie/migrations/__init__.py b/Back-End/GestionMessagerie/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/GestionMessagerie/models.py b/Back-End/GestionMessagerie/models.py index a17f91f..57e1a9c 100644 --- a/Back-End/GestionMessagerie/models.py +++ b/Back-End/GestionMessagerie/models.py @@ -1,15 +1,113 @@ -from django.contrib.auth.models import AbstractUser from django.db import models -from django.utils.translation import gettext_lazy as _ -from django.conf import settings from Auth.models import Profile - +from django.utils import timezone +import uuid + +class Conversation(models.Model): + """Modèle pour gérer les conversations entre utilisateurs""" + CONVERSATION_TYPES = [ + ('private', 'Privée'), + ('group', 'Groupe'), + ] + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=255, blank=True, null=True) # Nom pour les groupes + conversation_type = models.CharField(max_length=10, choices=CONVERSATION_TYPES, default='private') + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + last_activity = models.DateTimeField(default=timezone.now) + is_active = models.BooleanField(default=True) + + def __str__(self): + if self.name: + return f'Conversation: {self.name}' + return f'Conversation {self.id}' + + def get_participants(self): + return Profile.objects.filter(conversation_participants__conversation=self) + +class ConversationParticipant(models.Model): + """Modèle pour gérer les participants d'une conversation""" + conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='participants') + participant = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='conversation_participants') + joined_at = models.DateTimeField(auto_now_add=True) + last_read_at = models.DateTimeField(default=timezone.now) + is_active = models.BooleanField(default=True) + + class Meta: + unique_together = ('conversation', 'participant') + + def __str__(self): + return f'{self.participant.email} in {self.conversation.id}' + +class Message(models.Model): + """Modèle pour les messages instantanés""" + MESSAGE_TYPES = [ + ('text', 'Texte'), + ('file', 'Fichier'), + ('image', 'Image'), + ('system', 'Système'), + ] + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='messages') + sender = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='sent_messages') + content = models.TextField() + message_type = models.CharField(max_length=10, choices=MESSAGE_TYPES, default='text') + file_url = models.URLField(blank=True, null=True) # Pour les fichiers/images + file_name = models.CharField(max_length=255, blank=True, null=True) # Nom original du fichier + file_size = models.BigIntegerField(blank=True, null=True) # Taille en bytes + file_type = models.CharField(max_length=100, blank=True, null=True) # MIME type + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + is_edited = models.BooleanField(default=False) + is_deleted = models.BooleanField(default=False) + + class Meta: + ordering = ['created_at'] + + def __str__(self): + return f'Message from {self.sender.email} at {self.created_at}' + +class MessageRead(models.Model): + """Modèle pour tracker les messages lus par chaque participant""" + message = models.ForeignKey(Message, on_delete=models.CASCADE, related_name='read_by') + participant = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='read_messages') + read_at = models.DateTimeField(auto_now_add=True) + + class Meta: + unique_together = ('message', 'participant') + + def __str__(self): + return f'{self.participant.email} read {self.message.id}' + +class UserPresence(models.Model): + """Modèle pour gérer la présence des utilisateurs""" + PRESENCE_STATUS = [ + ('online', 'En ligne'), + ('away', 'Absent'), + ('busy', 'Occupé'), + ('offline', 'Hors ligne'), + ] + + user = models.OneToOneField(Profile, on_delete=models.CASCADE, related_name='presence') + status = models.CharField(max_length=10, choices=PRESENCE_STATUS, default='offline') + last_seen = models.DateTimeField(default=timezone.now) + is_typing_in = models.ForeignKey(Conversation, on_delete=models.SET_NULL, null=True, blank=True, related_name='typing_users') + + def __str__(self): + return f'{self.user.email} - {self.status}' + +# Ancien modèle conservé pour compatibilité class Messagerie(models.Model): id = models.AutoField(primary_key=True) objet = models.CharField(max_length=200, default="", blank=True) emetteur = models.ForeignKey(Profile, on_delete=models.PROTECT, related_name='messages_envoyes') destinataire = models.ForeignKey(Profile, on_delete=models.PROTECT, related_name='messages_recus') corpus = models.CharField(max_length=200, default="", blank=True) + date_envoi = models.DateTimeField(auto_now_add=True) # Date d'envoi du message + is_read = models.BooleanField(default=False) # Statut lu/non lu + conversation_id = models.CharField(max_length=100, blank=True, default="") # Pour regrouper les messages par conversation def __str__(self): - return 'Messagerie_'+self.id \ No newline at end of file + return f'Messagerie_{self.id}' \ No newline at end of file diff --git a/Back-End/GestionMessagerie/routing.py b/Back-End/GestionMessagerie/routing.py new file mode 100644 index 0000000..ce4585b --- /dev/null +++ b/Back-End/GestionMessagerie/routing.py @@ -0,0 +1,7 @@ +from django.urls import re_path +from . import consumers + +websocket_urlpatterns = [ + re_path(r'ws/chat/(?P\w+)/$', consumers.ChatConsumer.as_asgi()), + re_path(r'ws/chat/conversation/(?P[\w-]+)/$', consumers.ChatConsumer.as_asgi()), +] diff --git a/Back-End/GestionMessagerie/serializers.py b/Back-End/GestionMessagerie/serializers.py index ceea37a..536c8dc 100644 --- a/Back-End/GestionMessagerie/serializers.py +++ b/Back-End/GestionMessagerie/serializers.py @@ -1,14 +1,266 @@ from rest_framework import serializers from Auth.models import Profile -from GestionMessagerie.models import Messagerie +from GestionMessagerie.models import Messagerie, Conversation, ConversationParticipant, Message, MessageRead, UserPresence +from channels.layers import get_channel_layer +from asgiref.sync import async_to_sync + +class ProfileSimpleSerializer(serializers.ModelSerializer): + """Sérialiseur simple pour les profils utilisateur""" + class Meta: + model = Profile + fields = ['id', 'first_name', 'last_name', 'email'] + +class UserPresenceSerializer(serializers.ModelSerializer): + """Sérialiseur pour la présence utilisateur""" + user = ProfileSimpleSerializer(read_only=True) + + class Meta: + model = UserPresence + fields = ['user', 'status', 'last_seen', 'is_typing_in'] + +class MessageReadSerializer(serializers.ModelSerializer): + """Sérialiseur pour les messages lus""" + participant = ProfileSimpleSerializer(read_only=True) + + class Meta: + model = MessageRead + fields = ['participant', 'read_at'] class MessageSerializer(serializers.ModelSerializer): + """Sérialiseur pour les messages instantanés""" + sender = ProfileSimpleSerializer(read_only=True) + read_by = MessageReadSerializer(many=True, read_only=True) + attachment = serializers.SerializerMethodField() + is_read = serializers.SerializerMethodField() + + class Meta: + model = Message + fields = ['id', 'conversation', 'sender', 'content', 'message_type', 'file_url', + 'file_name', 'file_size', 'file_type', 'attachment', + 'created_at', 'updated_at', 'is_edited', 'is_deleted', 'read_by', 'is_read'] + read_only_fields = ['id', 'created_at', 'updated_at'] + + def get_attachment(self, obj): + """Retourne les informations du fichier attaché sous forme d'objet""" + if obj.file_url: + return { + 'fileName': obj.file_name, + 'fileSize': obj.file_size, + 'fileType': obj.file_type, + 'fileUrl': obj.file_url, + } + return None + + def get_is_read(self, obj): + """Détermine si le message est lu par l'utilisateur actuel""" + user = self.context.get('user') + if not user or not user.is_authenticated: + return False + + # Si c'est le message de l'utilisateur lui-même, vérifier si quelqu'un d'autre l'a lu + if obj.sender == user: + # Pour les messages envoyés par l'utilisateur, vérifier si au moins un autre participant l'a explicitement lu + # Utiliser le modèle MessageRead pour une vérification précise + from .models import MessageRead + other_participants = obj.conversation.participants.exclude(participant=user).filter(is_active=True) + + for participant in other_participants: + # Vérifier si ce participant a explicitement lu ce message + if MessageRead.objects.filter(message=obj, participant=participant.participant).exists(): + return True + + # Fallback: vérifier last_read_at seulement si l'utilisateur était en ligne récemment + # ou si last_read_at est postérieur à created_at (lecture explicite après réception) + if (participant.last_read_at and + participant.last_read_at > obj.created_at): + + # Vérifier la présence de l'utilisateur pour s'assurer qu'il était en ligne + try: + from .models import UserPresence + user_presence = UserPresence.objects.filter(user=participant.participant).first() + + # Si l'utilisateur était en ligne récemment (dans les 5 minutes suivant le message) + # ou si last_read_at est bien après created_at (lecture délibérée) + time_diff = participant.last_read_at - obj.created_at + if (user_presence and user_presence.last_seen and + user_presence.last_seen >= obj.created_at) or time_diff.total_seconds() > 10: + return True + except: + # En cas d'erreur, continuer avec la logique conservative + pass + + return False + else: + # Pour les messages reçus, vérifier si l'utilisateur actuel l'a lu + # D'abord vérifier dans MessageRead pour une lecture explicite + from .models import MessageRead + if MessageRead.objects.filter(message=obj, participant=user).exists(): + return True + + # Fallback: vérifier last_read_at du participant + participant = obj.conversation.participants.filter( + participant=user, + is_active=True + ).first() + + if participant and participant.last_read_at: + # Seulement considérer comme lu si last_read_at est postérieur à created_at + return participant.last_read_at > obj.created_at + + return False + +class ConversationParticipantSerializer(serializers.ModelSerializer): + """Sérialiseur pour les participants d'une conversation""" + participant = ProfileSimpleSerializer(read_only=True) + + class Meta: + model = ConversationParticipant + fields = ['participant', 'joined_at', 'last_read_at', 'is_active'] + +class ConversationSerializer(serializers.ModelSerializer): + """Sérialiseur pour les conversations""" + participants = ConversationParticipantSerializer(many=True, read_only=True) + last_message = serializers.SerializerMethodField() + unread_count = serializers.SerializerMethodField() + interlocuteur = serializers.SerializerMethodField() + + class Meta: + model = Conversation + fields = ['id', 'name', 'conversation_type', 'created_at', 'updated_at', + 'last_activity', 'is_active', 'participants', 'last_message', 'unread_count', 'interlocuteur'] + read_only_fields = ['id', 'created_at', 'updated_at'] + + def get_last_message(self, obj): + last_message = obj.messages.filter(is_deleted=False).last() + if last_message: + return MessageSerializer(last_message).data + return None + + def get_unread_count(self, obj): + user = self.context.get('user') + if not user or not user.is_authenticated: + return 0 + + participant = obj.participants.filter(participant=user).first() + if not participant: + return 0 + + # Nouvelle logique : compter les messages qui ne sont PAS dans MessageRead + # et qui ont été créés après last_read_at (ou tous si last_read_at est None) + + # Base query : messages de la conversation, excluant les propres messages et les supprimés + # ET ne comptant que les messages textuels + base_query = obj.messages.filter( + is_deleted=False, + message_type='text' # Ne compter que les messages textuels + ).exclude(sender=user) + + # Si l'utilisateur n'a pas de last_read_at, tous les messages sont non lus + if not participant.last_read_at: + unread_from_timestamp = base_query + else: + # Messages créés après le dernier moment de lecture + unread_from_timestamp = base_query.filter( + created_at__gt=participant.last_read_at + ) + + # Soustraire les messages explicitement marqués comme lus dans MessageRead + from .models import MessageRead + read_message_ids = MessageRead.objects.filter( + participant=user, + message__conversation=obj + ).values_list('message_id', flat=True) + + # Compter les messages non lus = messages après last_read_at MOINS ceux explicitement lus + unread_count = unread_from_timestamp.exclude( + id__in=read_message_ids + ).count() + + return unread_count + + def get_interlocuteur(self, obj): + """Pour les conversations privées, retourne l'autre participant""" + user = self.context.get('user') + if not user or not user.is_authenticated or obj.conversation_type != 'private': + return None + + # Trouver l'autre participant (pas l'utilisateur actuel) + other_participant = obj.participants.filter(is_active=True).exclude(participant=user).first() + if other_participant: + return ProfileSimpleSerializer(other_participant.participant).data + return None + +class ConversationCreateSerializer(serializers.ModelSerializer): + """Sérialiseur pour créer une conversation""" + participant_ids = serializers.ListField( + child=serializers.IntegerField(), + write_only=True + ) + + class Meta: + model = Conversation + fields = ['name', 'conversation_type', 'participant_ids'] + + def create(self, validated_data): + participant_ids = validated_data.pop('participant_ids') + conversation_type = validated_data.get('conversation_type', 'private') + + # Pour les conversations privées, ne pas utiliser de nom spécifique + # Le nom sera géré côté frontend en affichant le nom de l'interlocuteur + if conversation_type == 'private': + validated_data['name'] = None + + conversation = super().create(validated_data) + + # Ajouter les participants + participants = [] + for participant_id in participant_ids: + try: + participant = Profile.objects.get(id=participant_id) + ConversationParticipant.objects.create( + conversation=conversation, + participant=participant + ) + participants.append(participant) + except Profile.DoesNotExist: + continue + + # Notifier les participants via WebSocket de la nouvelle conversation + try: + from channels.layers import get_channel_layer + from asgiref.sync import async_to_sync + + channel_layer = get_channel_layer() + if channel_layer: + # Envoyer à chaque participant avec le bon contexte + for participant in participants: + # Sérialiser la conversation avec le contexte de ce participant + conversation_data = ConversationSerializer(conversation, context={'user': participant}).data + + async_to_sync(channel_layer.group_send)( + f'user_{participant.id}', + { + 'type': 'new_conversation_notification', + 'conversation': conversation_data + } + ) + except Exception as e: + # Log l'erreur mais ne pas interrompre la création de la conversation + import logging + logger = logging.getLogger(__name__) + logger.error(f"Erreur lors de la notification WebSocket de nouvelle conversation: {str(e)}") + + return conversation + +# Ancien sérialiseur conservé pour compatibilité +class MessageLegacySerializer(serializers.ModelSerializer): destinataire_profil = serializers.SerializerMethodField() emetteur_profil = serializers.SerializerMethodField() class Meta: model = Messagerie fields = '__all__' - + read_only_fields = ['date_envoi'] + def get_destinataire_profil(self, obj): return obj.destinataire.email diff --git a/Back-End/GestionMessagerie/urls.py b/Back-End/GestionMessagerie/urls.py index 9636936..45222f7 100644 --- a/Back-End/GestionMessagerie/urls.py +++ b/Back-End/GestionMessagerie/urls.py @@ -1,9 +1,22 @@ -from django.urls import path, re_path - -from GestionMessagerie.views import MessagerieView, MessageView +from django.urls import path +from .views import ( + InstantConversationListView, InstantConversationCreateView, InstantConversationDeleteView, + InstantMessageListView, InstantMessageCreateView, + InstantMarkAsReadView, FileUploadView, + InstantRecipientSearchView +) urlpatterns = [ - re_path(r'^messagerie/([0-9]+)$', MessagerieView.as_view(), name="messagerie"), - re_path(r'^message$', MessageView.as_view(), name="message"), - re_path(r'^message/([0-9]+)$', MessageView.as_view(), name="message"), + # URLs pour messagerie instantanée + path('conversations/', InstantConversationListView.as_view(), name='conversations'), + path('create-conversation/', InstantConversationCreateView.as_view(), name='create_conversation'), + path('send-message/', InstantMessageCreateView.as_view(), name='send_message'), + path('conversations/mark-as-read/', InstantMarkAsReadView.as_view(), name='mark_as_read'), + path('search-recipients/', InstantRecipientSearchView.as_view(), name='search_recipients'), + path('upload-file/', FileUploadView.as_view(), name='upload_file'), + + # URLs avec paramètres - doivent être après les URLs statiques + path('conversations/user//', InstantConversationListView.as_view(), name='conversations_by_user'), + path('conversations//', InstantConversationDeleteView.as_view(), name='delete_conversation'), + path('conversations//messages/', InstantMessageListView.as_view(), name='conversation_messages'), ] \ No newline at end of file diff --git a/Back-End/GestionMessagerie/views.py b/Back-End/GestionMessagerie/views.py index 40c7297..49be6d7 100644 --- a/Back-End/GestionMessagerie/views.py +++ b/Back-End/GestionMessagerie/views.py @@ -1,32 +1,455 @@ -from django.http.response import JsonResponse from rest_framework.views import APIView -from rest_framework.parsers import JSONParser +from rest_framework.response import Response +from rest_framework import status +from rest_framework.parsers import MultiPartParser, FormParser +from django.db import models +from .models import Conversation, ConversationParticipant, Message, UserPresence +from Auth.models import Profile, ProfileRole +from GestionMessagerie.serializers import ( + ConversationSerializer, MessageSerializer, + ConversationCreateSerializer, UserPresenceSerializer, + ProfileSimpleSerializer +) +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from django.utils import timezone +import os +import uuid +import logging +from django.core.files.storage import default_storage +from django.core.files.base import ContentFile +from django.db.models import Q -from .models import * +logger = logging.getLogger(__name__) -from GestionMessagerie.serializers import MessageSerializer +# ====================== MESSAGERIE INSTANTANÉE ====================== -from N3wtSchool import bdd +class InstantConversationListView(APIView): + """ + API pour lister les conversations instantanées d'un utilisateur + """ + @swagger_auto_schema( + operation_description="Liste les conversations instantanées d'un utilisateur", + responses={200: ConversationSerializer(many=True)} + ) + def get(self, request, user_id=None): + try: + user = Profile.objects.get(id=user_id) -class MessagerieView(APIView): - def get(self, request, _idProfile): - messagesList = bdd.getObjects(_objectName=Messagerie, _columnName='destinataire__id', _value=_idProfile) - messages_serializer = MessageSerializer(messagesList, many=True) - return JsonResponse(messages_serializer.data, safe=False) + conversations = Conversation.objects.filter( + participants__participant=user, + participants__is_active=True, + is_active=True + ).distinct().order_by('-last_activity') -class MessageView(APIView): - def get(self, request, _id): - message=bdd.getObject(Messagerie, "id", _id) - message_serializer=MessageSerializer(message) - return JsonResponse(message_serializer.data, safe=False) - + serializer = ConversationSerializer(conversations, many=True, context={'user': user}) + return Response(serializer.data, status=status.HTTP_200_OK) + except Profile.DoesNotExist: + return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +class InstantConversationCreateView(APIView): + """ + API pour créer une nouvelle conversation instantanée + """ + @swagger_auto_schema( + operation_description="Crée une nouvelle conversation instantanée", + request_body=ConversationCreateSerializer, + responses={201: ConversationSerializer} + ) def post(self, request): - message_data=JSONParser().parse(request) - message_serializer = MessageSerializer(data=message_data) + serializer = ConversationCreateSerializer(data=request.data) + if serializer.is_valid(): + conversation = serializer.save() + response_serializer = ConversationSerializer(conversation, context={'user': request.user}) + return Response(response_serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - if message_serializer.is_valid(): - message_serializer.save() - - return JsonResponse('Nouveau Message ajouté', safe=False) +class InstantMessageListView(APIView): + """ + API pour lister les messages d'une conversation + """ + @swagger_auto_schema( + operation_description="Liste les messages d'une conversation", + responses={200: MessageSerializer(many=True)} + ) + def get(self, request, conversation_id): + try: + conversation = Conversation.objects.get(id=conversation_id) + messages = conversation.messages.filter(is_deleted=False).order_by('created_at') + + # Récupérer l'utilisateur actuel depuis les paramètres de requête + user_id = request.GET.get('user_id') + user = None + if user_id: + try: + user = Profile.objects.get(id=user_id) + except Profile.DoesNotExist: + pass + + serializer = MessageSerializer(messages, many=True, context={'user': user}) + return Response(serializer.data, status=status.HTTP_200_OK) + except Conversation.DoesNotExist: + return Response({'error': 'Conversation not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +class InstantMessageCreateView(APIView): + """ + API pour envoyer un nouveau message instantané + """ + @swagger_auto_schema( + operation_description="Envoie un nouveau message instantané", + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'conversation_id': openapi.Schema(type=openapi.TYPE_STRING, description='ID de la conversation'), + 'sender_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='ID de l\'expéditeur'), + 'content': openapi.Schema(type=openapi.TYPE_STRING, description='Contenu du message'), + 'message_type': openapi.Schema(type=openapi.TYPE_STRING, description='Type de message', default='text') + }, + required=['conversation_id', 'sender_id', 'content'] + ), + responses={201: MessageSerializer} + ) + def post(self, request): + try: + conversation_id = request.data.get('conversation_id') + sender_id = request.data.get('sender_id') + content = request.data.get('content', '').strip() + message_type = request.data.get('message_type', 'text') + + if not all([conversation_id, sender_id, content]): + return Response( + {'error': 'conversation_id, sender_id, and content are required'}, + status=status.HTTP_400_BAD_REQUEST + ) + + # Vérifier que la conversation existe + conversation = Conversation.objects.get(id=conversation_id) + + # Vérifier que l'expéditeur existe et peut envoyer dans cette conversation + sender = Profile.objects.get(id=sender_id) + participant = ConversationParticipant.objects.filter( + conversation=conversation, + participant=sender, + is_active=True + ).first() + + if not participant: + return Response( + {'error': 'You are not a participant in this conversation'}, + status=status.HTTP_403_FORBIDDEN + ) + + # Récupérer les données de fichier si disponibles + file_url = request.data.get('file_url') + file_name = request.data.get('file_name') + file_type = request.data.get('file_type') + file_size = request.data.get('file_size') + + # Créer le message + message = Message.objects.create( + conversation=conversation, + sender=sender, + content=content, + message_type=message_type, + file_url=file_url, + file_name=file_name, + file_type=file_type, + file_size=file_size + ) + + # Mettre à jour l'activité de la conversation + conversation.last_activity = message.created_at + conversation.save(update_fields=['last_activity']) + + serializer = MessageSerializer(message) + return Response(serializer.data, status=status.HTTP_201_CREATED) + + except Conversation.DoesNotExist: + return Response({'error': 'Conversation not found'}, status=status.HTTP_404_NOT_FOUND) + except Profile.DoesNotExist: + return Response({'error': 'Sender not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +class InstantMarkAsReadView(APIView): + """ + API pour marquer une conversation comme lue + """ + @swagger_auto_schema( + operation_description="Marque une conversation comme lue pour un utilisateur", + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'user_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='ID de l\'utilisateur') + }, + required=['user_id'] + ), + responses={200: openapi.Response('Success')} + ) + def post(self, request, conversation_id): + try: + user_id = request.data.get('user_id') + if not user_id: + return Response({'error': 'user_id is required'}, status=status.HTTP_400_BAD_REQUEST) + + participant = ConversationParticipant.objects.get( + conversation_id=conversation_id, + participant_id=user_id, + is_active=True + ) + + participant.last_read_at = timezone.now() + participant.save(update_fields=['last_read_at']) + + return Response({'status': 'success'}, status=status.HTTP_200_OK) + + except ConversationParticipant.DoesNotExist: + return Response({'error': 'Participant not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +class UserPresenceView(APIView): + """ + API pour gérer la présence des utilisateurs + """ + @swagger_auto_schema( + operation_description="Met à jour le statut de présence d'un utilisateur", + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'status': openapi.Schema(type=openapi.TYPE_STRING, description='Statut de présence') + }, + required=['status'] + ), + responses={200: UserPresenceSerializer} + ) + def post(self, request, user_id): + try: + user = Profile.objects.get(id=user_id) + status_value = request.data.get('status') + + if status_value not in ['online', 'away', 'busy', 'offline']: + return Response({'error': 'Invalid status'}, status=status.HTTP_400_BAD_REQUEST) + + presence, created = UserPresence.objects.get_or_create(user=user) + presence.status = status_value + presence.last_seen = timezone.now() + presence.save() + + serializer = UserPresenceSerializer(presence) + return Response(serializer.data, status=status.HTTP_200_OK) + + except Profile.DoesNotExist: + return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + @swagger_auto_schema( + operation_description="Récupère le statut de présence d'un utilisateur", + responses={200: UserPresenceSerializer} + ) + def get(self, request, user_id): + try: + user = Profile.objects.get(id=user_id) + presence, created = UserPresence.objects.get_or_create(user=user) + + if created: + presence.status = 'offline' + presence.save() + + serializer = UserPresenceSerializer(presence) + return Response(serializer.data, status=status.HTTP_200_OK) + + except Profile.DoesNotExist: + return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +class FileUploadView(APIView): + """ + API pour l'upload de fichiers dans la messagerie instantanée + """ + parser_classes = (MultiPartParser, FormParser) + + @swagger_auto_schema( + operation_description="Upload un fichier pour la messagerie", + manual_parameters=[ + openapi.Parameter('file', openapi.IN_FORM, description="Fichier à uploader", type=openapi.TYPE_FILE, required=True), + openapi.Parameter('conversation_id', openapi.IN_FORM, description="ID de la conversation", type=openapi.TYPE_INTEGER, required=True), + openapi.Parameter('sender_id', openapi.IN_FORM, description="ID de l'expéditeur", type=openapi.TYPE_INTEGER, required=True), + ], + responses={ + 200: openapi.Response('Success', openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'fileUrl': openapi.Schema(type=openapi.TYPE_STRING), + 'fileName': openapi.Schema(type=openapi.TYPE_STRING), + 'fileSize': openapi.Schema(type=openapi.TYPE_INTEGER), + 'fileType': openapi.Schema(type=openapi.TYPE_STRING), + } + )), + 400: 'Bad Request', + 413: 'File too large', + 415: 'Unsupported file type' + } + ) + def post(self, request): + try: + file = request.FILES.get('file') + conversation_id = request.data.get('conversation_id') + sender_id = request.data.get('sender_id') + + if not file: + return Response({'error': 'Aucun fichier fourni'}, status=status.HTTP_400_BAD_REQUEST) + + if not conversation_id or not sender_id: + return Response({'error': 'conversation_id et sender_id requis'}, status=status.HTTP_400_BAD_REQUEST) + + # Vérifier que la conversation existe et que l'utilisateur y participe + try: + conversation = Conversation.objects.get(id=conversation_id) + sender = Profile.objects.get(id=sender_id) + + # Vérifier que l'expéditeur participe à la conversation + if not ConversationParticipant.objects.filter( + conversation=conversation, + participant=sender, + is_active=True + ).exists(): + return Response({'error': 'Accès non autorisé à cette conversation'}, status=status.HTTP_403_FORBIDDEN) + + except (Conversation.DoesNotExist, Profile.DoesNotExist): + return Response({'error': 'Conversation ou utilisateur introuvable'}, status=status.HTTP_404_NOT_FOUND) + + # Valider le type de fichier + allowed_types = [ + 'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', + 'application/pdf', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'text/plain' + ] + + if file.content_type not in allowed_types: + return Response({'error': 'Type de fichier non autorisé'}, status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE) + + # Valider la taille du fichier (10MB max) + max_size = 10 * 1024 * 1024 # 10MB + if file.size > max_size: + return Response({'error': 'Fichier trop volumineux (max 10MB)'}, status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE) + + # Générer un nom de fichier unique + file_extension = os.path.splitext(file.name)[1] + unique_filename = f"{uuid.uuid4()}{file_extension}" + + # Chemin de stockage : messagerie/conversation_id/ + storage_path = f"messagerie/{conversation_id}/{unique_filename}" + + # Sauvegarder le fichier + file_path = default_storage.save(storage_path, ContentFile(file.read())) + + # Générer l'URL du fichier + file_url = default_storage.url(file_path) + if not file_url.startswith('http'): + # Construire l'URL complète si nécessaire + file_url = request.build_absolute_uri(file_url) + + return Response({ + 'fileUrl': file_url, + 'fileName': file.name, + 'fileSize': file.size, + 'fileType': file.content_type, + 'filePath': file_path + }, status=status.HTTP_200_OK) + + except Exception as e: + return Response({'error': f'Erreur lors de l\'upload: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +class InstantRecipientSearchView(APIView): + """ + API pour rechercher des destinataires pour la messagerie instantanée + """ + @swagger_auto_schema( + operation_description="Recherche des destinataires pour la messagerie instantanée", + manual_parameters=[ + openapi.Parameter('establishment_id', openapi.IN_QUERY, description="ID de l'établissement", type=openapi.TYPE_INTEGER, required=True), + openapi.Parameter('q', openapi.IN_QUERY, description="Terme de recherche", type=openapi.TYPE_STRING, required=True) + ], + responses={200: ProfileSimpleSerializer(many=True)} + ) + def get(self, request): + try: + establishment_id = request.query_params.get('establishment_id') + search_query = request.query_params.get('q', '').strip() + + if not establishment_id: + return Response({'error': 'establishment_id is required'}, status=status.HTTP_400_BAD_REQUEST) + + # Récupérer les IDs des profils actifs dans l'établissement + profile_roles = ProfileRole.objects.filter( + establishment_id=establishment_id, + is_active=True + ).values_list('profile_id', flat=True) + + # Rechercher les profils correspondants + users = Profile.objects.filter(id__in=profile_roles) + + # Appliquer le filtre de recherche si un terme est fourni + if search_query: + users = users.filter( + Q(first_name__icontains=search_query) | + Q(last_name__icontains=search_query) | + Q(email__icontains=search_query) + ) + + # Exclure l'utilisateur actuel des résultats + if request.user.is_authenticated: + users = users.exclude(id=request.user.id) + + serializer = ProfileSimpleSerializer(users[:10], many=True) # Limiter à 10 résultats + return Response(serializer.data, status=status.HTTP_200_OK) + + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +class InstantConversationDeleteView(APIView): + """ + API pour supprimer (désactiver) une conversation instantanée + """ + @swagger_auto_schema( + operation_description="Supprime une conversation instantanée (désactivation soft)", + responses={200: openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'success': openapi.Schema(type=openapi.TYPE_BOOLEAN), + 'message': openapi.Schema(type=openapi.TYPE_STRING) + } + )} + ) + def delete(self, request, conversation_id): + try: + # Récupérer la conversation par son ID UUID + conversation = Conversation.objects.filter(id=conversation_id).first() + + if not conversation: + return Response({'error': 'Conversation not found'}, status=status.HTTP_404_NOT_FOUND) + + # Suppression simple : désactiver la conversation + conversation.is_active = False + conversation.save() + + return Response({ + 'success': True, + 'message': 'Conversation deleted successfully' + }, status=status.HTTP_200_OK) + + except Exception as e: + logger.error(f"Error deleting conversation: {str(e)}") + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - return JsonResponse(message_serializer.errors, safe=False) \ No newline at end of file diff --git a/Back-End/GestionNotification/migrations/0001_initial.py b/Back-End/GestionNotification/migrations/0001_initial.py new file mode 100644 index 0000000..3517e45 --- /dev/null +++ b/Back-End/GestionNotification/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Notification', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('message', models.CharField(max_length=255)), + ('is_read', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('typeNotification', models.IntegerField(choices=[(0, 'Aucune notification'), (1, 'Un message a été reçu'), (2, "Le dossier d'inscription a été mis à jour")], default=0)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/Back-End/GestionNotification/migrations/__init__.py b/Back-End/GestionNotification/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/GestionNotification/signals.py b/Back-End/GestionNotification/signals.py index 20e78f3..4e3fb0a 100644 --- a/Back-End/GestionNotification/signals.py +++ b/Back-End/GestionNotification/signals.py @@ -4,20 +4,20 @@ from .models import Notification, TypeNotif from GestionMessagerie.models import Messagerie from Subscriptions.models import RegistrationForm -@receiver(post_save, sender=Messagerie) -def notification_MESSAGE(sender, instance, created, **kwargs): - if created: - Notification.objects.create( - user=instance.destinataire, - message=(TypeNotif.NOTIF_MESSAGE).label, - typeNotification=TypeNotif.NOTIF_MESSAGE - ) +# @receiver(post_save, sender=Messagerie) +# def notification_MESSAGE(sender, instance, created, **kwargs): +# if created: +# Notification.objects.create( +# user=instance.destinataire, +# message=(TypeNotif.NOTIF_MESSAGE).label, +# typeNotification=TypeNotif.NOTIF_MESSAGE +# ) -@receiver(post_save, sender=RegistrationForm) -def notification_DI(sender, instance, created, **kwargs): - for responsable in instance.student.guardians.all(): - Notification.objects.create( - user=responsable.associated_profile, - message=(TypeNotif.NOTIF_DI).label, - typeNotification=TypeNotif.NOTIF_DI - ) +# @receiver(post_save, sender=RegistrationForm) +# def notification_DI(sender, instance, created, **kwargs): +# for responsable in instance.student.guardians.all(): +# Notification.objects.create( +# user=responsable.associated_profile, +# message=(TypeNotif.NOTIF_DI).label, +# typeNotification=TypeNotif.NOTIF_DI +# ) diff --git a/Back-End/GestionNotification/urls.py b/Back-End/GestionNotification/urls.py index 7619357..e966cc6 100644 --- a/Back-End/GestionNotification/urls.py +++ b/Back-End/GestionNotification/urls.py @@ -3,5 +3,5 @@ from django.urls import path, re_path from GestionNotification.views import NotificationView urlpatterns = [ - re_path(r'^notification$', NotificationView.as_view(), name="notification"), + re_path(r'^notifications$', NotificationView.as_view(), name="notifications"), ] \ No newline at end of file diff --git a/Back-End/N3wtSchool/Configuration/application.default.json b/Back-End/N3wtSchool/Configuration/application.default.json new file mode 100644 index 0000000..3e9effc --- /dev/null +++ b/Back-End/N3wtSchool/Configuration/application.default.json @@ -0,0 +1,8 @@ +{ + "hostSMTP": "", + "portSMTP": 25, + "username": "", + "password": "", + "useSSL": false, + "useTLS": false +} \ No newline at end of file diff --git a/Back-End/N3wtSchool/asgi.py b/Back-End/N3wtSchool/asgi.py index 6a4912d..29f2085 100644 --- a/Back-End/N3wtSchool/asgi.py +++ b/Back-End/N3wtSchool/asgi.py @@ -8,9 +8,40 @@ https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ """ import os - from django.core.asgi import get_asgi_application +from channels.routing import ProtocolTypeRouter, URLRouter +from channels.security.websocket import AllowedHostsOriginValidator +from django.urls import re_path +from django.conf import settings +from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'N3wtSchool.settings') -application = get_asgi_application() +# Initialize Django ASGI application early to ensure the AppRegistry +# is populated before importing code that may import ORM models. +django_asgi_app = get_asgi_application() + +# Import consumers after Django is initialized +from GestionMessagerie.consumers import ChatConsumer +from GestionMessagerie.middleware import JWTAuthMiddlewareStack + +# WebSocket URL patterns +websocket_urlpatterns = [ + re_path(r'ws/chat/(?P\w+)/$', ChatConsumer.as_asgi()), +] + +# Créer l'application ASGI avec gestion des fichiers statiques +if settings.DEBUG: + # En mode DEBUG, utiliser ASGIStaticFilesHandler pour servir les fichiers statiques + http_application = ASGIStaticFilesHandler(django_asgi_app) +else: + http_application = django_asgi_app + +application = ProtocolTypeRouter({ + "http": http_application, + "websocket": AllowedHostsOriginValidator( + JWTAuthMiddlewareStack( + URLRouter(websocket_urlpatterns) + ) + ), +}) diff --git a/Back-End/N3wtSchool/bdd.py b/Back-End/N3wtSchool/bdd.py index 1fe62ef..12eafff 100644 --- a/Back-End/N3wtSchool/bdd.py +++ b/Back-End/N3wtSchool/bdd.py @@ -2,7 +2,8 @@ import logging from django.db.models import Q from django.http import JsonResponse from django.core.exceptions import ObjectDoesNotExist -from Subscriptions.models import RegistrationForm, Profile, Student +from Subscriptions.models import RegistrationForm, Student +from Auth.models import Profile logger = logging.getLogger('N3wtSchool') @@ -92,6 +93,7 @@ def searchObjects(_objectName, _searchTerm=None, _excludeStates=None): def delete_object(model_class, object_id, related_field=None): try: obj = model_class.objects.get(id=object_id) + if related_field and hasattr(obj, related_field): related_obj = getattr(obj, related_field) if related_obj: @@ -103,5 +105,3 @@ def delete_object(model_class, object_id, related_field=None): return JsonResponse({'error': f'L\'objet {model_class.__name__} n\'existe pas avec cet ID'}, status=404, safe=False) except Exception as e: return JsonResponse({'error': f'Une erreur est survenue : {str(e)}'}, status=500, safe=False) - - diff --git a/Back-End/N3wtSchool/error.py b/Back-End/N3wtSchool/error.py index 300a992..a357692 100644 --- a/Back-End/N3wtSchool/error.py +++ b/Back-End/N3wtSchool/error.py @@ -1,4 +1,5 @@ from typing import Final +from N3wtSchool import settings WRONG_ID: Final = 1 INCOMPLETE: Final = 2 @@ -8,11 +9,14 @@ DIFFERENT_PASWWORD: Final = 5 PROFIL_NOT_EXISTS: Final = 6 MESSAGE_REINIT_PASSWORD: Final = 7 EXPIRED_URL: Final = 8 -PASSWORD_CHANGED: Final = 8 -WRONG_MAIL_FORMAT: Final = 9 -PROFIL_INACTIVE: Final = 10 -MESSAGE_ACTIVATION_PROFILE: Final = 11 -PROFIL_ACTIVE: Final = 12 +PASSWORD_CHANGED: Final = 9 +WRONG_MAIL_FORMAT: Final = 10 +PROFIL_INACTIVE: Final = 11 +MESSAGE_ACTIVATION_PROFILE: Final = 12 +PROFIL_ACTIVE: Final = 13 + +def get_expired_url_message(): + return f"L'URL a expiré. Effectuer à nouveau la demande de réinitialisation de mot de passe : {settings.BASE_URL}/password/new" returnMessage = { WRONG_ID:'Identifiants invalides', @@ -22,7 +26,7 @@ returnMessage = { DIFFERENT_PASWWORD: 'Les mots de passe ne correspondent pas', PROFIL_NOT_EXISTS: 'Aucun profil associé à cet utilisateur', MESSAGE_REINIT_PASSWORD: 'Un mail a été envoyé à l\'adresse \'%s\'', - EXPIRED_URL:'L\'URL a expiré. Effectuer à nouveau la demande de réinitialisation de mot de passe : http://localhost:3000/password/reset?uuid=%s', + EXPIRED_URL: get_expired_url_message(), PASSWORD_CHANGED: 'Le mot de passe a été réinitialisé', WRONG_MAIL_FORMAT: 'L\'adresse mail est mal formatée', PROFIL_INACTIVE: 'Le profil n\'est pas actif', diff --git a/Back-End/N3wtSchool/mailManager.py b/Back-End/N3wtSchool/mailManager.py new file mode 100644 index 0000000..183c1b4 --- /dev/null +++ b/Back-End/N3wtSchool/mailManager.py @@ -0,0 +1,201 @@ +from django.core.mail import send_mail, get_connection, EmailMultiAlternatives, EmailMessage +from django.template.loader import render_to_string +from django.utils.html import strip_tags +from django.conf import settings +import re +from rest_framework.response import Response +from rest_framework import status +from rest_framework.exceptions import NotFound +from Settings.models import SMTPSettings +from Establishment.models import Establishment # Importer le modèle Establishment +import logging + +# Ajouter un logger pour debug +logger = logging.getLogger(__name__) + +def getConnection(id_establishement): + try: + # Récupérer l'instance de l'établissement + establishment = Establishment.objects.get(id=id_establishement) + try: + # Récupérer les paramètres SMTP associés à l'établissement + smtp_settings = SMTPSettings.objects.get(establishment=establishment) + + # Créer une connexion SMTP avec les paramètres récupérés + connection = get_connection( + host=smtp_settings.smtp_server, + port=smtp_settings.smtp_port, + username=smtp_settings.smtp_user, + password=smtp_settings.smtp_password, + use_tls=smtp_settings.use_tls, + use_ssl=smtp_settings.use_ssl + ) + return connection + except SMTPSettings.DoesNotExist: + # Aucun paramètre SMTP spécifique, retournera None + return None + except Establishment.DoesNotExist: + raise NotFound(f"Aucun établissement trouvé avec l'ID {id_establishement}") + +def sendMail(subject, message, recipients, cc=[], bcc=[], attachments=[], connection=None): + try: + # S'assurer que recipients, cc, bcc sont des listes + if isinstance(recipients, str): + recipients = [recipients] + if isinstance(cc, str): + cc = [cc] + if isinstance(bcc, str): + bcc = [bcc] + + # Récupération robuste du username + username = getattr(connection, 'username', None) + + plain_message = strip_tags(message) + if connection is not None: + from_email = username + else: + from_email = settings.EMAIL_HOST_USER + + + logger.info(f"From email: {from_email}") + + email = EmailMultiAlternatives( + subject=subject, + body=plain_message, + from_email=from_email, + to=recipients, + cc=cc, + bcc=bcc, + connection=connection + ) + email.attach_alternative(message, "text/html") + + for attachment in attachments: + email.attach(*attachment) + + logger.info("Tentative d'envoi de l'email...") + email.send(fail_silently=False) + logger.info("Email envoyé avec succès !") + return Response({'message': 'Email envoyé avec succès.'}, status=status.HTTP_200_OK) + except Exception as e: + logger.error(f"Erreur lors de l'envoi de l'email: {str(e)}") + logger.error(f"Type d'erreur: {type(e)}") + import traceback + logger.error(f"Traceback: {traceback.format_exc()}") + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +def envoieReinitMotDePasse(recipients, code): + errorMessage = '' + try: + EMAIL_REINIT_SUBJECT = 'Réinitialisation du mot de passe' + context = { + 'BASE_URL': settings.BASE_URL, + 'code': str(code) + } + subject = EMAIL_REINIT_SUBJECT + html_message = render_to_string('emails/resetPassword.html', context) + sendMail(subject=subject, message=html_message, recipients=recipients) + + except Exception as e: + errorMessage = str(e) + + return errorMessage + +def sendRegistrationDirector(recipients, establishment_id): + errorMessage = '' + try: + # Préparation du contexte pour le template + EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Bienvenue dans la communauté !' + context = { + 'BASE_URL': settings.BASE_URL, + 'URL_DJANGO': settings.URL_DJANGO, + 'email': recipients, + 'establishment': establishment_id + } + subject = EMAIL_INSCRIPTION_SUBJECT + html_message = render_to_string('emails/subscribeDirector.html', context) + sendMail(subject=subject, message=html_message, recipients=recipients) + + + except Exception as e: + errorMessage = str(e) + + return errorMessage + + +def sendRegisterForm(recipients, establishment_id): + errorMessage = '' + try: + # Préparation du contexte pour le template + EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Dossier Inscription' + context = { + 'BASE_URL': settings.BASE_URL, + 'email': recipients, + 'establishment': establishment_id + } + # Récupérer la connexion SMTP + connection = getConnection(establishment_id) + subject = EMAIL_INSCRIPTION_SUBJECT + html_message = render_to_string('emails/inscription.html', context) + sendMail(subject=subject, message=html_message, recipients=recipients, connection=connection) + + + except Exception as e: + errorMessage = str(e) + + return errorMessage + +def sendMandatSEPA(recipients, establishment_id): + errorMessage = '' + try: + # Préparation du contexte pour le template + EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Mandat de prélèvement SEPA' + context = { + 'BASE_URL': settings.BASE_URL, + 'email': recipients, + 'establishment': establishment_id + } + + # Récupérer la connexion SMTP + connection = getConnection(establishment_id) + subject = EMAIL_INSCRIPTION_SUBJECT + html_message = render_to_string('emails/sepa.html', context) + sendMail(subject=subject, message=html_message, recipients=recipients, connection=connection) + + except Exception as e: + errorMessage = str(e) + + return errorMessage + +def envoieRelanceDossierInscription(recipients, code): + EMAIL_RELANCE_SUBJECT = '[N3WT-SCHOOL] Relance - Dossier Inscription' + EMAIL_RELANCE_CORPUS = 'Bonjour,\nN\'ayant pas eu de retour de votre part, nous vous renvoyons le lien vers le formulaire d\'inscription : ' + BASE_URL + '/users/login\nCordialement' + errorMessage = '' + try: + sendMail(EMAIL_RELANCE_SUBJECT, EMAIL_RELANCE_CORPUS%str(code), recipients) + + except Exception as e: + errorMessage = str(e) + + return errorMessage + +def isValid(message, fiche_inscription): + # Est-ce que la référence du dossier est VALIDATED + subject = message.subject + print ("++++ " + subject) + responsableMail = message.from_header + result = re.search('<(.*)>', responsableMail) + + if result: + responsableMail = result.group(1) + + result = re.search(r'.*\[Ref(.*)\].*', subject) + idMail = -1 + if result: + idMail = result.group(1).strip() + + eleve = fiche_inscription.eleve + responsable = eleve.getMainGuardian() + mailReponsableAVerifier = responsable.mail + + return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id) \ No newline at end of file diff --git a/Back-End/N3wtSchool/middleware.py b/Back-End/N3wtSchool/middleware.py new file mode 100644 index 0000000..2035519 --- /dev/null +++ b/Back-End/N3wtSchool/middleware.py @@ -0,0 +1,11 @@ +from django.conf import settings + + +class ContentSecurityPolicyMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + response = self.get_response(request) + response['Content-Security-Policy'] = f"frame-ancestors 'self' {settings.BASE_URL}" + return response diff --git a/Back-End/N3wtSchool/renderers.py b/Back-End/N3wtSchool/renderers.py index 96b2b67..9411cf3 100644 --- a/Back-End/N3wtSchool/renderers.py +++ b/Back-End/N3wtSchool/renderers.py @@ -4,11 +4,22 @@ from django.template.loader import get_template from xhtml2pdf import pisa +class PDFResult: + def __init__(self, content): + self.content = content + def render_to_pdf(template_src, context_dict={}): + """ + Génère un PDF à partir d'un template HTML et retourne le contenu en mémoire. + """ template = get_template(template_src) - html = template.render(context_dict) + html = template.render(context_dict) result = BytesIO() pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result) + if pdf.err: - return HttpResponse("Invalid PDF", status_code=400, content_type='text/plain') - return HttpResponse(result.getvalue(), content_type='application/pdf') \ No newline at end of file + # Lever une exception ou retourner None en cas d'erreur + raise ValueError("Erreur lors de la génération du PDF.") + + # Retourner le contenu du PDF en mémoire + return PDFResult(result.getvalue()) \ No newline at end of file diff --git a/Back-End/N3wtSchool/settings.py b/Back-End/N3wtSchool/settings.py index 078d6e8..5a7a8c6 100644 --- a/Back-End/N3wtSchool/settings.py +++ b/Back-End/N3wtSchool/settings.py @@ -13,33 +13,43 @@ https://docs.djangoproject.com/en/5.0/ref/settings/ from pathlib import Path import json import os +from datetime import timedelta +import logging + +# Configuration du logger +logger = logging.getLogger(__name__) # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent MEDIA_URL = '/data/' MEDIA_ROOT = os.path.join(BASE_DIR, 'data') +BASE_URL = os.getenv('BASE_URL', 'http://localhost:3000') + + LOGIN_REDIRECT_URL = '/Subscriptions/registerForms' # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-afjm6kvigncxzx6jjjf(qb0n(*qvi#je79r=gqflcn007d_ve9' - # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.getenv('DJANGO_DEBUG', True) ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ + 'Common.apps.CommonConfig', 'Subscriptions.apps.GestioninscriptionsConfig', 'Auth.apps.GestionloginConfig', 'GestionMessagerie.apps.GestionMessagerieConfig', + 'GestionEmail.apps.GestionEmailConfig', 'GestionNotification.apps.GestionNotificationConfig', 'School.apps.SchoolConfig', + 'Planning.apps.PlanningConfig', + 'Establishment.apps.EstablishmentConfig', + 'Settings.apps.SettingsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -51,17 +61,20 @@ INSTALLED_APPS = [ 'django_celery_beat', 'N3wtSchool', 'drf_yasg', + 'rest_framework_simplejwt', + 'channels', ] MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', # Déplacez ici, avant CorsMiddleware - 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'N3wtSchool.middleware.ContentSecurityPolicyMiddleware' ] @@ -148,6 +161,11 @@ LOGGING = { "level": os.getenv("GESTION_MESSAGERIE_LOG_LEVEL", "INFO"), "propagate": False, }, + "GestionEmail": { + "handlers": ["console"], + "level": os.getenv("GESTION_EMAIL_LOG_LEVEL", "INFO"), + "propagate": False, + }, "School": { "handlers": ["console"], "level": os.getenv("GESTION_ENSEIGNANTS_LOG_LEVEL", "INFO"), @@ -191,8 +209,6 @@ USE_I18N = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/ -DEBUG = True - STATIC_URL = 'static/' STATICFILES_DIRS = [ @@ -211,67 +227,71 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' #################### Application Settings ############################## ######################################################################## -with open('Subscriptions/Configuration/application.json', 'r') as f: - jsonObject = json.load(f) -DJANGO_SUPERUSER_PASSWORD='admin' -DJANGO_SUPERUSER_USERNAME='admin' -DJANGO_SUPERUSER_EMAIL='admin@n3wtschool.com' - -EMAIL_HOST='smtp.gmail.com' -EMAIL_PORT=587 -EMAIL_HOST_USER=jsonObject['mailFrom'] -EMAIL_HOST_PASSWORD=jsonObject['password'] +# Configuration de l'email de l'application +EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp.example.com') +EMAIL_PORT = os.getenv('EMAIL_PORT', 587) +EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '') +EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '') EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_USE_TLS = True -EMAIL_USE_SSL = False -EMAIL_INSCRIPTION_SUBJECT = '[N3WT-SCHOOL] Dossier Inscription' -EMAIL_INSCRIPTION_CORPUS = """Bonjour, - -Afin de procéder à l'inscription de votre petit bout, vous trouverez ci-joint le lien vers la page d'authentification : http://localhost:3000/users/login - -S'il s'agit de votre première connexion, veuillez procéder à l'activation de votre compte : http://localhost:3000/users/subscribe -identifiant = %s - -Cordialement, -""" - -EMAIL_RELANCE_SUBJECT = '[N3WT-SCHOOL] Relance - Dossier Inscription' -EMAIL_RELANCE_CORPUS = 'Bonjour,\nN\'ayant pas eu de retour de votre part, nous vous renvoyons le lien vers le formulaire d\'inscription : http://localhost:3000/users/login\nCordialement' -EMAIL_REINIT_SUBJECT = 'Réinitialisation du mot de passe' -EMAIL_REINIT_CORPUS = 'Bonjour,\nVous trouverez ci-joint le lien pour réinitialiser votre mot de passe : http://localhost:3000/users/password/reset?uuid=%s\nCordialement' +EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', 'true').lower() == 'true' +EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', 'false').lower() == 'true' DOCUMENT_DIR = 'documents' -CORS_ORIGIN_ALLOW_ALL = True -CORS_ALLOW_ALL_HEADERS = True +# Configuration CORS temporaire pour debug CORS_ALLOW_CREDENTIALS = True -CORS_ALLOWED_ORIGINS = [ - "http://localhost:3000" +# Configuration CORS spécifique pour la production +CORS_ALLOWED_ORIGINS = os.getenv('CORS_ALLOWED_ORIGINS', 'http://localhost:3000,http://localhost:8080,http://127.0.0.1:3000,http://127.0.0.1:8080').split(',') + + +CORS_ALLOW_HEADERS = [ + 'accept', + 'accept-encoding', + 'authorization', + 'content-type', + 'dnt', + 'origin', + 'user-agent', + 'x-csrftoken', + 'x-requested-with', + 'X-Auth-Token', ] -CSRF_TRUSTED_ORIGINS = [ - "http://localhost:3000", # Front Next.js - "http://localhost:8080" # Insomnia +# Méthodes HTTP autorisées +CORS_ALLOWED_METHODS = [ + 'DELETE', + 'GET', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', ] +CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost:3000,http://localhost:8080').split(',') + CSRF_COOKIE_HTTPONLY = False -CSRF_COOKIE_SECURE = False +CSRF_COOKIE_SECURE = os.getenv('CSRF_COOKIE_SECURE', 'false').lower() == 'true' CSRF_COOKIE_NAME = 'csrftoken' - +CSRF_COOKIE_DOMAIN = os.getenv('CSRF_COOKIE_DOMAIN', '') USE_TZ = True TZ_APPLI = 'Europe/Paris' +DB_NAME = os.getenv('DB_NAME', 'school') +DB_USER = os.getenv('DB_USER', 'postgres') +DB_PASSWORD = os.getenv('DB_PASSWORD', 'postgres') +DB_HOST = os.getenv('DB_HOST', 'database') +DB_PORT = os.getenv('DB_PORT', '5432') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', - "NAME": "school", - "USER": "postgres", - "PASSWORD": "postgres", - "HOST": "database", - "PORT": "5432", + "NAME": DB_NAME, + "USER": DB_USER, + "PASSWORD": DB_PASSWORD, + "HOST": DB_HOST, + "PORT": DB_PORT, } } @@ -285,12 +305,17 @@ DATE_FORMAT = '%d-%m-%Y %H:%M' EXPIRATION_SESSION_NB_SEC = 10 -NB_RESULT_PER_PAGE = 8 +NB_RESULT_SUBSCRIPTIONS_PER_PAGE = 8 +NB_RESULT_PROFILES_PER_PAGE = 15 NB_MAX_PAGE = 100 REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'Subscriptions.pagination.CustomPagination', - 'PAGE_SIZE': NB_RESULT_PER_PAGE + 'DEFAULT_PAGINATION_CLASS': 'Subscriptions.pagination.CustomSubscriptionPagination', + 'PAGE_SIZE': NB_RESULT_SUBSCRIPTIONS_PER_PAGE, + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + 'rest_framework.authentication.SessionAuthentication', + ), } CELERY_BROKER_URL = 'redis://redis:6379/0' @@ -301,11 +326,44 @@ CELERY_RESULT_SERIALIZER = 'json' CELERY_TIMEZONE = 'Europe/Paris' CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True -URL_DJANGO = 'http://localhost:8080/' +URL_DJANGO = os.getenv('URL_DJANGO', 'http://localhost:8080/') REDIS_HOST = 'redis' REDIS_PORT = 6379 REDIS_DB = 0 REDIS_PASSWORD = None -SECRET_KEY = 'QWQ8bYlCz1NpQ9G0vR5kxMnvWszfH2y3' +SECRET_KEY = os.getenv('SECRET_KEY', 'QWQ8bYlCz1NpQ9G0vR5kxMnvWszfH2y3') +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'ROTATE_REFRESH_TOKENS': False, + 'BLACKLIST_AFTER_ROTATION': True, + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': SECRET_KEY, + 'VERIFYING_KEY': None, + 'AUTH_HEADER_TYPES': ('Bearer',), + 'USER_ID_FIELD': 'id', + 'USER_ID_CLAIM': 'user_id', + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'TOKEN_TYPE_CLAIM': 'token_type', +} + +# Configuration for DocuSeal JWT +DOCUSEAL_JWT = { + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': SECRET_KEY, + 'EXPIRATION_DELTA': timedelta(hours=1) +} + +# Django Channels Configuration +ASGI_APPLICATION = 'N3wtSchool.asgi.application' + +CHANNEL_LAYERS = { + 'default': { + 'BACKEND': 'channels_redis.core.RedisChannelLayer', + 'CONFIG': { + "hosts": [('redis', 6379)], + }, + }, +} \ No newline at end of file diff --git a/Back-End/N3wtSchool/urls.py b/Back-End/N3wtSchool/urls.py index dfbc490..458d0b2 100644 --- a/Back-End/N3wtSchool/urls.py +++ b/Back-End/N3wtSchool/urls.py @@ -39,11 +39,17 @@ schema_view = get_schema_view( urlpatterns = [ path('admin/', admin.site.urls), + path("Common/", include(("Common.urls", 'Common'), namespace='Common')), path("Subscriptions/", include(("Subscriptions.urls", 'Subscriptions'), namespace='Subscriptions')), path("Auth/", include(("Auth.urls", 'Auth'), namespace='Auth')), path("GestionMessagerie/", include(("GestionMessagerie.urls", 'GestionMessagerie'), namespace='GestionMessagerie')), + path("GestionEmail/", include(("GestionEmail.urls", 'GestionEmail'), namespace='GestionEmail')), path("GestionNotification/", include(("GestionNotification.urls", 'GestionNotification'), namespace='GestionNotification')), path("School/", include(("School.urls", 'School'), namespace='School')), + path("DocuSeal/", include(("DocuSeal.urls", 'DocuSeal'), namespace='DocuSeal')), + path("Planning/", include(("Planning.urls", 'Planning'), namespace='Planning')), + path("Establishment/", include(("Establishment.urls", 'Establishment'), namespace='Establishment')), + path("Settings/", include(("Settings.urls", 'Settings'), namespace='Settings')), # Documentation Api re_path(r'^swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), diff --git a/Back-End/Planning/__init__.py b/Back-End/Planning/__init__.py new file mode 100644 index 0000000..d3fd592 --- /dev/null +++ b/Back-End/Planning/__init__.py @@ -0,0 +1 @@ +default_app_config = 'Planning.apps.PlanningConfig' diff --git a/Back-End/Planning/admin.py b/Back-End/Planning/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Back-End/Planning/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Back-End/Planning/apps.py b/Back-End/Planning/apps.py new file mode 100644 index 0000000..43ec794 --- /dev/null +++ b/Back-End/Planning/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class PlanningConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'Planning' + + diff --git a/Back-End/Planning/migrations/0001_initial.py b/Back-End/Planning/migrations/0001_initial.py new file mode 100644 index 0000000..e6472c6 --- /dev/null +++ b/Back-End/Planning/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('Establishment', '0001_initial'), + ('School', '__first__'), + ] + + operations = [ + migrations.CreateModel( + name='Planning', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(blank=True, default='', null=True)), + ('color', models.CharField(default='#000000', max_length=255)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Establishment.establishment')), + ('school_class', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='planning', to='School.schoolclass')), + ], + ), + migrations.CreateModel( + name='Events', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('description', models.TextField(blank=True, default='', null=True)), + ('start', models.DateTimeField()), + ('end', models.DateTimeField()), + ('recursionType', models.IntegerField(choices=[(0, 'Aucune'), (1, 'Quotidienne'), (2, 'Hebdomadaire'), (3, 'Mensuel'), (4, 'Personnalisé')], default=0)), + ('recursionEnd', models.DateTimeField(blank=True, default=None, null=True)), + ('color', models.CharField(max_length=255)), + ('location', models.CharField(blank=True, default='', max_length=255, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('planning', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Planning.planning')), + ], + ), + ] diff --git a/Back-End/Planning/migrations/__init__.py b/Back-End/Planning/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/Planning/models.py b/Back-End/Planning/models.py new file mode 100644 index 0000000..86be22f --- /dev/null +++ b/Back-End/Planning/models.py @@ -0,0 +1,47 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models +from django.utils.translation import gettext_lazy as _ +from django.conf import settings +from School.models import SchoolClass + +from Establishment.models import Establishment + +class RecursionType(models.IntegerChoices): + RECURSION_NONE = 0, _('Aucune') + RECURSION_DAILY = 1, _('Quotidienne') + RECURSION_WEEKLY = 2, _('Hebdomadaire') + RECURSION_MONTHLY = 3, _('Mensuel') + RECURSION_CUSTOM = 4, _('Personnalisé') + +class Planning(models.Model): + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE) + school_class = models.ForeignKey( + SchoolClass, + on_delete=models.CASCADE, + related_name="planning", + null=True, # Permet des valeurs nulles + blank=True # Rend le champ facultatif dans les formulaires + ) + name = models.CharField(max_length=255) + description = models.TextField(default="", blank=True, null=True) + color= models.CharField(max_length=255, default="#000000") + + def __str__(self): + return f'Planning {self.name}' + + +class Events(models.Model): + planning = models.ForeignKey(Planning, on_delete=models.CASCADE) + title = models.CharField(max_length=255) + description = models.TextField(default="", blank=True, null=True) + start = models.DateTimeField() + end = models.DateTimeField() + recursionType = models.IntegerField(choices=RecursionType, default=0) + recursionEnd = models.DateTimeField(default=None, blank=True, null=True) + color= models.CharField(max_length=255) + location = models.CharField(max_length=255, default="", blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) + + + def __str__(self): + return f'Event {self.title}' \ No newline at end of file diff --git a/Back-End/Planning/serializers.py b/Back-End/Planning/serializers.py new file mode 100644 index 0000000..b3467f1 --- /dev/null +++ b/Back-End/Planning/serializers.py @@ -0,0 +1,13 @@ +from rest_framework import serializers + +from .models import Planning, Events + + +class PlanningSerializer(serializers.ModelSerializer): + class Meta: + model = Planning + fields = '__all__' +class EventsSerializer(serializers.ModelSerializer): + class Meta: + model = Events + fields = '__all__' \ No newline at end of file diff --git a/Back-End/Planning/urls.py b/Back-End/Planning/urls.py new file mode 100644 index 0000000..62143bb --- /dev/null +++ b/Back-End/Planning/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, re_path + +from Planning.views import PlanningView,PlanningWithIdView,EventsView,EventsWithIdView,UpcomingEventsView + +urlpatterns = [ + re_path(r'^plannings$', PlanningView.as_view(), name="planning"), + re_path(r'^plannings/(?P[0-9]+)$', PlanningWithIdView.as_view(), name="planning"), + re_path(r'^events$', EventsView.as_view(), name="events"), + re_path(r'^events/(?P[0-9]+)$', EventsWithIdView.as_view(), name="events"), + re_path(r'^events/upcoming', UpcomingEventsView.as_view(), name="events"), +] \ No newline at end of file diff --git a/Back-End/Planning/views.py b/Back-End/Planning/views.py new file mode 100644 index 0000000..a74c536 --- /dev/null +++ b/Back-End/Planning/views.py @@ -0,0 +1,169 @@ +from django.http.response import JsonResponse +from rest_framework.views import APIView +from django.utils import timezone +from dateutil.relativedelta import relativedelta + +from .models import Planning, Events, RecursionType + +from .serializers import PlanningSerializer, EventsSerializer + +from N3wtSchool import bdd + + +class PlanningView(APIView): + def get(self, request): + establishment_id = request.GET.get('establishment_id', None) + planning_mode = request.GET.get('planning_mode', None) + + plannings = bdd.getAllObjects(Planning) + + if establishment_id is not None: + plannings = plannings.filter(establishment=establishment_id) + + # Filtrer en fonction du planning_mode + if planning_mode == "classSchedule": + plannings = plannings.filter(school_class__isnull=False) + elif planning_mode == "planning": + plannings = plannings.filter(school_class__isnull=True) + + planning_serializer = PlanningSerializer(plannings.distinct(), many=True) + return JsonResponse(planning_serializer.data, safe=False) + + def post(self, request): + planning_serializer = PlanningSerializer(data=request.data) + if planning_serializer.is_valid(): + planning_serializer.save() + return JsonResponse(planning_serializer.data, status=201) + return JsonResponse(planning_serializer.errors, status=400) + + + +class PlanningWithIdView(APIView): + def get(self, request,id): + planning = Planning.objects.get(pk=id) + if planning is None: + return JsonResponse({"errorMessage":'Le dossier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + planning_serializer=PlanningSerializer(planning) + + return JsonResponse(planning_serializer.data, safe=False) + + def put(self, request, id): + try: + planning = Planning.objects.get(pk=id) + except Planning.DoesNotExist: + return JsonResponse({'error': 'Planning not found'}, status=404) + + planning_serializer = PlanningSerializer(planning, data=request.data) + if planning_serializer.is_valid(): + planning_serializer.save() + return JsonResponse(planning_serializer.data) + return JsonResponse(planning_serializer.errors, status=400) + + def delete(self, request, id): + try: + planning = Planning.objects.get(pk=id) + except Planning.DoesNotExist: + return JsonResponse({'error': 'Planning not found'}, status=404) + + planning.delete() + return JsonResponse({'message': 'Planning deleted'}, status=204) + +class EventsView(APIView): + def get(self, request): + establishment_id = request.GET.get('establishment_id', None) + planning_mode = request.GET.get('planning_mode', None) + filterParams = {} + plannings=[] + events = Events.objects.all() + if establishment_id is not None : + filterParams['establishment'] = establishment_id + if planning_mode is not None: + filterParams['school_class__isnull'] = (planning_mode!="classSchedule") + if filterParams: + plannings = Planning.objects.filter(**filterParams) + events = Events.objects.filter(planning__in=plannings) + events_serializer = EventsSerializer(events, many=True) + return JsonResponse(events_serializer.data, safe=False) + + def post(self, request): + events_serializer = EventsSerializer(data=request.data) + if events_serializer.is_valid(): + event = events_serializer.save() + + # Gérer les événements récurrents + if event.recursionType != RecursionType.RECURSION_NONE: + self.create_recurring_events(event) + + return JsonResponse(events_serializer.data, status=201) + return JsonResponse(events_serializer.errors, status=400) + + def create_recurring_events(self, event): + current_start = event.start + current_end = event.end + + while current_start < event.recursionEnd: + if event.recursionType == RecursionType.RECURSION_DAILY: + current_start += relativedelta(days=1) + current_end += relativedelta(days=1) + elif event.recursionType == RecursionType.RECURSION_WEEKLY: + current_start += relativedelta(weeks=1) + current_end += relativedelta(weeks=1) + elif event.recursionType == RecursionType.RECURSION_MONTHLY: + current_start += relativedelta(months=1) + current_end += relativedelta(months=1) + else: + break # Pour d'autres types de récurrence non gérés + + # Créer une nouvelle occurrence + Events.objects.create( + planning=event.planning, + title=event.title, + description=event.description, + start=current_start, + end=current_end, + recursionEnd=event.recursionEnd, + recursionType=event.recursionType, # Les occurrences ne sont pas récurrentes + color=event.color, + location=event.location, + ) + +class EventsWithIdView(APIView): + def put(self, request, id): + try: + event = Events.objects.get(pk=id) + except Events.DoesNotExist: + return JsonResponse({'error': 'Event not found'}, status=404) + + events_serializer = EventsSerializer(event, data=request.data) + if events_serializer.is_valid(): + events_serializer.save() + return JsonResponse(events_serializer.data) + return JsonResponse(events_serializer.errors, status=400) + + def delete(self, request, id): + try: + event = Events.objects.get(pk=id) + except Events.DoesNotExist: + return JsonResponse({'error': 'Event not found'}, status=404) + + event.delete() + return JsonResponse({'message': 'Event deleted'}, status=200) + +class UpcomingEventsView(APIView): + def get(self, request): + current_date = timezone.now() + establishment_id = request.GET.get('establishment_id', None) + + if establishment_id is not None: + # Filtrer les plannings par establishment_id et sans school_class + plannings = Planning.objects.filter(establishment=establishment_id, school_class__isnull=True) + # Filtrer les événements associés à ces plannings et qui sont à venir + upcoming_events = Events.objects.filter(planning__in=plannings, start__gte=current_date) + else: + # Récupérer tous les événements à venir si aucun establishment_id n'est fourni + # et les plannings ne doivent pas être rattachés à une school_class + plannings = Planning.objects.filter(school_class__isnull=True) + upcoming_events = Events.objects.filter(planning__in=plannings, start__gte=current_date) + + events_serializer = EventsSerializer(upcoming_events, many=True) + return JsonResponse(events_serializer.data, safe=False) \ No newline at end of file diff --git a/Back-End/School/apps.py b/Back-End/School/apps.py index 8e7c210..1dafe58 100644 --- a/Back-End/School/apps.py +++ b/Back-End/School/apps.py @@ -1,14 +1,5 @@ from django.apps import AppConfig -from django.db.models.signals import post_migrate - -def create_speciality(sender, **kwargs): - from .models import Speciality - if not Speciality.objects.filter(name='GROUPE').exists(): - Speciality.objects.create(name='GROUPE', color_code='#FF0000') class SchoolConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' - name = 'School' - - def ready(self): - post_migrate.connect(create_speciality, sender=self) + name = 'School' \ No newline at end of file diff --git a/Back-End/School/management/commands/References/CapSchool/Autorisations Parentales 2024-2025.pdf b/Back-End/School/management/commands/References/CapSchool/Autorisations Parentales 2024-2025.pdf new file mode 100644 index 0000000..5c940c7 Binary files /dev/null and b/Back-End/School/management/commands/References/CapSchool/Autorisations Parentales 2024-2025.pdf differ diff --git a/Back-End/School/management/commands/References/CapSchool/Dossier Inscription 24-25.pdf b/Back-End/School/management/commands/References/CapSchool/Dossier Inscription 24-25.pdf new file mode 100644 index 0000000..abdc77e Binary files /dev/null and b/Back-End/School/management/commands/References/CapSchool/Dossier Inscription 24-25.pdf differ diff --git a/Back-End/School/management/commands/References/CapSchool/Règlement Cap School 24-25.pdf b/Back-End/School/management/commands/References/CapSchool/Règlement Cap School 24-25.pdf new file mode 100644 index 0000000..8cab532 Binary files /dev/null and b/Back-End/School/management/commands/References/CapSchool/Règlement Cap School 24-25.pdf differ diff --git a/Back-End/School/management/commands/References/LMDE/Bulletin d'adhésion familiale scolaire.pdf b/Back-End/School/management/commands/References/LMDE/Bulletin d'adhésion familiale scolaire.pdf new file mode 100644 index 0000000..a3b0caa Binary files /dev/null and b/Back-End/School/management/commands/References/LMDE/Bulletin d'adhésion familiale scolaire.pdf differ diff --git a/Back-End/School/management/commands/References/LMDE/Contrat d'engagement 2024 2025.pdf b/Back-End/School/management/commands/References/LMDE/Contrat d'engagement 2024 2025.pdf new file mode 100644 index 0000000..a967e18 Binary files /dev/null and b/Back-End/School/management/commands/References/LMDE/Contrat d'engagement 2024 2025.pdf differ diff --git a/Back-End/School/management/commands/References/LMDE/Fiche d'inscription 2024 2025.pdf b/Back-End/School/management/commands/References/LMDE/Fiche d'inscription 2024 2025.pdf new file mode 100644 index 0000000..54c2ed0 Binary files /dev/null and b/Back-End/School/management/commands/References/LMDE/Fiche d'inscription 2024 2025.pdf differ diff --git a/Back-End/School/management/commands/References/LMDE/Fiche sanitaire de liaison.pdf b/Back-End/School/management/commands/References/LMDE/Fiche sanitaire de liaison.pdf new file mode 100644 index 0000000..d4de1d7 Binary files /dev/null and b/Back-End/School/management/commands/References/LMDE/Fiche sanitaire de liaison.pdf differ diff --git a/Back-End/School/management/commands/References/LMDE/RIB LA MAISON DES ENFANTS.pdf b/Back-End/School/management/commands/References/LMDE/RIB LA MAISON DES ENFANTS.pdf new file mode 100644 index 0000000..528c95b Binary files /dev/null and b/Back-End/School/management/commands/References/LMDE/RIB LA MAISON DES ENFANTS.pdf differ diff --git a/Back-End/School/management/commands/References/LMDE/mandat_prelevement_sepa_interentreprise.pdf b/Back-End/School/management/commands/References/LMDE/mandat_prelevement_sepa_interentreprise.pdf new file mode 100644 index 0000000..71d16dc Binary files /dev/null and b/Back-End/School/management/commands/References/LMDE/mandat_prelevement_sepa_interentreprise.pdf differ diff --git a/Back-End/School/management/commands/init_mock_datas.py b/Back-End/School/management/commands/init_mock_datas.py new file mode 100644 index 0000000..7ba2447 --- /dev/null +++ b/Back-End/School/management/commands/init_mock_datas.py @@ -0,0 +1,427 @@ +from django.core.management.base import BaseCommand +from Subscriptions.models import ( + RegistrationForm, + RegistrationFileGroup +) +from Auth.models import Profile, ProfileRole +from Common.models import ( + PaymentModeType, + PaymentPlanType, + Level +) +from School.models import ( + FeeType, + Speciality, + Teacher, + DiscountType, + Fee, + Discount, +) +from django.utils import timezone +from dateutil.relativedelta import relativedelta +import os +from django.conf import settings +from faker import Faker +import random +import json + +from School.serializers import ( + FeeSerializer, + DiscountSerializer, + PaymentModeSerializer, + PaymentPlanSerializer, + SpecialitySerializer, + TeacherSerializer, + SchoolClassSerializer +) +from Auth.serializers import ProfileSerializer, ProfileRoleSerializer +from Establishment.serializers import EstablishmentSerializer +from Subscriptions.serializers import StudentSerializer + +from Subscriptions.util import getCurrentSchoolYear, getNextSchoolYear # Import des fonctions nécessaires + +# Définir le chemin vers le dossier mock_datas +MOCK_DATAS_PATH = os.path.join(settings.BASE_DIR, 'School', 'management', 'mock_datas') + +class Command(BaseCommand): + help = 'Initialise toutes les données mock' + + def handle(self, *args, **kwargs): + self.init_establishments() + self.init_profiles() + self.init_fees() + self.init_discounts() + self.init_payment_modes() + self.init_payment_plans() + self.init_specialities() + self.init_teachers() + self.init_school_classes() + self.init_file_group() + self.init_register_form() + + def load_data(self, filename): + with open(os.path.join(MOCK_DATAS_PATH, filename), 'r') as file: + return json.load(file) + + def init_establishments(self): + establishments_data = self.load_data('establishments.json') + + self.establishments = [] + for establishment_data in establishments_data: + serializer = EstablishmentSerializer(data=establishment_data) + if serializer.is_valid(): + establishment = serializer.save() + self.establishments.append(establishment) + self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} created or updated successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for establishment: {serializer.errors}')) + + def init_profiles(self): + fake = Faker() + + for _ in range(50): + # Générer des données fictives pour le profil + profile_data = { + "username": fake.user_name(), + "email": fake.email(), + "password": "Provisoire01!", + "code": "", + "datePeremption": "" + } + + # Créer le profil + profile_serializer = ProfileSerializer(data=profile_data) + if profile_serializer.is_valid(): + profile = profile_serializer.save() + profile.set_password(profile_data["password"]) + profile.save() + self.stdout.write(self.style.SUCCESS(f'Profile {profile.email} created successfully')) + + # Créer entre 1 et 3 ProfileRole pour chaque profil + num_roles = random.randint(1, 3) + created_roles = set() + for _ in range(num_roles): + establishment = random.choice(self.establishments) + role_type = random.choice([ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN, ProfileRole.RoleType.PROFIL_PARENT]) + + # Vérifier si le rôle existe déjà pour cet établissement + if (establishment.id, role_type) in created_roles: + continue + + profile_role_data = { + "profile": profile.id, + "establishment": establishment.id, + "role_type": role_type, + "is_active": random.choice([True, False]) + } + + profile_role_serializer = ProfileRoleSerializer(data=profile_role_data) + if profile_role_serializer.is_valid(): + profile_role_serializer.save() + created_roles.add((establishment.id, role_type)) + self.stdout.write(self.style.SUCCESS(f'ProfileRole for {profile.email} created successfully with role type {role_type}')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for profile role: {profile_role_serializer.errors}')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for profile: {profile_serializer.errors}')) + + def init_fees(self): + fees_data = self.load_data('fees.json') + + for fee_data in fees_data: + establishment = random.choice(self.establishments) + print(f'establishment : {establishment}') + fee_data["name"] = fee_data['name'] + fee_data["establishment"] = establishment.id + fee_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]) + + serializer = FeeSerializer(data=fee_data) + if serializer.is_valid(): + fee = serializer.save() + self.stdout.write(self.style.SUCCESS(f'Fee {fee.name} created successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for fee: {serializer.errors}')) + + def init_discounts(self): + discounts_data = self.load_data('discounts.json') + + for discount_data in discounts_data: + establishment = random.choice(self.establishments) + discount_data["name"] = discount_data['name'] + discount_data["establishment"] = establishment.id + discount_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]) + discount_data["discount_type"] = random.choice([DiscountType.CURRENCY, DiscountType.PERCENT]) + + serializer = DiscountSerializer(data=discount_data) + if serializer.is_valid(): + discount = serializer.save() + self.stdout.write(self.style.SUCCESS(f'Discount {discount.name} created successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for discount: {serializer.errors}')) + + def init_payment_modes(self): + modes = list(PaymentModeType.objects.filter(code__in=["SEPA", "TRANSFER", "CHECK", "CASH"])) + types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE] + + for establishment in self.establishments: + for mode in modes: + for type in types: + payment_mode_data = { + "mode": mode.pk, + "type": type, + "establishment": establishment.id + } + + serializer = PaymentModeSerializer(data=payment_mode_data) + if serializer.is_valid(): + payment_mode = serializer.save() + self.stdout.write(self.style.SUCCESS(f'Payment Mode {payment_mode} created successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for payment mode: {serializer.errors}')) + + def init_payment_plans(self): + frequencies = list(PaymentPlanType.objects.filter(code__in=["ONE_TIME", "THREE_TIMES", "TEN_TIMES", "TWELVE_TIMES"])) + types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE] + current_date = timezone.now().date() + + for establishment in self.establishments: + for frequency in frequencies: + for type in types: + payment_plan_data = { + "frequency": frequency.pk, + "type": type, + "establishment": establishment.id, + "due_dates": self.generate_due_dates(frequency, current_date) + } + + serializer = PaymentPlanSerializer(data=payment_plan_data) + if serializer.is_valid(): + payment_plan = serializer.save() + self.stdout.write(self.style.SUCCESS(f'Payment Plan {payment_plan} created successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for payment plan: {serializer.errors}')) + + def generate_due_dates(self, frequency, start_date): + if frequency.code == "ONE_TIME": + return [start_date + relativedelta(months=1)] + elif frequency.code == "THREE_TIMES": + return [start_date + relativedelta(months=1+4*i) for i in range(3)] + elif frequency.code == "TEN_TIMES": + return [start_date + relativedelta(months=1+i) for i in range(10)] + elif frequency.code == "TWELVE_TIMES": + return [start_date + relativedelta(months=1+i) for i in range(12)] + + def init_specialities(self): + specialities_data = self.load_data('specialities.json') + + for speciality_data in specialities_data: + establishment = random.choice(self.establishments) + speciality_data["name"] = speciality_data['name'] + speciality_data["establishment"] = establishment.id + + serializer = SpecialitySerializer(data=speciality_data) + if serializer.is_valid(): + speciality = serializer.save() + self.stdout.write(self.style.SUCCESS(f'Speciality {speciality.name} created successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for speciality: {serializer.errors}')) + + def init_teachers(self): + fake = Faker() + + # Récupérer tous les profils existants avec un rôle ECOLE ou ADMIN + profiles_with_roles = Profile.objects.filter(roles__role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN]).distinct() + + if not profiles_with_roles.exists(): + self.stdout.write(self.style.ERROR('No profiles with role_type ECOLE or ADMIN found')) + return + + used_profiles = set() + + for _ in range(15): + # Récupérer un profil aléatoire qui n'a pas encore été utilisé + available_profiles = profiles_with_roles.exclude(id__in=used_profiles) + if not available_profiles.exists(): + self.stdout.write(self.style.ERROR('Not enough profiles with role_type ECOLE or ADMIN available')) + break + + profile = random.choice(available_profiles) + used_profiles.add(profile.id) + + # Récupérer les ProfileRole associés au profil avec les rôles ECOLE ou ADMIN + profile_roles = ProfileRole.objects.filter(profile=profile, role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN]) + + if not profile_roles.exists(): + self.stdout.write(self.style.ERROR(f'No ProfileRole with role_type ECOLE or ADMIN found for profile {profile.email}')) + continue + + profile_role = random.choice(profile_roles) + + # Générer des données fictives pour l'enseignant + teacher_data = { + "last_name": fake.last_name(), + "first_name": fake.first_name(), + "profile_role": profile_role.id + } + + establishment_specialities = list(Speciality.objects.filter(establishment=profile_role.establishment)) + num_specialities = min(random.randint(1, 3), len(establishment_specialities)) + selected_specialities = random.sample(establishment_specialities, num_specialities) + + # Créer l'enseignant si il n'existe pas + teacher_serializer = TeacherSerializer(data=teacher_data) + if teacher_serializer.is_valid(): + teacher = teacher_serializer.save() + # Associer les spécialités + teacher.specialities.set(selected_specialities) + teacher.save() + self.stdout.write(self.style.SUCCESS(f'Teacher {teacher.last_name} created successfully for establishment {profile_role.establishment.name}')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for teacher: {teacher_serializer.errors}')) + + self.stdout.write(self.style.SUCCESS('Teachers initialized or updated successfully')) + + def init_school_classes(self): + school_classes_data = self.load_data('school_classes.json') + levels = list(Level.objects.all()) + + for index, class_data in enumerate(school_classes_data, start=1): + # Randomize establishment + establishment = random.choice(self.establishments) + class_data["atmosphere_name"] = f"Classe {index}" + class_data["establishment"] = establishment.id + + # Randomize levels + class_data["levels"] = [level.id for level in random.sample(levels, random.randint(1, min(5, len(levels))))] + + # Randomize teachers + establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment)) + if len(establishment_teachers) > 0: + num_teachers = min(2, len(establishment_teachers)) + selected_teachers = random.sample(establishment_teachers, num_teachers) + teachers_ids = [teacher.id for teacher in selected_teachers] + else: + teachers_ids = [] + + # Use the serializer to create or update the school class + class_data["teachers"] = teachers_ids + serializer = SchoolClassSerializer(data=class_data) + if serializer.is_valid(): + school_class = serializer.save() + self.stdout.write(self.style.SUCCESS(f'SchoolClass {school_class.atmosphere_name} created or updated successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for school class: {serializer.errors}')) + + self.stdout.write(self.style.SUCCESS('SchoolClasses initialized or updated successfully')) + + def init_file_group(self): + fake = Faker() + + for establishment in self.establishments: + for i in range(1, 4): # Créer 3 groupes de fichiers par établissement + name = f"Fichiers d'inscription - {fake.word()}" + description = fake.sentence() + group_data = { + "name": name, + "description": description, + "establishment": establishment + } + RegistrationFileGroup.objects.update_or_create(name=name, defaults=group_data) + self.stdout.write(self.style.SUCCESS(f'RegistrationFileGroup {name} initialized or updated successfully')) + + self.stdout.write(self.style.SUCCESS('All RegistrationFileGroups initialized or updated successfully')) + + def init_register_form(self): + fake = Faker('fr_FR') # Utiliser le locale français pour Faker + + file_group_count = RegistrationFileGroup.objects.count() + levels = list(Level.objects.all()) + + # Récupérer tous les profils existants avec un ProfileRole Parent + profiles_with_parent_role = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct() + + if not profiles_with_parent_role.exists(): + self.stdout.write(self.style.ERROR('No profiles with ProfileRole Parent found')) + return + + used_profiles = set() + + for _ in range(50): + # Récupérer un profil aléatoire qui n'a pas encore été utilisé + available_profiles = profiles_with_parent_role.exclude(id__in=used_profiles) + if not available_profiles.exists(): + self.stdout.write(self.style.ERROR('Not enough profiles with ProfileRole Parent available')) + break + + profile = random.choice(available_profiles) + used_profiles.add(profile.id) + + # Récupérer le ProfileRole Parent associé au profil + profile_roles = ProfileRole.objects.filter(profile=profile, role_type=ProfileRole.RoleType.PROFIL_PARENT) + profile_role = random.choice(profile_roles) + + # Générer des données fictives pour le guardian + guardian_data = { + "profile_role": profile_role.id, + "last_name": fake.last_name(), + "first_name": fake.first_name(), + "birth_date": fake.date_of_birth().strftime('%Y-%m-%d'), + "address": fake.address(), + "phone": "+33122334455", + "profession": fake.job() + } + + # Générer des données fictives pour l'étudiant + student_data = { + "last_name": fake.last_name(), + "first_name": fake.first_name(), + "address": fake.address(), + "birth_date": fake.date_of_birth(), + "birth_place": fake.city(), + "birth_postal_code": fake.postcode(), + "nationality": fake.country(), + "attending_physician": fake.name(), + "level": random.choice(levels).id, + "guardians": [guardian_data], + "sibling": [] + } + + # Créer ou mettre à jour l'étudiant + student_serializer = StudentSerializer(data=student_data) + if student_serializer.is_valid(): + student = student_serializer.save() + self.stdout.write(self.style.SUCCESS(f'Student {student.last_name} created successfully')) + else: + self.stdout.write(self.style.ERROR(f'Error in data for student: {student_serializer.errors}')) + continue + + # Récupérer les frais et les réductions + fees = Fee.objects.filter(id__in=[1, 2, 3, 4]) + discounts = Discount.objects.filter(id__in=[1]) + + # Déterminer l'année scolaire (soit l'année en cours, soit l'année prochaine) + school_year = random.choice([getCurrentSchoolYear(), getNextSchoolYear()]) + + # Créer les données du formulaire d'inscription + register_form_data = { + "fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)), + "establishment": profile_role.establishment, + "status": fake.random_int(min=1, max=3), + "school_year": school_year # Ajouter l'année scolaire + } + + # Créer ou mettre à jour le formulaire d'inscription + register_form, created = RegistrationForm.objects.get_or_create( + student=student, + establishment=profile_role.establishment, + defaults=register_form_data + ) + + if created: + register_form.fees.set(fees) + register_form.discounts.set(discounts) + self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} created successfully with school year {school_year}')) + else: + self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} already exists')) + + self.stdout.write(self.style.SUCCESS('50 RegistrationForms initialized or updated successfully')) \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/discounts.json b/Back-End/School/management/mock_datas/discounts.json new file mode 100644 index 0000000..f498b92 --- /dev/null +++ b/Back-End/School/management/mock_datas/discounts.json @@ -0,0 +1,42 @@ +[ + { + "name": "Parrainage", + "amount": "10.00", + "description": "Réduction pour parrainage" + }, + { + "name": "Réinscription", + "amount": "100.00", + "description": "Réduction pour Réinscription" + }, + { + "name": "Famille nombreuse", + "amount": "50.00", + "description": "Réduction pour les familles nombreuses" + }, + { + "name": "Excellence académique", + "amount": "200.00", + "description": "Réduction pour les élèves ayant des résultats académiques exceptionnels" + }, + { + "name": "Sportif de haut niveau", + "amount": "150.00", + "description": "Réduction pour les élèves pratiquant un sport de haut niveau" + }, + { + "name": "Artiste talentueux", + "amount": "100.00", + "description": "Réduction pour les élèves ayant des talents artistiques" + }, + { + "name": "Bourse d'études", + "amount": "300.00", + "description": "Réduction pour les élèves bénéficiant d'une bourse d'études" + }, + { + "name": "Réduction spéciale", + "amount": "75.00", + "description": "Réduction spéciale pour des occasions particulières" + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/establishments.json b/Back-End/School/management/mock_datas/establishments.json new file mode 100644 index 0000000..99a8db5 --- /dev/null +++ b/Back-End/School/management/mock_datas/establishments.json @@ -0,0 +1,23 @@ +[ + { + "name": "Ecole A", + "address": "Adresse de l'Ecole A", + "total_capacity": 69, + "establishment_type": [1, 2], + "licence_code": "" + }, + { + "name": "Ecole B", + "address": "Adresse de l'Ecole B", + "total_capacity": 100, + "establishment_type": [2, 3], + "licence_code": "" + }, + { + "name": "Ecole C", + "address": "Adresse de l'Ecole C", + "total_capacity": 50, + "establishment_type": [1], + "licence_code": "" + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/fees.json b/Back-End/School/management/mock_datas/fees.json new file mode 100644 index 0000000..043afe2 --- /dev/null +++ b/Back-End/School/management/mock_datas/fees.json @@ -0,0 +1,32 @@ +[ + { + "name": "Frais d'inscription", + "base_amount": "150.00", + "description": "Montant de base", + "is_active": true + }, + { + "name": "Matériel", + "base_amount": "85.00", + "description": "Livres / jouets", + "is_active": true + }, + { + "name": "Sorties périscolaires", + "base_amount": "120.00", + "description": "Sorties", + "is_active": true + }, + { + "name": "Les colibris", + "base_amount": "4500.00", + "description": "TPS / PS / MS / GS", + "is_active": true + }, + { + "name": "Les butterflies", + "base_amount": "5000.00", + "description": "CP / CE1 / CE2 / CM1 / CM2", + "is_active": true + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/school_classes.json b/Back-End/School/management/mock_datas/school_classes.json new file mode 100644 index 0000000..fe8b388 --- /dev/null +++ b/Back-End/School/management/mock_datas/school_classes.json @@ -0,0 +1,52 @@ +[ + { + "age_range": "3-6", + "number_of_students": 14, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [2, 3, 4], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5] + }, + { + "age_range": "2-3", + "number_of_students": 5, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [1], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5] + }, + { + "age_range": "6-12", + "number_of_students": 21, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [5, 6, 7, 8, 9], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5] + }, + { + "age_range": "4-6", + "number_of_students": 18, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [4, 5], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5] + }, + { + "age_range": "7-9", + "number_of_students": 20, + "teaching_language": "", + "school_year": "2024-2025", + "levels": [6, 7], + "type": 1, + "time_range": ["08:30", "17:30"], + "opening_days": [1, 2, 4, 5] + } +] \ No newline at end of file diff --git a/Back-End/School/management/mock_datas/specialities.json b/Back-End/School/management/mock_datas/specialities.json new file mode 100644 index 0000000..1806e7c --- /dev/null +++ b/Back-End/School/management/mock_datas/specialities.json @@ -0,0 +1,42 @@ +[ + { + "name": "GROUPE", + "color_code": "#FF0000" + }, + { + "name": "MATHS", + "color_code": "#0a98f0" + }, + { + "name": "ANGLAIS", + "color_code": "#f708d7" + }, + { + "name": "FRANCAIS", + "color_code": "#04f108" + }, + { + "name": "HISTOIRE", + "color_code": "#ffb005" + }, + { + "name": "SPORT", + "color_code": "#bbb9b9" + }, + { + "name": "SCIENCES", + "color_code": "#00FF00" + }, + { + "name": "MUSIQUE", + "color_code": "#0000FF" + }, + { + "name": "ART", + "color_code": "#FF00FF" + }, + { + "name": "INFORMATIQUE", + "color_code": "#00FFFF" + } +] \ No newline at end of file diff --git a/Back-End/School/migrations/0001_initial.py b/Back-End/School/migrations/0001_initial.py new file mode 100644 index 0000000..4289620 --- /dev/null +++ b/Back-End/School/migrations/0001_initial.py @@ -0,0 +1,145 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.contrib.postgres.fields +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('Auth', '0001_initial'), + ('Common', '0001_initial'), + ('Establishment', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Competency', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField()), + ('end_of_cycle', models.BooleanField(blank=True, default=False, null=True)), + ('level', models.CharField(blank=True, max_length=50, null=True)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='competencies', to='Common.category')), + ], + ), + migrations.CreateModel( + name='Discount', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, null=True)), + ('amount', models.DecimalField(decimal_places=2, default=0, max_digits=10)), + ('description', models.TextField(blank=True)), + ('discount_type', models.IntegerField(choices=[(0, 'Currency'), (1, 'Percent')], default=0)), + ('type', models.IntegerField(choices=[(0, 'Registration Fee'), (1, 'Tuition Fee')], default=0)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='discounts', to='Establishment.establishment')), + ], + ), + migrations.CreateModel( + name='EstablishmentCompetency', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('custom_name', models.TextField(blank=True, help_text='Nom de la compétence custom', null=True)), + ('is_required', models.BooleanField(default=True)), + ('competency', models.ForeignKey(blank=True, help_text='Compétence de référence (optionnelle si custom)', null=True, on_delete=django.db.models.deletion.CASCADE, to='School.competency')), + ('custom_category', models.ForeignKey(blank=True, help_text='Catégorie de la compétence custom', null=True, on_delete=django.db.models.deletion.CASCADE, to='Common.category')), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Establishment.establishment')), + ], + options={ + 'unique_together': {('establishment', 'competency', 'custom_name', 'custom_category')}, + }, + ), + migrations.AddField( + model_name='competency', + name='establishments', + field=models.ManyToManyField(blank=True, related_name='competencies', through='School.EstablishmentCompetency', to='Establishment.establishment'), + ), + migrations.CreateModel( + name='Fee', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, null=True)), + ('base_amount', models.DecimalField(decimal_places=2, default=0, max_digits=10)), + ('description', models.TextField(blank=True)), + ('is_active', models.BooleanField(default=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('type', models.IntegerField(choices=[(0, 'Registration Fee'), (1, 'Tuition Fee')], default=0)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fees', to='Establishment.establishment')), + ], + ), + migrations.CreateModel( + name='PaymentMode', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.IntegerField(choices=[(0, 'Registration Fee'), (1, 'Tuition Fee')], default=0)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payment_modes', to='Establishment.establishment')), + ('mode', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='payment_modes', to='Common.paymentmodetype')), + ], + ), + migrations.CreateModel( + name='PaymentPlan', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('due_dates', django.contrib.postgres.fields.ArrayField(base_field=models.DateField(), blank=True, null=True, size=None)), + ('type', models.IntegerField(choices=[(0, 'Registration Fee'), (1, 'Tuition Fee')], default=0)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payment_plans', to='Establishment.establishment')), + ('plan_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='payment_plans', to='Common.paymentplantype')), + ], + ), + migrations.CreateModel( + name='SchoolClass', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('atmosphere_name', models.CharField(blank=True, max_length=255, null=True)), + ('age_range', models.JSONField(blank=True, null=True)), + ('number_of_students', models.PositiveIntegerField(blank=True, null=True)), + ('teaching_language', models.CharField(blank=True, max_length=255)), + ('school_year', models.CharField(blank=True, max_length=9)), + ('updated_date', models.DateTimeField(auto_now=True)), + ('type', models.IntegerField(choices=[(1, 'Annuel'), (2, 'Semestriel'), (3, 'Trimestriel')], default=1)), + ('time_range', models.JSONField(default=list)), + ('opening_days', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), default=list, size=None)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='school_classes', to='Establishment.establishment')), + ('levels', models.ManyToManyField(blank=True, related_name='school_classes', to='Common.level')), + ], + ), + migrations.CreateModel( + name='Planning', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('level', models.IntegerField(blank=True, choices=[(1, 'Très Petite Section (TPS)'), (2, 'Petite Section (PS)'), (3, 'Moyenne Section (MS)'), (4, 'Grande Section (GS)'), (5, 'Cours Préparatoire (CP)'), (6, 'Cours Élémentaire 1 (CE1)'), (7, 'Cours Élémentaire 2 (CE2)'), (8, 'Cours Moyen 1 (CM1)'), (9, 'Cours Moyen 2 (CM2)')], null=True)), + ('schedule', models.JSONField(default=dict)), + ('school_class', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='plannings', to='School.schoolclass')), + ], + ), + migrations.CreateModel( + name='Speciality', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('updated_date', models.DateTimeField(auto_now=True)), + ('color_code', models.CharField(default='#FFFFFF', max_length=7)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='specialities', to='Establishment.establishment')), + ], + ), + migrations.CreateModel( + name='Teacher', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('last_name', models.CharField(max_length=100)), + ('first_name', models.CharField(max_length=100)), + ('updated_date', models.DateTimeField(auto_now=True)), + ('profile_role', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teacher_profile', to='Auth.profilerole')), + ('specialities', models.ManyToManyField(blank=True, to='School.speciality')), + ], + ), + migrations.AddField( + model_name='schoolclass', + name='teachers', + field=models.ManyToManyField(blank=True, to='School.teacher'), + ), + ] diff --git a/Back-End/School/migrations/__init__.py b/Back-End/School/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/School/models.py b/Back-End/School/models.py index 31d2a80..b9bffc8 100644 --- a/Back-End/School/models.py +++ b/Back-End/School/models.py @@ -1,8 +1,9 @@ from django.db import models -from Auth.models import Profile from django.db.models import JSONField from django.dispatch import receiver from django.contrib.postgres.fields import ArrayField +from django.utils.translation import gettext_lazy as _ +from django.core.exceptions import ValidationError LEVEL_CHOICES = [ (1, 'Très Petite Section (TPS)'), @@ -20,6 +21,7 @@ class Speciality(models.Model): name = models.CharField(max_length=100) updated_date = models.DateTimeField(auto_now=True) color_code = models.CharField(max_length=7, default='#FFFFFF') + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='specialities') def __str__(self): return self.name @@ -27,9 +29,8 @@ class Speciality(models.Model): class Teacher(models.Model): last_name = models.CharField(max_length=100) first_name = models.CharField(max_length=100) - email = models.EmailField(unique=True) specialities = models.ManyToManyField(Speciality, blank=True) - associated_profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True, blank=True) + profile_role = models.OneToOneField('Auth.ProfileRole', on_delete=models.CASCADE, related_name='teacher_profile', null=True, blank=True) updated_date = models.DateTimeField(auto_now=True) def __str__(self): @@ -43,16 +44,17 @@ class SchoolClass(models.Model): ] atmosphere_name = models.CharField(max_length=255, null=True, blank=True) - age_range = models.JSONField(blank=True) - number_of_students = models.PositiveIntegerField(blank=True) + age_range = models.JSONField(null=True, blank=True) + number_of_students = models.PositiveIntegerField(null=True, blank=True) teaching_language = models.CharField(max_length=255, blank=True) school_year = models.CharField(max_length=9, blank=True) - updated_date = models.DateTimeField(auto_now_add=True) + updated_date = models.DateTimeField(auto_now=True) teachers = models.ManyToManyField(Teacher, blank=True) - levels = ArrayField(models.IntegerField(choices=LEVEL_CHOICES), default=list) + levels = models.ManyToManyField('Common.Level', blank=True, related_name='school_classes') type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1) time_range = models.JSONField(default=list) opening_days = ArrayField(models.IntegerField(), default=list) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='school_classes') def __str__(self): return self.atmosphere_name @@ -64,3 +66,93 @@ class Planning(models.Model): def __str__(self): return f'Planning for {self.level} of {self.school_class.atmosphere_name}' + +class DiscountType(models.IntegerChoices): + CURRENCY = 0, 'Currency' + PERCENT = 1, 'Percent' + +class FeeType(models.IntegerChoices): + REGISTRATION_FEE = 0, 'Registration Fee' + TUITION_FEE = 1, 'Tuition Fee' + +class Discount(models.Model): + name = models.CharField(max_length=255, null=True, blank=True) + amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) + description = models.TextField(blank=True) + discount_type = models.IntegerField(choices=DiscountType.choices, default=DiscountType.CURRENCY) + type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) + updated_at = models.DateTimeField(auto_now=True) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='discounts') + + def __str__(self): + return self.name + +class Fee(models.Model): + name = models.CharField(max_length=255, null=True, blank=True) + base_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0) + description = models.TextField(blank=True) + is_active = models.BooleanField(default=True) + updated_at = models.DateTimeField(auto_now=True) + type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='fees') + + def __str__(self): + return self.name + +class PaymentPlan(models.Model): + plan_type = models.ForeignKey('Common.PaymentPlanType', on_delete=models.PROTECT, related_name='payment_plans') + due_dates = ArrayField(models.DateField(), null=True, blank=True) + type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_plans') + + def __str__(self): + return f"{self.plan_type.label} - {self.get_type_display()}" + +class PaymentMode(models.Model): + mode = models.ForeignKey('Common.PaymentModeType', on_delete=models.PROTECT, related_name='payment_modes') + type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_modes') + + def __str__(self): + return f"{self.mode.label} - {self.get_type_display()}" + +class Competency(models.Model): + name = models.TextField() + end_of_cycle = models.BooleanField(default=False, null=True, blank=True) + level = models.CharField(max_length=50, null=True, blank=True) + category = models.ForeignKey('Common.Category', on_delete=models.CASCADE, related_name='competencies') + + establishments = models.ManyToManyField( + 'Establishment.Establishment', + through='School.EstablishmentCompetency', + related_name='competencies', + blank=True + ) + + def __str__(self): + return self.name + +class EstablishmentCompetency(models.Model): + """ + Relation entre un établissement et une compétence. + Peut référencer une compétence de référence OU une compétence custom propre à l'établissement. + """ + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE) + competency = models.ForeignKey( + 'School.Competency', on_delete=models.CASCADE, null=True, blank=True, + help_text="Compétence de référence (optionnelle si custom)" + ) + custom_name = models.TextField(null=True, blank=True, help_text="Nom de la compétence custom") + custom_category = models.ForeignKey( + 'Common.Category', on_delete=models.CASCADE, null=True, blank=True, + help_text="Catégorie de la compétence custom" + ) + is_required = models.BooleanField(default=True) + + class Meta: + unique_together = ('establishment', 'competency', 'custom_name', 'custom_category') + + def __str__(self): + if self.competency: + return f"{self.establishment.name} - {self.competency.name}" + return f"{self.establishment.name} - {self.custom_name} (custom)" \ No newline at end of file diff --git a/Back-End/School/serializers.py b/Back-End/School/serializers.py index 320efd5..801b1c5 100644 --- a/Back-End/School/serializers.py +++ b/Back-End/School/serializers.py @@ -1,13 +1,36 @@ from rest_framework import serializers -from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES -from Subscriptions.models import RegistrationForm -from Subscriptions.serializers import StudentSerializer -from Auth.serializers import ProfileSerializer -from Auth.models import Profile -from N3wtSchool import settings, bdd +from .models import ( + Teacher, + Speciality, + SchoolClass, + Planning, + LEVEL_CHOICES, + Discount, + Fee, + PaymentPlan, + PaymentMode, + EstablishmentCompetency, + Competency +) +from Auth.models import Profile, ProfileRole +from Subscriptions.models import Student +from Establishment.models import Establishment +from Auth.serializers import ProfileRoleSerializer +from N3wtSchool import settings from django.utils import timezone import pytz + +class CompetencySerializer(serializers.ModelSerializer): + class Meta: + model = Competency + fields = '__all__' + +class EstablishmentCompetencySerializer(serializers.ModelSerializer): + class Meta: + model = EstablishmentCompetency + fields = '__all__' + class SpecialitySerializer(serializers.ModelSerializer): updated_date_formatted = serializers.SerializerMethodField() @@ -31,42 +54,107 @@ class TeacherDetailSerializer(serializers.ModelSerializer): class TeacherSerializer(serializers.ModelSerializer): specialities = serializers.PrimaryKeyRelatedField(queryset=Speciality.objects.all(), many=True, required=False) - associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True) + specialities_details = serializers.SerializerMethodField() updated_date_formatted = serializers.SerializerMethodField() + role_type = serializers.SerializerMethodField() + profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False) + profile_role_data = ProfileRoleSerializer(write_only=True, required=False) + associated_profile_email = serializers.SerializerMethodField() class Meta: model = Teacher fields = '__all__' + def create_or_update_teacher(self, teacher_data): + profile_role_data = teacher_data.pop('profile_role_data', None) + profile_role = teacher_data.pop('profile_role', None) + + # Gestion du ProfileRole + if profile_role_data: + # Vérifiez si 'profile' est un objet ou une clé primaire + if isinstance(profile_role_data.get('profile'), Profile): + profile_role_data['profile'] = profile_role_data['profile'].id + establishment_id = profile_role_data.pop('establishment').id + profile_role_data['establishment'] = establishment_id + + # Vérifiez si un ID est fourni pour une mise à jour + profile_role_id = profile_role_data.get('id') + if profile_role_id: + # Mettre à jour un ProfileRole existant + try: + profile_role_instance = ProfileRole.objects.get(id=profile_role_id) + profile_role_serializer = ProfileRoleSerializer(profile_role_instance, data=profile_role_data) + profile_role_serializer.is_valid(raise_exception=True) + profile_role = profile_role_serializer.save() + except ProfileRole.DoesNotExist: + raise serializers.ValidationError({"profile_role_data": f"ProfileRole with id {profile_role_id} does not exist."}) + else: + # Créer un nouveau ProfileRole + profile_role_serializer = ProfileRoleSerializer(data=profile_role_data) + profile_role_serializer.is_valid(raise_exception=True) + profile_role = profile_role_serializer.save() + elif profile_role: + # Récupérer un ProfileRole existant + profile_role = ProfileRole.objects.get(id=profile_role.id) + + if profile_role: + teacher_data['profile_role'] = profile_role + + # Vérifier si un Teacher existe déjà pour ce profile_role + teacher_instance = Teacher.objects.filter(profile_role=profile_role).first() + + if teacher_instance: + # Mettre à jour les champs existants + for key, value in teacher_data.items(): + setattr(teacher_instance, key, value) + teacher_instance.save() + created = False + else: + # Créer un nouveau Teacher + teacher_instance = Teacher.objects.create(**teacher_data) + created = True + + return teacher_instance + def create(self, validated_data): - specialities_data = validated_data.pop('specialities', None) - associated_profile = validated_data.pop('associated_profile', None) - teacher = Teacher.objects.create(**validated_data) + specialities_data = validated_data.pop('specialities', []) + teacher_instance = self.create_or_update_teacher(validated_data) + + # Associer les spécialités if specialities_data: - teacher.specialities.set(specialities_data) - if associated_profile: - teacher.associated_profile = associated_profile - teacher.save() - return teacher + teacher_instance.specialities.set(specialities_data) + + return teacher_instance def update(self, instance, validated_data): specialities_data = validated_data.pop('specialities', []) - instance.last_name = validated_data.get('last_name', instance.last_name) - instance.first_name = validated_data.get('first_name', instance.first_name) - instance.email = validated_data.get('email', instance.email) - instance.associated_profile = validated_data.get('associated_profile', instance.associated_profile) - instance.save() + teacher_instance = self.create_or_update_teacher(validated_data) + + # Mettre à jour les spécialités if specialities_data: - instance.specialities.set(specialities_data) - return instance + teacher_instance.specialities.set(specialities_data) + + return teacher_instance def get_updated_date_formatted(self, obj): utc_time = timezone.localtime(obj.updated_date) # Convert to local time local_tz = pytz.timezone(settings.TZ_APPLI) local_time = utc_time.astimezone(local_tz) - return local_time.strftime("%d-%m-%Y %H:%M") + def get_specialities_details(self, obj): + return [{'id': speciality.id, 'name': speciality.name, 'color_code': speciality.color_code} for speciality in obj.specialities.all()] + + def get_associated_profile_email(self, obj): + if obj.profile_role and obj.profile_role.profile: + return obj.profile_role.profile.email + return None + + def get_role_type(self, obj): + if obj.profile_role: + return obj.profile_role.role_type + return None + class PlanningSerializer(serializers.ModelSerializer): class Meta: model = Planning @@ -77,9 +165,17 @@ class PlanningSerializer(serializers.ModelSerializer): internal_value['schedule'] = data.get('schedule', {}) return internal_value +class StudentDetailSerializer(serializers.ModelSerializer): + class Meta: + model = Student + fields = ['id', 'last_name', 'first_name', 'photo', 'level'] + class SchoolClassSerializer(serializers.ModelSerializer): updated_date_formatted = serializers.SerializerMethodField() teachers = serializers.PrimaryKeyRelatedField(queryset=Teacher.objects.all(), many=True, required=False) + establishment = serializers.PrimaryKeyRelatedField(queryset=Establishment.objects.all(), required=False) + teachers_details = serializers.SerializerMethodField() + students = StudentDetailSerializer(many=True, read_only=True) class Meta: model = SchoolClass @@ -89,20 +185,21 @@ class SchoolClassSerializer(serializers.ModelSerializer): teachers_data = validated_data.pop('teachers', []) levels_data = validated_data.pop('levels', []) plannings_data = validated_data.pop('plannings', []) - + school_class = SchoolClass.objects.create( atmosphere_name=validated_data.get('atmosphere_name', ''), age_range=validated_data.get('age_range', []), number_of_students=validated_data.get('number_of_students', 0), teaching_language=validated_data.get('teaching_language', ''), school_year=validated_data.get('school_year', ''), - levels=levels_data, - type=validated_data.get('type', 1), # Added here - time_range=validated_data.get('time_range', ['08:30', '17:30']), # Added here - opening_days=validated_data.get('opening_days', [1, 2, 4, 5]) # Added here + type=validated_data.get('type', 1), + time_range=validated_data.get('time_range', ['08:30', '17:30']), + opening_days=validated_data.get('opening_days', [1, 2, 4, 5]), + establishment=validated_data.get('establishment', None) ) school_class.teachers.set(teachers_data) + school_class.levels.set(levels_data) for planning_data in plannings_data: Planning.objects.create( @@ -123,13 +220,14 @@ class SchoolClassSerializer(serializers.ModelSerializer): instance.number_of_students = validated_data.get('number_of_students', instance.number_of_students) instance.teaching_language = validated_data.get('teaching_language', instance.teaching_language) instance.school_year = validated_data.get('school_year', instance.school_year) - instance.levels = levels_data - instance.type = validated_data.get('type', instance.type) # Added here - instance.time_range = validated_data.get('time_range', instance.time_range) # Added here - instance.opening_days = validated_data.get('opening_days', instance.opening_days) # Added here + instance.type = validated_data.get('type', instance.type) + instance.time_range = validated_data.get('time_range', instance.time_range) + instance.opening_days = validated_data.get('opening_days', instance.opening_days) + instance.establishment = validated_data.get('establishment', instance.establishment) instance.save() instance.teachers.set(teachers_data) + instance.levels.set(levels_data) existing_plannings = {planning.level: planning for planning in instance.plannings.all()} @@ -150,8 +248,48 @@ class SchoolClassSerializer(serializers.ModelSerializer): return instance + def get_teachers_details(self, obj): + return [{'id': teacher.id, 'last_name': teacher.last_name, 'first_name': teacher.first_name} for teacher in obj.teachers.all()] + def get_updated_date_formatted(self, obj): utc_time = timezone.localtime(obj.updated_date) local_tz = pytz.timezone(settings.TZ_APPLI) local_time = utc_time.astimezone(local_tz) - return local_time.strftime("%d-%m-%Y %H:%M") \ No newline at end of file + return local_time.strftime("%d-%m-%Y %H:%M") + +class DiscountSerializer(serializers.ModelSerializer): + updated_at_formatted = serializers.SerializerMethodField() + establishment = serializers.PrimaryKeyRelatedField(queryset=Establishment.objects.all(), required=False) + class Meta: + model = Discount + fields = '__all__' + + def get_updated_at_formatted(self, obj): + utc_time = timezone.localtime(obj.updated_at) + local_tz = pytz.timezone(settings.TZ_APPLI) + local_time = utc_time.astimezone(local_tz) + return local_time.strftime("%d-%m-%Y %H:%M") + +class FeeSerializer(serializers.ModelSerializer): + updated_at_formatted = serializers.SerializerMethodField() + establishment = serializers.PrimaryKeyRelatedField(queryset=Establishment.objects.all(), required=False) + + class Meta: + model = Fee + fields = '__all__' + + def get_updated_at_formatted(self, obj): + utc_time = timezone.localtime(obj.updated_at) + local_tz = pytz.timezone(settings.TZ_APPLI) + local_time = utc_time.astimezone(local_tz) + return local_time.strftime("%d-%m-%Y %H:%M") + +class PaymentPlanSerializer(serializers.ModelSerializer): + class Meta: + model = PaymentPlan + fields = '__all__' + +class PaymentModeSerializer(serializers.ModelSerializer): + class Meta: + model = PaymentMode + fields = '__all__' \ No newline at end of file diff --git a/Back-End/School/urls.py b/Back-End/School/urls.py index 77897c3..9cec102 100644 --- a/Back-End/School/urls.py +++ b/Back-End/School/urls.py @@ -1,21 +1,46 @@ from django.urls import path, re_path -from School.views import TeachersView, TeacherView, SpecialitiesView, SpecialityView, ClassesView, ClasseView, PlanningsView, PlanningView +from .views import ( + TeacherListCreateView, TeacherDetailView, + SpecialityListCreateView, SpecialityDetailView, + SchoolClassListCreateView, SchoolClassDetailView, + PlanningListCreateView, PlanningDetailView, + FeeListCreateView, FeeDetailView, + DiscountListCreateView, DiscountDetailView, + PaymentPlanListCreateView, PaymentPlanDetailView, + PaymentModeListCreateView, PaymentModeDetailView, + CompetencyListCreateView, CompetencyDetailView, + EstablishmentCompetencyListCreateView, EstablishmentCompetencyDetailView, +) urlpatterns = [ - re_path(r'^specialities$', SpecialitiesView.as_view(), name="specialities"), - re_path(r'^speciality$', SpecialityView.as_view(), name="speciality"), - re_path(r'^speciality/([0-9]+)$', SpecialityView.as_view(), name="speciality"), + re_path(r'^specialities$', SpecialityListCreateView.as_view(), name="speciality_list_create"), + re_path(r'^specialities/(?P[0-9]+)$', SpecialityDetailView.as_view(), name="speciality_detail"), - re_path(r'^teachers$', TeachersView.as_view(), name="teachers"), - re_path(r'^teacher$', TeacherView.as_view(), name="teacher"), - re_path(r'^teacher/([0-9]+)$', TeacherView.as_view(), name="teacher"), + re_path(r'^teachers$', TeacherListCreateView.as_view(), name="teacher_list_create"), + re_path(r'^teachers/(?P[0-9]+)', TeacherDetailView.as_view(), name="teacher_detail"), - re_path(r'^schoolClasses$', ClassesView.as_view(), name="schoolClasses"), - re_path(r'^schoolClass$', ClasseView.as_view(), name="schoolClass"), - re_path(r'^schoolClass/([0-9]+)$', ClasseView.as_view(), name="schoolClass"), + re_path(r'^schoolClasses$', SchoolClassListCreateView.as_view(), name="school_class_list_create"), + re_path(r'^schoolClasses/(?P[0-9]+)', SchoolClassDetailView.as_view(), name="school_class_detail"), - re_path(r'^plannings$', PlanningsView.as_view(), name="plannings"), - re_path(r'^planning$', PlanningView.as_view(), name="planning"), - re_path(r'^planning/([0-9]+)$', PlanningView.as_view(), name="planning"), + re_path(r'^plannings$', PlanningListCreateView.as_view(), name="planninglist_create"), + re_path(r'^plannings/(?P[0-9]+)$', PlanningDetailView.as_view(), name="planning_detail"), + + re_path(r'^fees$', FeeListCreateView.as_view(), name="fee_list_create"), + re_path(r'^fees/(?P[0-9]+)$', FeeDetailView.as_view(), name="fee_detail"), + + re_path(r'^discounts$', DiscountListCreateView.as_view(), name="discount_list_create"), + re_path(r'^discounts/(?P[0-9]+)$', DiscountDetailView.as_view(), name="discount_detail"), + + re_path(r'^paymentPlans$', PaymentPlanListCreateView.as_view(), name="payment_plan_list_create"), + re_path(r'^paymentPlans/(?P[0-9]+)$', PaymentPlanDetailView.as_view(), name="payment_plan_detail"), + + re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"), + re_path(r'^paymentModes/(?P[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail"), + + re_path(r'^competencies$', CompetencyListCreateView.as_view(), name="competency_list_create"), + re_path(r'^competencies/(?P[0-9]+)$', CompetencyDetailView.as_view(), name="competency_detail"), + + re_path(r'^establishmentCompetencies$', EstablishmentCompetencyListCreateView.as_view(), name="establishment_competency_list_create"), + re_path(r'^establishmentCompetencies/(?P[0-9]+)$', EstablishmentCompetencyDetailView.as_view(), name="establishment_competency_detail"), ] \ No newline at end of file diff --git a/Back-End/School/views.py b/Back-End/School/views.py index 7ba4984..b17a74a 100644 --- a/Back-End/School/views.py +++ b/Back-End/School/views.py @@ -3,63 +3,76 @@ from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect from django.utils.decorators import method_decorator from rest_framework.parsers import JSONParser from rest_framework.views import APIView -from django.core.cache import cache -from django.core.exceptions import ObjectDoesNotExist -from .models import Teacher, Speciality, SchoolClass, Planning -from .serializers import TeacherSerializer, SpecialitySerializer, SchoolClassSerializer, PlanningSerializer -from N3wtSchool import bdd +from rest_framework import status +from .models import ( + Teacher, + Speciality, + SchoolClass, + Planning, + Discount, + Fee, + PaymentPlan, + PaymentMode, + EstablishmentCompetency, + Competency +) +from .serializers import ( + TeacherSerializer, + SpecialitySerializer, + SchoolClassSerializer, + PlanningSerializer, + DiscountSerializer, + FeeSerializer, + PaymentPlanSerializer, + PaymentModeSerializer, + EstablishmentCompetencySerializer, + CompetencySerializer +) +from Common.models import Domain, Category +from N3wtSchool.bdd import delete_object, getAllObjects, getObject +from django.db.models import Q +from collections import defaultdict +from Subscriptions.models import Student, StudentCompetency +from Subscriptions.util import getCurrentSchoolYear +import logging + +logger = logging.getLogger(__name__) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') -class SpecialitiesView(APIView): +class SpecialityListCreateView(APIView): def get(self, request): - specialitiesList=bdd.getAllObjects(Speciality) - specialities_serializer=SpecialitySerializer(specialitiesList, many=True) + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + specialities_list = getAllObjects(Speciality) + if establishment_id: + specialities_list = specialities_list.filter(establishment__id=establishment_id).distinct() + specialities_serializer = SpecialitySerializer(specialities_list, many=True) return JsonResponse(specialities_serializer.data, safe=False) - def post(self, request): - specialities_data=JSONParser().parse(request) - all_valid = True - for speciality_data in specialities_data: - speciality_serializer = SpecialitySerializer(data=speciality_data) - - if speciality_serializer.is_valid(): - speciality_serializer.save() - else: - all_valid = False - break - if all_valid: - specialitiesList = bdd.getAllObjects(Speciality) - specialities_serializer = SpecialitySerializer(specialitiesList, many=True) - - return JsonResponse(specialities_serializer.data, safe=False) - - return JsonResponse(speciality_serializer.errors, safe=False) - - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class SpecialityView(APIView): - def get (self, request, _id): - speciality = bdd.getObject(_objectName=Speciality, _columnName='id', _value=_id) - speciality_serializer=SpecialitySerializer(speciality) - - return JsonResponse(speciality_serializer.data, safe=False) - def post(self, request): speciality_data=JSONParser().parse(request) speciality_serializer = SpecialitySerializer(data=speciality_data) if speciality_serializer.is_valid(): speciality_serializer.save() - return JsonResponse(speciality_serializer.data, safe=False) + return JsonResponse(speciality_serializer.data, safe=False, status=status.HTTP_201_CREATED) - return JsonResponse(speciality_serializer.errors, safe=False) + return JsonResponse(speciality_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) - def put(self, request, _id): +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class SpecialityDetailView(APIView): + def get(self, request, id): + speciality = getObject(_objectName=Speciality, _columnName='id', _value=id) + speciality_serializer=SpecialitySerializer(speciality) + return JsonResponse(speciality_serializer.data, safe=False) + + def put(self, request, id): speciality_data=JSONParser().parse(request) - speciality = bdd.getObject(_objectName=Speciality, _columnName='id', _value=_id) + speciality = getObject(_objectName=Speciality, _columnName='id', _value=id) speciality_serializer = SpecialitySerializer(speciality, data=speciality_data) if speciality_serializer.is_valid(): speciality_serializer.save() @@ -67,24 +80,22 @@ class SpecialityView(APIView): return JsonResponse(speciality_serializer.errors, safe=False) - def delete(self, request, _id): - return bdd.delete_object(Speciality, _id) - -class TeachersView(APIView): - def get(self, request): - teachersList=bdd.getAllObjects(Teacher) - teachers_serializer=TeacherSerializer(teachersList, many=True) - - return JsonResponse(teachers_serializer.data, safe=False) + def delete(self, request, id): + return delete_object(Speciality, id) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') -class TeacherView(APIView): - def get (self, request, _id): - teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id) - teacher_serializer=TeacherSerializer(teacher) +class TeacherListCreateView(APIView): + def get(self, request): + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) - return JsonResponse(teacher_serializer.data, safe=False) + teachers_list = getAllObjects(Teacher) + if teachers_list: + teachers_list = teachers_list.filter(profile_role__establishment_id=establishment_id) + teachers_serializer = TeacherSerializer(teachers_list, many=True) + return JsonResponse(teachers_serializer.data, safe=False) def post(self, request): teacher_data=JSONParser().parse(request) @@ -92,14 +103,23 @@ class TeacherView(APIView): if teacher_serializer.is_valid(): teacher_serializer.save() - - return JsonResponse(teacher_serializer.data, safe=False) + + return JsonResponse(teacher_serializer.data, safe=False) return JsonResponse(teacher_serializer.errors, safe=False) - def put(self, request, _id): +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class TeacherDetailView(APIView): + def get (self, request, id): + teacher = getObject(_objectName=Teacher, _columnName='id', _value=id) + teacher_serializer=TeacherSerializer(teacher) + + return JsonResponse(teacher_serializer.data, safe=False) + + def put(self, request, id): teacher_data=JSONParser().parse(request) - teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id) + teacher = getObject(_objectName=Teacher, _columnName='id', _value=id) teacher_serializer = TeacherSerializer(teacher, data=teacher_data) if teacher_serializer.is_valid(): teacher_serializer.save() @@ -107,59 +127,45 @@ class TeacherView(APIView): return JsonResponse(teacher_serializer.errors, safe=False) - def delete(self, request, _id): - return bdd.delete_object(Teacher, _id, related_field='associated_profile') + def delete(self, request, id): + return delete_object(Teacher, id, related_field='profile_role') @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') -class ClassesView(APIView): - def get(self, request): - classesList=bdd.getAllObjects(SchoolClass) - classes_serializer=SchoolClassSerializer(classesList, many=True) +class SchoolClassListCreateView(APIView): + def get(self, request): + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + school_classes_list = getAllObjects(SchoolClass) + if school_classes_list: + school_classes_list = school_classes_list.filter(establishment=establishment_id).distinct() + classes_serializer = SchoolClassSerializer(school_classes_list, many=True) return JsonResponse(classes_serializer.data, safe=False) - def post(self, request): - all_valid = True - classes_data=JSONParser().parse(request) - for classe_data in classes_data: - classe_serializer = SchoolClassSerializer(data=classe_data) - - if classe_serializer.is_valid(): - classe_serializer.save() - else: - all_valid = False - break - - if all_valid: - classesList = bdd.getAllObjects(SchoolClass) - classes_serializer = SchoolClassSerializer(classesList, many=True) - - return JsonResponse(classes_serializer.data, safe=False) - - return JsonResponse(classe_serializer.errors, safe=False) - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class ClasseView(APIView): - def get (self, request, _id): - schoolClass = bdd.getObject(_objectName=SchoolClass, _columnName='id', _value=_id) - classe_serializer=SchoolClassSerializer(schoolClass) - - return JsonResponse(classe_serializer.data, safe=False) - def post(self, request): classe_data=JSONParser().parse(request) classe_serializer = SchoolClassSerializer(data=classe_data) if classe_serializer.is_valid(): - classe_serializer.save() + classe_serializer.save() return JsonResponse(classe_serializer.data, safe=False) return JsonResponse(classe_serializer.errors, safe=False) - def put(self, request, _id): +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class SchoolClassDetailView(APIView): + def get (self, request, id): + schoolClass = getObject(_objectName=SchoolClass, _columnName='id', _value=id) + classe_serializer=SchoolClassSerializer(schoolClass) + + return JsonResponse(classe_serializer.data, safe=False) + + def put(self, request, id): classe_data=JSONParser().parse(request) - schoolClass = bdd.getObject(_objectName=SchoolClass, _columnName='id', _value=_id) + schoolClass = getObject(_objectName=SchoolClass, _columnName='id', _value=id) classe_serializer = SchoolClassSerializer(schoolClass, data=classe_data) if classe_serializer.is_valid(): classe_serializer.save() @@ -167,27 +173,17 @@ class ClasseView(APIView): return JsonResponse(classe_serializer.errors, safe=False) - def delete(self, request, _id): - return bdd.delete_object(SchoolClass, _id) - + def delete(self, request, id): + return delete_object(SchoolClass, id) @method_decorator(csrf_protect, name='dispatch') @method_decorator(ensure_csrf_cookie, name='dispatch') -class PlanningsView(APIView): +class PlanningListCreateView(APIView): def get(self, request): - schedulesList=bdd.getAllObjects(Planning) + schedulesList=getAllObjects(Planning) schedules_serializer=PlanningSerializer(schedulesList, many=True) return JsonResponse(schedules_serializer.data, safe=False) -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class PlanningView(APIView): - def get (self, request, _id): - planning = bdd.getObject(_objectName=Planning, _columnName='classe__id', _value=_id) - planning_serializer=PlanningSerializer(planning) - - return JsonResponse(planning_serializer.data, safe=False) - def post(self, request): planning_data=JSONParser().parse(request) planning_serializer = PlanningSerializer(data=planning_data) @@ -198,15 +194,24 @@ class PlanningView(APIView): return JsonResponse(planning_serializer.errors, safe=False) - def put(self, request, _id): +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class PlanningDetailView(APIView): + def get (self, request, id): + planning = getObject(_objectName=Planning, _columnName='classe_id', _value=id) + planning_serializer=PlanningSerializer(planning) + + return JsonResponse(planning_serializer.data, safe=False) + + def put(self, request, id): planning_data = JSONParser().parse(request) - + try: - planning = Planning.objects.get(id=_id) + planning = Planning.objects.get(id=id) except Planning.DoesNotExist: - return JsonResponse({'error': 'No object found'}, status=404) + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) except Planning.MultipleObjectsReturned: - return JsonResponse({'error': 'Multiple objects found'}, status=400) + return JsonResponse({'error': 'Multiple objects found'}, status=status.HTTP_400_BAD_REQUEST) planning_serializer = PlanningSerializer(planning, data=planning_data) @@ -215,3 +220,484 @@ class PlanningView(APIView): return JsonResponse(planning_serializer.data, safe=False) return JsonResponse(planning_serializer.errors, safe=False) + + def delete(self, request, id): + return delete_object(Planning, id) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class FeeListCreateView(APIView): + def get(self, request, *args, **kwargs): + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + filter = request.GET.get('filter', '').strip() + fee_type_value = 0 if filter == 'registration' else 1 + + fees = Fee.objects.filter(type=fee_type_value, establishment_id=establishment_id).distinct() + fee_serializer = FeeSerializer(fees, many=True) + + return JsonResponse(fee_serializer.data, safe=False, status=status.HTTP_200_OK) + + def post(self, request): + fee_data = JSONParser().parse(request) + fee_serializer = FeeSerializer(data=fee_data) + if fee_serializer.is_valid(): + fee_serializer.save() + return JsonResponse(fee_serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(fee_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class FeeDetailView(APIView): + def get(self, request, id): + try: + fee = Fee.objects.get(id=id) + fee_serializer = FeeSerializer(fee) + return JsonResponse(fee_serializer.data, safe=False) + except Fee.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + fee_data = JSONParser().parse(request) + try: + fee = Fee.objects.get(id=id) + except Fee.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + fee_serializer = FeeSerializer(fee, data=fee_data, partial=True) + if fee_serializer.is_valid(): + fee_serializer.save() + return JsonResponse(fee_serializer.data, safe=False) + return JsonResponse(fee_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + return delete_object(Fee, id) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DiscountListCreateView(APIView): + def get(self, request, *args, **kwargs): + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + filter = request.GET.get('filter', '').strip() + discount_type_value = 0 if filter == 'registration' else 1 + + discounts = Discount.objects.filter(type=discount_type_value, establishment_id=establishment_id).distinct() + discounts_serializer = DiscountSerializer(discounts, many=True) + + return JsonResponse(discounts_serializer.data, safe=False, status=status.HTTP_200_OK) + + def post(self, request): + discount_data = JSONParser().parse(request) + discount_serializer = DiscountSerializer(data=discount_data) + if discount_serializer.is_valid(): + discount_serializer.save() + return JsonResponse(discount_serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(discount_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class DiscountDetailView(APIView): + def get(self, request, id): + try: + discount = Discount.objects.get(id=id) + discount_serializer = DiscountSerializer(discount) + return JsonResponse(discount_serializer.data, safe=False) + except Discount.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + discount_data = JSONParser().parse(request) + try: + discount = Discount.objects.get(id=id) + except Discount.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + discount_serializer = DiscountSerializer(discount, data=discount_data, partial=True) # Utilisation de partial=True + if discount_serializer.is_valid(): + discount_serializer.save() + return JsonResponse(discount_serializer.data, safe=False) + return JsonResponse(discount_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + return delete_object(Discount, id) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class PaymentPlanListCreateView(APIView): + def get(self, request, *args, **kwargs): + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + filter = request.GET.get('filter', '').strip() + type_value = 0 if filter == 'registration' else 1 + + payment_plans = PaymentPlan.objects.filter(type=type_value, establishment_id=establishment_id).distinct() + payment_plans_serializer = PaymentPlanSerializer(payment_plans, many=True) + + return JsonResponse(payment_plans_serializer.data, safe=False, status=status.HTTP_200_OK) + + def post(self, request): + payment_plan_data = JSONParser().parse(request) + payment_plan_serializer = PaymentPlanSerializer(data=payment_plan_data) + if payment_plan_serializer.is_valid(): + payment_plan_serializer.save() + return JsonResponse(payment_plan_serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(payment_plan_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class PaymentPlanDetailView(APIView): + def get(self, request, id): + try: + payment_plan = PaymentPlan.objects.get(id=id) + payment_plan_serializer = PaymentPlanSerializer(payment_plan) + return JsonResponse(payment_plan_serializer.data, safe=False) + except PaymentPlan.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + payment_plan_data = JSONParser().parse(request) + try: + payment_plan = PaymentPlan.objects.get(id=id) + except PaymentPlan.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + payment_plan_serializer = PaymentPlanSerializer(payment_plan, data=payment_plan_data, partial=True) + if payment_plan_serializer.is_valid(): + payment_plan_serializer.save() + return JsonResponse(payment_plan_serializer.data, safe=False) + return JsonResponse(payment_plan_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + return delete_object(PaymentPlan, id) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class PaymentModeListCreateView(APIView): + def get(self, request): + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + filter = request.GET.get('filter', '').strip() + type_value = 0 if filter == 'registration' else 1 + + payment_modes = PaymentMode.objects.filter(type=type_value, establishment_id=establishment_id).distinct() + payment_modes_serializer = PaymentModeSerializer(payment_modes, many=True) + + return JsonResponse(payment_modes_serializer.data, safe=False, status=status.HTTP_200_OK) + + def post(self, request): + payment_mode_data = JSONParser().parse(request) + payment_mode_serializer = PaymentModeSerializer(data=payment_mode_data) + if payment_mode_serializer.is_valid(): + payment_mode_serializer.save() + return JsonResponse(payment_mode_serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(payment_mode_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class PaymentModeDetailView(APIView): + def get(self, request, id): + try: + payment_mode = PaymentMode.objects.get(id=id) + payment_mode_serializer = PaymentModeSerializer(payment_mode) + return JsonResponse(payment_mode_serializer.data, safe=False) + except PaymentMode.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + payment_mode_data = JSONParser().parse(request) + try: + payment_mode = PaymentMode.objects.get(id=id) + except PaymentMode.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + payment_mode_serializer = PaymentModeSerializer(payment_mode, data=payment_mode_data, partial=True) + if payment_mode_serializer.is_valid(): + payment_mode_serializer.save() + return JsonResponse(payment_mode_serializer.data, safe=False) + return JsonResponse(payment_mode_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + return delete_object(PaymentMode, id) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CompetencyListCreateView(APIView): + def get(self, request): + cycle = request.GET.get('cycle') + if cycle is None: + return JsonResponse({'error': 'cycle est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + competencies_list = getAllObjects(Competency) + competencies_list = competencies_list.filter( + category__domain__cycle=cycle + ).distinct() + serializer = CompetencySerializer(competencies_list, many=True) + return JsonResponse(serializer.data, safe=False) + + def post(self, request): + data = JSONParser().parse(request) + serializer = CompetencySerializer(data=data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class CompetencyDetailView(APIView): + def get(self, request, id): + try: + competency = Competency.objects.get(id=id) + serializer = CompetencySerializer(competency) + return JsonResponse(serializer.data, safe=False) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + competency = Competency.objects.get(id=id) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = CompetencySerializer(competency, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + competency = Competency.objects.get(id=id) + competency.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except Competency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentCompetencyListCreateView(APIView): + def get(self, request): + establishment_id = request.GET.get('establishment_id') + cycle = request.GET.get('cycle') + if not establishment_id or not cycle: + return JsonResponse({'error': 'establishment_id et cycle sont requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + # Toutes les compétences du cycle + competencies = Competency.objects.filter(category__domain__cycle=cycle).select_related('category', 'category__domain') + + # Récupérer les EstablishmentCompetency pour l'établissement et le cycle + ec_qs = EstablishmentCompetency.objects.filter( + Q(competency__in=competencies) | Q(competency__isnull=True), + establishment_id=establishment_id + ).select_related('competency', 'custom_category') + + # Required = compétences de référence requises + required_ids = set(ec_qs.filter(is_required=True, competency__isnull=False).values_list('competency_id', flat=True)) + + # Custom = compétences custom (pas de lien vers Competency) + custom_ecs = ec_qs.filter(is_required=False, competency__isnull=True) + + # Organisation par domaine > catégorie + result = [] + selected_count = 0 + domaines = Domain.objects.filter(cycle=cycle) + for domaine in domaines: + domaine_dict = { + "domaine_id": domaine.id, + "domaine_nom": domaine.name, + "categories": [] + } + categories = domaine.categories.all() + for categorie in categories: + categorie_dict = { + "categorie_id": categorie.id, + "categorie_nom": categorie.name, + "competences": [] + } + + # Liste des noms de compétences custom pour cette catégorie + custom_for_cat = custom_ecs.filter(custom_category=categorie) + custom_names = set(ec.custom_name.strip().lower() for ec in custom_for_cat if ec.custom_name) + + # Compétences de référence (on saute celles qui sont déjà en custom) + competences = categorie.competencies.all() + for comp in competences: + if comp.name.strip().lower() in custom_names: + continue # On n'affiche pas la compétence de référence si une custom du même nom existe + if comp.id in required_ids: + state = "required" + selected_count += 1 + else: + state = "none" + categorie_dict["competences"].append({ + "competence_id": comp.id, + "nom": comp.name, + "state": state + }) + + # Ajout des compétences custom + for ec in custom_for_cat: + categorie_dict["competences"].append({ + "competence_id": ec.id, + "nom": ec.custom_name, + "state": "custom" + }) + selected_count += 1 + + domaine_dict["categories"].append(categorie_dict) + result.append(domaine_dict) + + return JsonResponse({ + "selected_count": selected_count, + "data": result + }, safe=False) + + def post(self, request): + """ + Crée une ou plusieurs compétences custom pour un établissement (is_required=False) + Attendu dans le body : + [ + { "establishment_id": ..., "category_id": ..., "nom": ... }, + ... + ] + """ + data = JSONParser().parse(request) + # Si data est un dict (un seul objet), on le met dans une liste + if isinstance(data, dict): + data = [data] + + created = [] + errors = [] + + for item in data: + establishment_id = item.get("establishment_id") + category_id = item.get("category_id") + nom = item.get("nom") + + if not establishment_id or not category_id or not nom: + errors.append({"error": "establishment_id, category_id et nom sont requis", "item": item}) + continue + + try: + category = Category.objects.get(id=category_id) + ec_exists = EstablishmentCompetency.objects.filter( + establishment_id=establishment_id, + competency__isnull=True, + custom_name=nom, + ).exists() + if ec_exists: + errors.append({"error": "Une compétence custom de ce nom existe déjà pour cet établissement", "item": item}) + continue + + ec = EstablishmentCompetency.objects.create( + establishment_id=establishment_id, + competency=None, + custom_name=nom, + custom_category=category, + is_required=False + ) + + # Récupérer l'établissement et sa fréquence d'évaluation + establishment = ec.establishment + evaluation_frequency = establishment.evaluation_frequency # 1=Trimestre, 2=Semestre, 3=Année + + # Déterminer l'année scolaire courante + school_year = getCurrentSchoolYear() + + # Générer les périodes selon la fréquence + periods = [] + if evaluation_frequency == 1: # Trimestre + periods = [f"T{i+1}_{school_year}" for i in range(3)] + elif evaluation_frequency == 2: # Semestre + periods = [f"S{i+1}_{school_year}" for i in range(2)] + elif evaluation_frequency == 3: # Année + periods = [f"A_{school_year}"] + + # Associer à tous les élèves de l'établissement pour chaque période + students = Student.objects.filter(associated_class__establishment_id=establishment_id) + for student in students: + for period in periods: + StudentCompetency.objects.get_or_create( + student=student, + establishment_competency=ec, + period=period + ) + + created.append({ + "competence_id": ec.id, + "nom": ec.custom_name, + "state": "custom" + }) + except Exception as e: + errors.append({"error": str(e), "item": item}) + + status_code = status.HTTP_201_CREATED if created else status.HTTP_400_BAD_REQUEST + return JsonResponse({ + "created": created, + "errors": errors + }, status=status_code, safe=False) + + def delete(self, request): + """ + Supprime une ou plusieurs compétences custom (EstablishmentCompetency) à partir d'une liste d'IDs. + Supprime aussi les StudentCompetency associés. + Attendu dans le body : + { + "ids": [1, 2, 3, ...] + } + """ + data = JSONParser().parse(request) + ids = data.get("ids", []) + deleted = [] + errors = [] + + for ec_id in ids: + try: + ec = EstablishmentCompetency.objects.get(id=ec_id) + # Supprimer les StudentCompetency associés + StudentCompetency.objects.filter(establishment_competency=ec).delete() + ec.delete() + deleted.append(ec_id) + except EstablishmentCompetency.DoesNotExist: + errors.append({"id": ec_id, "error": "No object found"}) + + return JsonResponse({ + "deleted": deleted, + "errors": errors + }, safe=False) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentCompetencyDetailView(APIView): + def get(self, request, id): + try: + ec = EstablishmentCompetency.objects.get(id=id) + serializer = EstablishmentCompetencySerializer(ec) + return JsonResponse(serializer.data, safe=False) + except EstablishmentCompetency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + ec = EstablishmentCompetency.objects.get(id=id) + except EstablishmentCompetency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) + data = JSONParser().parse(request) + serializer = EstablishmentCompetencySerializer(ec, data=data, partial=True) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, id): + try: + ec = EstablishmentCompetency.objects.get(id=id) + ec.delete() + return JsonResponse({'message': 'Deleted'}, safe=False) + except EstablishmentCompetency.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND) diff --git a/Back-End/Settings/__init__.py b/Back-End/Settings/__init__.py new file mode 100644 index 0000000..6ddb826 --- /dev/null +++ b/Back-End/Settings/__init__.py @@ -0,0 +1 @@ +default_app_config = 'Settings.apps.SettingsConfig' diff --git a/Back-End/Settings/admin.py b/Back-End/Settings/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/Back-End/Settings/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/Back-End/Settings/apps.py b/Back-End/Settings/apps.py new file mode 100644 index 0000000..32f1bfd --- /dev/null +++ b/Back-End/Settings/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + +class SettingsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'Settings' diff --git a/Back-End/Settings/migrations/0001_initial.py b/Back-End/Settings/migrations/0001_initial.py new file mode 100644 index 0000000..c05340d --- /dev/null +++ b/Back-End/Settings/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('Establishment', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='SMTPSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('smtp_server', models.CharField(max_length=255)), + ('smtp_port', models.PositiveIntegerField()), + ('smtp_user', models.CharField(max_length=255)), + ('smtp_password', models.CharField(max_length=255)), + ('use_tls', models.BooleanField(default=True)), + ('use_ssl', models.BooleanField(default=False)), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Establishment.establishment')), + ], + ), + ] diff --git a/Back-End/Settings/migrations/__init__.py b/Back-End/Settings/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/Settings/models.py b/Back-End/Settings/models.py new file mode 100644 index 0000000..9f3a999 --- /dev/null +++ b/Back-End/Settings/models.py @@ -0,0 +1,17 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models +from django.utils.translation import gettext_lazy as _ +from django.conf import settings +from Establishment.models import Establishment + +class SMTPSettings(models.Model): + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE) + smtp_server = models.CharField(max_length=255) + smtp_port = models.PositiveIntegerField() + smtp_user = models.CharField(max_length=255) + smtp_password = models.CharField(max_length=255) + use_tls = models.BooleanField(default=True) + use_ssl = models.BooleanField(default=False) + + def __str__(self): + return f"SMTP Settings ({self.smtp_server}:{self.smtp_port})" \ No newline at end of file diff --git a/Back-End/Settings/serializers.py b/Back-End/Settings/serializers.py new file mode 100644 index 0000000..380e5e9 --- /dev/null +++ b/Back-End/Settings/serializers.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from .models import SMTPSettings + +class SMTPSettingsSerializer(serializers.ModelSerializer): + class Meta: + model = SMTPSettings + fields = '__all__' \ No newline at end of file diff --git a/Back-End/Settings/urls.py b/Back-End/Settings/urls.py new file mode 100644 index 0000000..a8842b8 --- /dev/null +++ b/Back-End/Settings/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from .views import SMTPSettingsView + +urlpatterns = [ + path('smtp-settings/', SMTPSettingsView.as_view(), name='smtp_settings'), +] \ No newline at end of file diff --git a/Back-End/Settings/views.py b/Back-End/Settings/views.py new file mode 100644 index 0000000..c13c356 --- /dev/null +++ b/Back-End/Settings/views.py @@ -0,0 +1,81 @@ +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from .models import SMTPSettings +from .serializers import SMTPSettingsSerializer +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status + +class SMTPSettingsView(APIView): + """ + API pour gérer les paramètres SMTP. + """ + + @swagger_auto_schema( + operation_description="Récupérer les paramètres SMTP pour un établissement spécifique ou tous les paramètres si aucun ID n'est fourni", + manual_parameters=[ + openapi.Parameter( + 'establishment_id', + openapi.IN_QUERY, + description="ID de l'établissement (facultatif)", + type=openapi.TYPE_INTEGER, + required=False + ) + ], + responses={ + 200: SMTPSettingsSerializer(many=True), + 404: openapi.Response(description="Aucun paramètre SMTP trouvé."), + 500: openapi.Response(description="Erreur interne du serveur."), + }, + ) + def get(self, request): + establishment_id = request.query_params.get('establishment_id') + + try: + if establishment_id: + # Récupérer les paramètres SMTP pour un établissement spécifique + smtp_settings = SMTPSettings.objects.filter(establishment_id=establishment_id).first() + if not smtp_settings: + return Response( + {'error': f"Aucun paramètre SMTP trouvé pour l'établissement {establishment_id}."}, + status=status.HTTP_404_NOT_FOUND + ) + serializer = SMTPSettingsSerializer(smtp_settings) + return Response(serializer.data, status=status.HTTP_200_OK) + else: + # Récupérer tous les paramètres SMTP + smtp_settings = SMTPSettings.objects.all() + if not smtp_settings.exists(): + return Response( + {'error': "Aucun paramètre SMTP trouvé."}, + status=status.HTTP_404_NOT_FOUND + ) + serializer = SMTPSettingsSerializer(smtp_settings, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + @swagger_auto_schema( + operation_description="Créer ou mettre à jour les paramètres SMTP pour un établissement spécifique", + request_body=SMTPSettingsSerializer, + responses={ + 200: SMTPSettingsSerializer(), + 400: openapi.Response(description="Données invalides."), + 500: openapi.Response(description="Erreur interne du serveur."), + }, + ) + def post(self, request): + data = request.data + try: + smtp_settings = SMTPSettings.objects.first() + if smtp_settings: + serializer = SMTPSettingsSerializer(smtp_settings, data=data) + else: + serializer = SMTPSettingsSerializer(data=data) + + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) \ No newline at end of file diff --git a/Back-End/Subscriptions/Configuration/application.default.json b/Back-End/Subscriptions/Configuration/application.default.json deleted file mode 100644 index 9d2cefb..0000000 --- a/Back-End/Subscriptions/Configuration/application.default.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "mailFrom":"", - "password":"" -} \ No newline at end of file diff --git a/Back-End/Subscriptions/Configuration/automate.json b/Back-End/Subscriptions/Configuration/automate.json index 0249aa3..1dc13ad 100644 --- a/Back-End/Subscriptions/Configuration/automate.json +++ b/Back-End/Subscriptions/Configuration/automate.json @@ -1,63 +1,84 @@ { "states": [ - "ABSENT", - "CREE", - "ENVOYE", - "EN_VALIDATION", - "A_RELANCER", - "VALIDE", - "ARCHIVE" + "IDLE", + "INITIALIZED", + "SENT", + "UNDER_REVIEW", + "TO_BE_FOLLOWED_UP", + "VALIDATED", + "ARCHIVED", + "SEPA_SENT" ], "transitions": [ { - "name": "creationDI", - "from": "ABSENT", - "to": "CREE" + "name": "EVENT_INIT", + "from": "IDLE", + "to": "INITIALIZED" }, { - "name": "envoiDI", - "from": "CREE", - "to": "ENVOYE" + "name": "EVENT_SEND", + "from": "INITIALIZED", + "to": "SENT" }, { - "name": "archiveDI", - "from": "CREE", - "to": "ARCHIVE" + "name": "EVENT_ARCHIVE", + "from": "INITIALIZED", + "to": "ARCHIVED" }, { - "name": "saisiDI", - "from": "ENVOYE", - "to": "EN_VALIDATION" + "name": "EVENT_SIGNATURE", + "from": "SENT", + "to": "UNDER_REVIEW" }, { - "name": "relanceDI", - "from": "ENVOYE", - "to": "A_RELANCER" + "name": "EVENT_WAITING_FOR_SEPA", + "from": "SENT", + "to": "SEPA_TO_SEND" }, { - "name": "archiveDI", - "from": "A_RELANCER", - "to": "ARCHIVE" + "name": "EVENT_SEND_SEPA", + "from": "SEPA_TO_SEND", + "to": "SEPA_SENT" }, { - "name": "archiveDI", - "from": "ENVOYE", - "to": "ARCHIVE" + "name": "EVENT_FOLLOW_UP", + "from": "SENT", + "to": "TO_BE_FOLLOWED_UP" }, { - "name": "valideDI", - "from": "EN_VALIDATION", - "to": "VALIDE" + "name": "EVENT_ARCHIVE", + "from": "SENT", + "to": "ARCHIVED" }, { - "name": "archiveDI", - "from": "EN_VALIDATION", - "to": "ARCHIVE" + "name": "EVENT_ARCHIVE", + "from": "TO_BE_FOLLOWED_UP", + "to": "ARCHIVED" }, { - "name": "archiveDI", - "from": "VALIDE", - "to": "ARCHIVE" + "name": "EVENT_REFUSE", + "from": "UNDER_REVIEW", + "to": "SENT" + }, + { + "name": "EVENT_VALIDATE", + "from": "UNDER_REVIEW", + "to": "VALIDATED" + }, + { + "name": "EVENT_ARCHIVE", + "from": "UNDER_REVIEW", + "to": "ARCHIVED" + }, + { + "name": "EVENT_SIGNATURE_SEPA", + "from": "SEPA_SENT", + "to": "UNDER_REVIEW" + }, + { + "name": "EVENT_ARCHIVE", + "from": "VALIDATED", + "to": "ARCHIVED" } ] } diff --git a/Back-End/Subscriptions/apps.py b/Back-End/Subscriptions/apps.py index 503b621..3ab723f 100644 --- a/Back-End/Subscriptions/apps.py +++ b/Back-End/Subscriptions/apps.py @@ -5,6 +5,3 @@ class GestioninscriptionsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'Subscriptions' - def ready(self): - from Subscriptions.signals import clear_cache - clear_cache() diff --git a/Back-End/Subscriptions/automate.py b/Back-End/Subscriptions/automate.py index 3c457cb..1c28ad9 100644 --- a/Back-End/Subscriptions/automate.py +++ b/Back-End/Subscriptions/automate.py @@ -1,16 +1,17 @@ # state_machine.py import json from Subscriptions.models import RegistrationForm -from Subscriptions.signals import clear_cache state_mapping = { - "ABSENT": RegistrationForm.RegistrationFormStatus.RF_ABSENT, - "CREE": RegistrationForm.RegistrationFormStatus.RF_CREATED, - "ENVOYE": RegistrationForm.RegistrationFormStatus.RF_SENT, - "EN_VALIDATION": RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW, - "A_RELANCER": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP, - "VALIDE": RegistrationForm.RegistrationFormStatus.RF_VALIDATED, - "ARCHIVE": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED + "IDLE": RegistrationForm.RegistrationFormStatus.RF_IDLE, + "INITIALIZED": RegistrationForm.RegistrationFormStatus.RF_INITIALIZED, + "SENT": RegistrationForm.RegistrationFormStatus.RF_SENT, + "UNDER_REVIEW": RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW, + "TO_BE_FOLLOWED_UP": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP, + "VALIDATED": RegistrationForm.RegistrationFormStatus.RF_VALIDATED, + "ARCHIVED": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED, + "SEPA_SENT": RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT, + "SEPA_TO_SEND": RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND } def load_config(config_file): @@ -27,11 +28,9 @@ def getStateMachineObjectState(etat): def updateStateMachine(rf, transition) : automateModel = load_config('Subscriptions/Configuration/automate.json') state_machine = getStateMachineObject(rf.status) - print(f'etat DI : {state_machine.state}') if state_machine.trigger(transition, automateModel): rf.status = state_machine.state rf.save() - clear_cache() class Automate_RF_Register: def __init__(self, initial_state): diff --git a/Back-End/Subscriptions/mailManager.py b/Back-End/Subscriptions/mailManager.py deleted file mode 100644 index 02d3826..0000000 --- a/Back-End/Subscriptions/mailManager.py +++ /dev/null @@ -1,74 +0,0 @@ -from django.core.mail import send_mail -import re -from N3wtSchool import settings - -def envoieReinitMotDePasse(recipients, code): - send_mail( - settings.EMAIL_REINIT_SUBJECT, - settings.EMAIL_REINIT_CORPUS%(str(code)), - settings.EMAIL_HOST_USER, - [recipients], - fail_silently=False, - ) - -def sendRegisterForm(recipients): - errorMessage = '' - try: - print(f'{settings.EMAIL_HOST_USER}') - send_mail( - settings.EMAIL_INSCRIPTION_SUBJECT, - settings.EMAIL_INSCRIPTION_CORPUS%[recipients], - settings.EMAIL_HOST_USER, - [recipients], - fail_silently=False, - ) - except Exception as e: - errorMessage = str(e) - - return errorMessage - -def envoieRelanceDossierInscription(recipients, code): - errorMessage = '' - try: - send_mail( - settings.EMAIL_RELANCE_SUBJECT, - settings.EMAIL_RELANCE_CORPUS%str(code), - settings.EMAIL_HOST_USER, - [recipients], - fail_silently=False, - ) - except Exception as e: - errorMessage = str(e) - - return errorMessage - - -def envoieSEPA(recipients, ref): - send_mail( - settings.EMAIL_SEPA_SUBJECT%str(ref), - settings.EMAIL_SEPA_CORPUS, - settings.EMAIL_HOST_USER, - [recipients], - fail_silently=False, - ) - -def isValid(message, fiche_inscription): - # Est-ce que la référence du dossier est VALIDE - subject = message.subject - print ("++++ " + subject) - responsableMail = message.from_header - result = re.search('<(.*)>', responsableMail) - - if result: - responsableMail = result.group(1) - - result = re.search(r'.*\[Ref(.*)\].*', subject) - idMail = -1 - if result: - idMail = result.group(1).strip() - - eleve = fiche_inscription.eleve - responsable = eleve.getMainGuardian() - mailReponsableAVerifier = responsable.mail - - return responsableMail == mailReponsableAVerifier and str(idMail) == str(fiche_inscription.eleve.id) \ No newline at end of file diff --git a/Back-End/Subscriptions/migrations/0001_initial.py b/Back-End/Subscriptions/migrations/0001_initial.py new file mode 100644 index 0000000..afd9c5c --- /dev/null +++ b/Back-End/Subscriptions/migrations/0001_initial.py @@ -0,0 +1,213 @@ +# Generated by Django 5.1.3 on 2025-05-28 11:14 + +import Subscriptions.models +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('Auth', '__first__'), + ('Common', '0001_initial'), + ('Establishment', '0001_initial'), + ('School', '__first__'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Language', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('label', models.CharField(default='', max_length=200)), + ], + ), + migrations.CreateModel( + name='Student', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('photo', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_photo_upload_to)), + ('last_name', models.CharField(default='', max_length=200)), + ('first_name', models.CharField(default='', max_length=200)), + ('gender', models.IntegerField(blank=True, choices=[(0, 'Sélection du genre'), (1, 'Garçon'), (2, 'Fille')], default=0)), + ('nationality', models.CharField(blank=True, default='', max_length=200)), + ('address', models.CharField(blank=True, default='', max_length=200)), + ('birth_date', models.DateField(blank=True, null=True)), + ('birth_place', models.CharField(blank=True, default='', max_length=200)), + ('birth_postal_code', models.IntegerField(blank=True, default=0)), + ('attending_physician', models.CharField(blank=True, default='', max_length=200)), + ('associated_class', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='School.schoolclass')), + ], + ), + migrations.CreateModel( + name='RegistrationSchoolFileTemplate', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False)), + ('slug', models.CharField(default='', max_length=255)), + ('name', models.CharField(default='', max_length=255)), + ('file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_school_file_upload_to)), + ], + ), + migrations.CreateModel( + name='Sibling', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('last_name', models.CharField(blank=True, max_length=200, null=True)), + ('first_name', models.CharField(blank=True, max_length=200, null=True)), + ('birth_date', models.DateField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='Guardian', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('last_name', models.CharField(blank=True, max_length=200, null=True)), + ('first_name', models.CharField(blank=True, max_length=200, null=True)), + ('birth_date', models.DateField(blank=True, null=True)), + ('address', models.CharField(blank=True, default='', max_length=200)), + ('phone', models.CharField(blank=True, default='', max_length=200)), + ('profession', models.CharField(blank=True, default='', max_length=200)), + ('profile_role', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='guardian_profile', to='Auth.profilerole')), + ], + ), + migrations.CreateModel( + name='RegistrationFileGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(default='', max_length=255)), + ('description', models.TextField(blank=True, null=True)), + ('establishment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='file_group', to='Establishment.establishment')), + ], + ), + migrations.CreateModel( + name='RegistrationForm', + fields=[ + ('student', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='Subscriptions.student')), + ('status', models.IntegerField(choices=[(0, "Pas de dossier d'inscription"), (1, "Dossier d'inscription initialisé"), (2, "Dossier d'inscription envoyé"), (3, "Dossier d'inscription en cours de validation"), (4, "Dossier d'inscription à relancer"), (5, "Dossier d'inscription validé"), (6, "Dossier d'inscription archivé"), (7, 'Mandat SEPA envoyé'), (8, 'Mandat SEPA à envoyer')], default=0)), + ('last_update', models.DateTimeField(auto_now=True)), + ('school_year', models.CharField(blank=True, default='', max_length=9)), + ('notes', models.CharField(blank=True, max_length=200)), + ('registration_link_code', models.CharField(blank=True, default='', max_length=200)), + ('registration_file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_file_path)), + ('sepa_file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_file_path)), + ('fusion_file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_file_path)), + ('associated_rf', models.CharField(blank=True, default='', max_length=200)), + ('discounts', models.ManyToManyField(blank=True, related_name='register_forms', to='School.discount')), + ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='register_forms', to='Establishment.establishment')), + ('fees', models.ManyToManyField(blank=True, related_name='register_forms', to='School.fee')), + ('fileGroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='register_forms', to='Subscriptions.registrationfilegroup')), + ('registration_payment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='registration_payment_modes_forms', to='School.paymentmode')), + ('registration_payment_plan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='registration_payment_plans_forms', to='School.paymentplan')), + ('tuition_payment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tuition_payment_modes_forms', to='School.paymentmode')), + ('tuition_payment_plan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tuition_payment_plans_forms', to='School.paymentplan')), + ], + ), + migrations.AddField( + model_name='student', + name='guardians', + field=models.ManyToManyField(blank=True, to='Subscriptions.guardian'), + ), + migrations.AddField( + model_name='student', + name='level', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='Common.level'), + ), + migrations.AddField( + model_name='student', + name='profiles', + field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='student', + name='spoken_languages', + field=models.ManyToManyField(blank=True, to='Subscriptions.language'), + ), + migrations.CreateModel( + name='BilanCompetence', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_bilan_form_upload_to)), + ('period', models.CharField(help_text='Période ex: T1-2024_2025, S1-2024_2025, A-2024_2025', max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bilans', to='Subscriptions.student')), + ], + ), + migrations.CreateModel( + name='AbsenceManagement', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('day', models.DateField(blank=True, null=True)), + ('moment', models.IntegerField(choices=[(1, 'Morning'), (2, 'Afternoon'), (3, 'Total')], default=3)), + ('reason', models.IntegerField(choices=[(1, 'Justified Absence'), (2, 'Unjustified Absence'), (3, 'Justified Late'), (4, 'Unjustified Late')], default=2)), + ('commentaire', models.TextField(blank=True, null=True)), + ('establishment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='absences', to='Establishment.establishment')), + ('student', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='absences', to='Subscriptions.student')), + ], + ), + migrations.CreateModel( + name='RegistrationParentFileMaster', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(default='', max_length=255)), + ('description', models.CharField(blank=True, null=True)), + ('is_required', models.BooleanField(default=False)), + ('groups', models.ManyToManyField(blank=True, related_name='parent_file_masters', to='Subscriptions.registrationfilegroup')), + ], + ), + migrations.CreateModel( + name='RegistrationSchoolFileMaster', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False)), + ('name', models.CharField(default='', max_length=255)), + ('is_required', models.BooleanField(default=False)), + ('groups', models.ManyToManyField(blank=True, related_name='school_file_masters', to='Subscriptions.registrationfilegroup')), + ], + ), + migrations.AddField( + model_name='student', + name='registration_files', + field=models.ManyToManyField(blank=True, related_name='students', to='Subscriptions.registrationschoolfiletemplate'), + ), + migrations.AddField( + model_name='registrationschoolfiletemplate', + name='master', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='school_file_templates', to='Subscriptions.registrationschoolfilemaster'), + ), + migrations.AddField( + model_name='student', + name='siblings', + field=models.ManyToManyField(blank=True, to='Subscriptions.sibling'), + ), + migrations.AddField( + model_name='registrationschoolfiletemplate', + name='registration_form', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='school_file_templates', to='Subscriptions.registrationform'), + ), + migrations.CreateModel( + name='RegistrationParentFileTemplate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_parent_file_upload_to)), + ('master', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_file_templates', to='Subscriptions.registrationparentfilemaster')), + ('registration_form', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_file_templates', to='Subscriptions.registrationform')), + ], + ), + migrations.CreateModel( + name='StudentCompetency', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('score', models.IntegerField(blank=True, null=True)), + ('comment', models.TextField(blank=True, null=True)), + ('period', models.CharField(blank=True, default='', help_text="Période d'évaluation ex: T1-2024_2025, S1-2024_2025, A-2024_2025", max_length=20)), + ('establishment_competency', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_scores', to='School.establishmentcompetency')), + ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='competency_scores', to='Subscriptions.student')), + ], + options={ + 'unique_together': {('student', 'establishment_competency', 'period')}, + }, + ), + ] diff --git a/Back-End/Subscriptions/migrations/0002_alter_registrationparentfilemaster_description.py b/Back-End/Subscriptions/migrations/0002_alter_registrationparentfilemaster_description.py new file mode 100644 index 0000000..82d5627 --- /dev/null +++ b/Back-End/Subscriptions/migrations/0002_alter_registrationparentfilemaster_description.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2025-05-30 07:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('Subscriptions', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='registrationparentfilemaster', + name='description', + field=models.CharField(blank=True, max_length=500, null=True), + ), + ] diff --git a/Back-End/Subscriptions/migrations/__init__.py b/Back-End/Subscriptions/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index fc79a26..4334c88 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -3,30 +3,17 @@ from django.utils.timezone import now from django.conf import settings from django.utils.translation import gettext_lazy as _ -from Auth.models import Profile -from School.models import SchoolClass - from datetime import datetime -class RegistrationFee(models.Model): - class PaymentOptions(models.IntegerChoices): - SINGLE_PAYMENT = 0, _('Paiement en une seule fois') - MONTHLY_PAYMENT = 1, _('Paiement mensuel') - QUARTERLY_PAYMENT = 2, _('Paiement trimestriel') +import os +import logging - name = models.CharField(max_length=255, unique=True) - description = models.TextField(blank=True) - base_amount = models.DecimalField(max_digits=10, decimal_places=2) - discounts = models.JSONField(blank=True, null=True) - supplements = models.JSONField(blank=True, null=True) - validity_start_date = models.DateField() - validity_end_date = models.DateField() - payment_option = models.IntegerField(choices=PaymentOptions, default=PaymentOptions.SINGLE_PAYMENT) - - def __str__(self): - return self.name +logger = logging.getLogger("SubscriptionModels") class Language(models.Model): + """ + Représente une langue parlée par l’élève. + """ id = models.AutoField(primary_key=True) label = models.CharField(max_length=200, default="") @@ -34,29 +21,66 @@ class Language(models.Model): return "LANGUAGE" class Guardian(models.Model): - last_name = models.CharField(max_length=200, default="") - first_name = models.CharField(max_length=200, default="") - birth_date = models.CharField(max_length=200, default="", blank=True) + """ + Représente un responsable légal (parent/tuteur) d’un élève. + """ + last_name = models.CharField(max_length=200, null=True, blank=True) + first_name = models.CharField(max_length=200, null=True, blank=True) + birth_date = models.DateField(null=True, blank=True) address = models.CharField(max_length=200, default="", blank=True) - email = models.CharField(max_length=200, default="", blank=True) phone = models.CharField(max_length=200, default="", blank=True) profession = models.CharField(max_length=200, default="", blank=True) - associated_profile = models.ForeignKey(Profile, on_delete=models.CASCADE) + profile_role = models.OneToOneField('Auth.ProfileRole', on_delete=models.CASCADE, related_name='guardian_profile', null=True, blank=True) + + @property + def email(self): + """ + Retourne l'email du profil associé via le ProfileRole. + """ + if self.profile_role and self.profile_role.profile: + return self.profile_role.profile.email + return None def __str__(self): return self.last_name + "_" + self.first_name class Sibling(models.Model): - id = models.AutoField(primary_key=True) - last_name = models.CharField(max_length=200, default="") - first_name = models.CharField(max_length=200, default="") - birth_date = models.CharField(max_length=200, default="", blank=True) + """ + Représente un frère ou une sœur d’un élève. + """ + last_name = models.CharField(max_length=200, null=True, blank=True) + first_name = models.CharField(max_length=200, null=True, blank=True) + birth_date = models.DateField(null=True, blank=True) def __str__(self): return "SIBLING" -class Student(models.Model): +def registration_photo_upload_to(instance, filename): + return f"registration_files/dossier_rf_{instance.pk}/parent/{filename}" +def registration_bilan_form_upload_to(instance, filename): + # On récupère le RegistrationForm lié à l'élève + register_form = getattr(instance.student, 'registrationform', None) + if register_form: + pk = register_form.pk + else: + # fallback sur l'id de l'élève si pas de registrationform + pk = instance.student.pk + return f"registration_files/dossier_rf_{pk}/bilan/{filename}" + +class BilanCompetence(models.Model): + student = models.ForeignKey('Subscriptions.Student', on_delete=models.CASCADE, related_name='bilans') + file = models.FileField(upload_to=registration_bilan_form_upload_to, null=True, blank=True) + period = models.CharField(max_length=20, help_text="Période ex: T1-2024_2025, S1-2024_2025, A-2024_2025") + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"{self.student} - {self.period}" + +class Student(models.Model): + """ + Représente l’élève inscrit ou en cours d’inscription. + """ class StudentGender(models.IntegerChoices): NONE = 0, _('Sélection du genre') MALE = 1, _('Garçon') @@ -68,60 +92,94 @@ class Student(models.Model): PS = 2, _('PS - Petite Section') MS = 3, _('MS - Moyenne Section') GS = 4, _('GS - Grande Section') + CP = 5, _('CP') + CE1 = 6, _('CE1') + CE2 = 7, _('CE2') + CM1 = 8, _('CM1') + CM2 = 9, _('CM2') - class PaymentMethod(models.IntegerChoices): - NONE = 0, _('Sélection du mode de paiement') - SEPA_DIRECT_DEBIT = 1, _('Prélèvement SEPA') - CHECK = 2, _('Chèques') - + photo = models.FileField( + upload_to=registration_photo_upload_to, + null=True, + blank=True + ) last_name = models.CharField(max_length=200, default="") first_name = models.CharField(max_length=200, default="") gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True) - level = models.IntegerField(choices=StudentLevel, default=StudentLevel.NONE, blank=True) + level = models.ForeignKey('Common.Level', on_delete=models.SET_NULL, null=True, blank=True, related_name='students') nationality = models.CharField(max_length=200, default="", blank=True) address = models.CharField(max_length=200, default="", blank=True) birth_date = models.DateField(null=True, blank=True) birth_place = models.CharField(max_length=200, default="", blank=True) birth_postal_code = models.IntegerField(default=0, blank=True) attending_physician = models.CharField(max_length=200, default="", blank=True) - payment_method = models.IntegerField(choices=PaymentMethod, default=PaymentMethod.NONE, blank=True) # Many-to-Many Relationship - profiles = models.ManyToManyField(Profile, blank=True) + profiles = models.ManyToManyField('Auth.Profile', blank=True) # Many-to-Many Relationship - guardians = models.ManyToManyField(Guardian, blank=True) + guardians = models.ManyToManyField('Subscriptions.Guardian', blank=True) # Many-to-Many Relationship - siblings = models.ManyToManyField(Sibling, blank=True) + siblings = models.ManyToManyField('Subscriptions.Sibling', blank=True) # Many-to-Many Relationship - spoken_languages = models.ManyToManyField(Language, blank=True) + registration_files = models.ManyToManyField('Subscriptions.RegistrationSchoolFileTemplate', blank=True, related_name='students') + + # Many-to-Many Relationship + spoken_languages = models.ManyToManyField('Subscriptions.Language', blank=True) # One-to-Many Relationship - associated_class = models.ForeignKey(SchoolClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='students') + associated_class = models.ForeignKey('School.SchoolClass', on_delete=models.SET_NULL, null=True, blank=True, related_name='students') def __str__(self): return self.last_name + "_" + self.first_name def getSpokenLanguages(self): + """ + Retourne la liste des langues parlées par l’élève. + """ return self.spoken_languages.all() def getMainGuardian(self): + """ + Retourne le responsable légal principal de l’élève. + """ return self.guardians.all()[0] def getGuardians(self): + """ + Retourne tous les responsables légaux de l’élève. + """ return self.guardians.all() def getProfiles(self): + """ + Retourne les profils utilisateurs liés à l’élève. + """ return self.profiles.all() def getSiblings(self): + """ + Retourne les frères et sœurs de l’élève. + """ return self.siblings.all() def getNumberOfSiblings(self): + """ + Retourne le nombre de frères et sœurs. + """ return self.siblings.count() + def get_photo_url(self): + """ + Retourne le chemin correct de la photo pour le template HTML. + Si la photo n'existe pas, retourne le chemin de l'image par défaut. + """ + if self.photo and hasattr(self.photo, 'url'): + # Retourne l'URL complète de la photo + return self.photo.url + @property def age(self): if self.birth_date: @@ -147,33 +205,231 @@ class Student(models.Model): return self.birth_date.strftime('%d-%m-%Y') return None -class RegistrationForm(models.Model): +class RegistrationFileGroup(models.Model): + name = models.CharField(max_length=255, default="") + description = models.TextField(blank=True, null=True) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='file_group', null=True, blank=True) + def __str__(self): + return self.name + + def __str__(self): + return f'{self.group.name} - {self.id}' + +def registration_file_path(instance, filename): + # Génère le chemin : registration_files/dossier_rf_{student_id}/filename + return f'registration_files/dossier_rf_{instance.student_id}/{filename}' + +class RegistrationForm(models.Model): class RegistrationFormStatus(models.IntegerChoices): - RF_ABSENT = 0, _('Pas de dossier d\'inscription') - RF_CREATED = 1, _('Dossier d\'inscription créé') + RF_IDLE = 0, _('Pas de dossier d\'inscription') + RF_INITIALIZED = 1, _('Dossier d\'inscription initialisé') RF_SENT = 2, _('Dossier d\'inscription envoyé') RF_UNDER_REVIEW = 3, _('Dossier d\'inscription en cours de validation') RF_TO_BE_FOLLOWED_UP = 4, _('Dossier d\'inscription à relancer') RF_VALIDATED = 5, _('Dossier d\'inscription validé') RF_ARCHIVED = 6, _('Dossier d\'inscription archivé') + RF_SEPA_SENT = 7, _('Mandat SEPA envoyé') + RF_SEPA_TO_SEND = 8, _('Mandat SEPA à envoyer') # One-to-One Relationship student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True) - status = models.IntegerField(choices=RegistrationFormStatus, default=RegistrationFormStatus.RF_ABSENT) + status = models.IntegerField(choices=RegistrationFormStatus, default=RegistrationFormStatus.RF_IDLE) last_update = models.DateTimeField(auto_now=True) + school_year = models.CharField(max_length=9, default="", blank=True) notes = models.CharField(max_length=200, blank=True) registration_link_code = models.CharField(max_length=200, default="", blank=True) - registration_file = models.FileField(upload_to=settings.DOCUMENT_DIR, default="", blank=True) + registration_file = models.FileField( + upload_to=registration_file_path, + null=True, + blank=True + ) + sepa_file = models.FileField( + upload_to=registration_file_path, + null=True, + blank=True + ) + fusion_file = models.FileField( + upload_to=registration_file_path, + null=True, + blank=True + ) associated_rf = models.CharField(max_length=200, default="", blank=True) + # Many-to-Many Relationship + fees = models.ManyToManyField('School.Fee', blank=True, related_name='register_forms') + + # Many-to-Many Relationship + discounts = models.ManyToManyField('School.Discount', blank=True, related_name='register_forms') + fileGroup = models.ForeignKey(RegistrationFileGroup, + on_delete=models.SET_NULL, + related_name='register_forms', + null=True, + blank=True) + + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='register_forms') + registration_payment = models.ForeignKey('School.PaymentMode', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_modes_forms') + tuition_payment = models.ForeignKey('School.PaymentMode', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_modes_forms') + registration_payment_plan = models.ForeignKey('School.PaymentPlan', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_plans_forms') + tuition_payment_plan = models.ForeignKey('School.PaymentPlan', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_plans_forms') + def __str__(self): return "RF_" + self.student.last_name + "_" + self.student.first_name -class RegistrationFile(models.Model): - name = models.CharField(max_length=255) - file = models.FileField(upload_to='registration_files/') - date_added = models.DateTimeField(auto_now_add=True) + def save(self, *args, **kwargs): + # Vérifier si un fichier existant doit être remplacé + if self.pk: # Si l'objet existe déjà dans la base de données + try: + old_instance = RegistrationForm.objects.get(pk=self.pk) + if old_instance.sepa_file and old_instance.sepa_file != self.sepa_file: + # Supprimer l'ancien fichier + old_instance.sepa_file.delete(save=False) + except RegistrationForm.DoesNotExist: + pass # L'objet n'existe pas encore, rien à supprimer + + # Appeler la méthode save originale + super().save(*args, **kwargs) + +############################################################# +####################### MASTER FILES ######################## +############################################################# + +####### DocuSeal masters (documents école, à signer ou pas) ####### +class RegistrationSchoolFileMaster(models.Model): + groups = models.ManyToManyField(RegistrationFileGroup, related_name='school_file_masters', blank=True) + id = models.IntegerField(primary_key=True) + name = models.CharField(max_length=255, default="") + is_required = models.BooleanField(default=False) + + def __str__(self): + return f'{self.group.name} - {self.id}' + +####### Parent files masters (documents à fournir par les parents) ####### +class RegistrationParentFileMaster(models.Model): + groups = models.ManyToManyField(RegistrationFileGroup, related_name='parent_file_masters', blank=True) + name = models.CharField(max_length=255, default="") + description = models.CharField(max_length=500, blank=True, null=True) + is_required = models.BooleanField(default=False) + +############################################################ +####################### CLONE FILES ######################## +############################################################ + +def registration_school_file_upload_to(instance, filename): + return f"registration_files/dossier_rf_{instance.registration_form.pk}/school/{filename}" + +def registration_parent_file_upload_to(instance, filename): + return f"registration_files/dossier_rf_{instance.registration_form.pk}/parent/{filename}" + +####### DocuSeal templates (par dossier d'inscription) ####### +class RegistrationSchoolFileTemplate(models.Model): + master = models.ForeignKey(RegistrationSchoolFileMaster, on_delete=models.CASCADE, related_name='school_file_templates', blank=True) + id = models.IntegerField(primary_key=True) + slug = models.CharField(max_length=255, default="") + name = models.CharField(max_length=255, default="") + registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='school_file_templates', blank=True) + file = models.FileField(null=True,blank=True, upload_to=registration_school_file_upload_to) def __str__(self): return self.name + + @staticmethod + def get_files_from_rf(register_form_id): + """ + Récupère tous les fichiers liés à un dossier d’inscription donné. + """ + registration_files = RegistrationSchoolFileTemplate.objects.filter(registration_form=register_form_id) + filenames = [] + for reg_file in registration_files: + filenames.append(reg_file.file.path) + return filenames + +class StudentCompetency(models.Model): + student = models.ForeignKey('Subscriptions.Student', on_delete=models.CASCADE, related_name='competency_scores') + establishment_competency = models.ForeignKey('School.EstablishmentCompetency', on_delete=models.CASCADE, related_name='student_scores') + score = models.IntegerField(null=True, blank=True) + comment = models.TextField(blank=True, null=True) + period = models.CharField( + max_length=20, + help_text="Période d'évaluation ex: T1-2024_2025, S1-2024_2025, A-2024_2025", + default="", + blank=True + ) + + class Meta: + unique_together = ('student', 'establishment_competency', 'period') + + indexes = [ + models.Index(fields=['student', 'establishment_competency', 'period']), + ] + + def __str__(self): + return f"{self.student} - {self.establishment_competency} - Score: {self.score} - Period: {self.period}" + +####### Parent files templates (par dossier d'inscription) ####### +class RegistrationParentFileTemplate(models.Model): + master = models.ForeignKey(RegistrationParentFileMaster, on_delete=models.CASCADE, related_name='parent_file_templates', blank=True) + registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='parent_file_templates', blank=True) + file = models.FileField(null=True,blank=True, upload_to=registration_parent_file_upload_to) + + def __str__(self): + return self.name + + def save(self, *args, **kwargs): + if self.pk: # Si l'objet existe déjà dans la base de données + try: + old_instance = RegistrationParentFileTemplate.objects.get(pk=self.pk) + if old_instance.file and (not self.file or self.file.name == ''): + if os.path.exists(old_instance.file.path): + old_instance.file.delete(save=False) + self.file = None + else: + print(f"Le fichier {old_instance.file.path} n'existe pas.") + except RegistrationParentFileTemplate.DoesNotExist: + print("Ancienne instance introuvable.") + super().save(*args, **kwargs) + + @staticmethod + def get_files_from_rf(register_form_id): + """ + Récupère tous les fichiers liés à un dossier d’inscription donné. + """ + registration_files = RegistrationParentFileTemplate.objects.filter(registration_form=register_form_id) + filenames = [] + for reg_file in registration_files: + if reg_file.file and hasattr(reg_file.file, 'path'): + filenames.append(reg_file.file.path) + return filenames + +class AbsenceMoment(models.IntegerChoices): + MORNING = 1, 'Morning' + AFTERNOON = 2, 'Afternoon' + TOTAL = 3, 'Total' + +class AbsenceReason(models.IntegerChoices): + JUSTIFIED_ABSENCE = 1, 'Justified Absence' + UNJUSTIFIED_ABSENCE = 2, 'Unjustified Absence' + JUSTIFIED_LATE = 3, 'Justified Late' + UNJUSTIFIED_LATE = 4, 'Unjustified Late' + +class AbsenceManagement(models.Model): + day = models.DateField(blank=True, null=True) + moment = models.IntegerField( + choices=AbsenceMoment.choices, + default=AbsenceMoment.TOTAL + ) + reason = models.IntegerField( + choices=AbsenceReason.choices, + default=AbsenceReason.UNJUSTIFIED_ABSENCE + ) + student = models.ForeignKey( + Student, + on_delete=models.CASCADE, + related_name='absences', + blank=True, null=True + ) + establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='absences', blank=True, null=True) + commentaire = models.TextField(blank=True, null=True) + + def __str__(self): + return f"{self.student} - {self.day} - {self.get_moment_display()} - {self.get_reason_display()}" \ No newline at end of file diff --git a/Back-End/Subscriptions/pagination.py b/Back-End/Subscriptions/pagination.py index f2a97dc..bb52e88 100644 --- a/Back-End/Subscriptions/pagination.py +++ b/Back-End/Subscriptions/pagination.py @@ -2,10 +2,10 @@ from rest_framework.pagination import PageNumberPagination from N3wtSchool import settings -class CustomPagination(PageNumberPagination): +class CustomSubscriptionPagination(PageNumberPagination): page_size_query_param = 'page_size' max_page_size = settings.NB_MAX_PAGE - page_size = settings.NB_RESULT_PER_PAGE + page_size = settings.NB_RESULT_SUBSCRIPTIONS_PER_PAGE def get_paginated_response(self, data): return ({ diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py index 2490962..e3aeabd 100644 --- a/Back-End/Subscriptions/serializers.py +++ b/Back-End/Subscriptions/serializers.py @@ -1,26 +1,118 @@ from rest_framework import serializers -from .models import RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationFee -from School.models import SchoolClass -from Auth.models import Profile -from Auth.serializers import ProfileSerializer -from GestionMessagerie.models import Messagerie +from .models import ( + RegistrationFileGroup, + RegistrationForm, + Student, + Guardian, + Sibling, + Language, + RegistrationSchoolFileMaster, + RegistrationSchoolFileTemplate, + RegistrationParentFileMaster, + RegistrationParentFileTemplate, + AbsenceManagement, + BilanCompetence +) +from School.models import SchoolClass, Fee, Discount, FeeType +from Auth.models import ProfileRole, Profile +from Auth.serializers import ProfileSerializer, ProfileRoleSerializer from GestionNotification.models import Notification from N3wtSchool import settings from django.utils import timezone import pytz -from datetime import datetime +import Subscriptions.util as util + +class AbsenceManagementSerializer(serializers.ModelSerializer): + student_name = serializers.SerializerMethodField() -class RegistrationFileSerializer(serializers.ModelSerializer): class Meta: - model = RegistrationFile + model = AbsenceManagement fields = '__all__' + # Ajoute les nouveaux champs au retour JSON + extra_fields = ['student_name'] -class RegistrationFeeSerializer(serializers.ModelSerializer): + def get_student_name(self, obj): + if obj.student: + return f"{obj.student.first_name} {obj.student.last_name}" + return None + +class RegistrationSchoolFileMasterSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) class Meta: - model = RegistrationFee + model = RegistrationSchoolFileMaster fields = '__all__' +class RegistrationParentFileMasterSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(required=False) + class Meta: + model = RegistrationParentFileMaster + fields = '__all__' + +class RegistrationSchoolFileTemplateSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(required=False) + file_url = serializers.SerializerMethodField() + + class Meta: + model = RegistrationSchoolFileTemplate + fields = '__all__' + + def get_file_url(self, obj): + # Retourne l'URL complète du fichier si disponible + return obj.file.url if obj.file else None + +class RegistrationParentFileTemplateSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(required=False) + file_url = serializers.SerializerMethodField() + master_name = serializers.CharField(source='master.name', read_only=True) + master_description = serializers.CharField(source='master.description', read_only=True) + is_required = serializers.BooleanField(source='master.is_required', read_only=True) + + class Meta: + model = RegistrationParentFileTemplate + fields = '__all__' + + def get_file_url(self, obj): + # Retourne l'URL complète du fichier si disponible + return obj.file.url if obj.file else None + +class GuardianSimpleSerializer(serializers.ModelSerializer): + associated_profile_email = serializers.SerializerMethodField() + + class Meta: + model = Guardian + fields = ['id', 'associated_profile_email'] + + def get_associated_profile_email(self, obj): + if obj.profile_role and obj.profile_role.profile: + return obj.profile_role.profile.email + return None + +class RegistrationFormSimpleSerializer(serializers.ModelSerializer): + guardians = GuardianSimpleSerializer(many=True, source='student.guardians') + last_name = serializers.SerializerMethodField() + first_name = serializers.SerializerMethodField() + + class Meta: + model = RegistrationForm + fields = ['student_id', 'last_name', 'first_name', 'guardians'] + + def get_last_name(self, obj): + return obj.student.last_name + + def get_first_name(self, obj): + return obj.student.first_name + +class RegistrationFileGroupSerializer(serializers.ModelSerializer): + registration_forms = serializers.SerializerMethodField() + + class Meta: + model = RegistrationFileGroup + fields = '__all__' + + def get_registration_forms(self, obj): + forms = RegistrationForm.objects.filter(fileGroup=obj) + return RegistrationFormSimpleSerializer(forms, many=True).data + class LanguageSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) class Meta: @@ -35,7 +127,8 @@ class SiblingSerializer(serializers.ModelSerializer): class GuardianSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) - associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True) + profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False) + profile_role_data = ProfileRoleSerializer(write_only=True, required=False) associated_profile_email = serializers.SerializerMethodField() class Meta: @@ -43,14 +136,15 @@ class GuardianSerializer(serializers.ModelSerializer): fields = '__all__' def get_associated_profile_email(self, obj): - return obj.associated_profile.email - + if obj.profile_role and obj.profile_role.profile: + return obj.profile_role.profile.email + return None class StudentSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) guardians = GuardianSerializer(many=True, required=False) siblings = SiblingSerializer(many=True, required=False) - languages = LanguageSerializer(many=True, required=False) + spoken_languages = LanguageSerializer(many=True, required=False) associated_class_id = serializers.PrimaryKeyRelatedField(queryset=SchoolClass.objects.all(), source='associated_class', required=False, write_only=False, read_only=False) age = serializers.SerializerMethodField() formatted_birth_date = serializers.SerializerMethodField() @@ -61,47 +155,141 @@ class StudentSerializer(serializers.ModelSerializer): model = Student fields = '__all__' - def get_or_create_packages(self, guardians_data): + def create_or_update_guardians(self, guardians_data): guardians_ids = [] for guardian_data in guardians_data: - guardian_instance, created = Guardian.objects.get_or_create( - id=guardian_data.get('id'), - defaults=guardian_data - ) - guardians_ids.append(guardian_instance.id) + guardian_id = guardian_data.get('id', None) + profile_role_data = guardian_data.pop('profile_role_data', None) + profile_role = guardian_data.pop('profile_role', None) + + if guardian_id: + # Si un ID est fourni, récupérer ou mettre à jour le Guardian existant + guardian_instance, created = Guardian.objects.update_or_create( + id=guardian_id, + defaults=guardian_data + ) + guardians_ids.append(guardian_instance.id) + continue + + if profile_role_data: + # Vérifiez si 'profile_data' est fourni pour créer un nouveau profil + profile_data = profile_role_data.pop('profile_data', None) + if profile_data: + # Créer un nouveau profil + profile_serializer = ProfileSerializer(data=profile_data) + profile_serializer.is_valid(raise_exception=True) + profile = profile_serializer.save() + profile.set_password(profile_data['password']) + profile.save() + profile_role_data['profile'] = profile.id # Associer le profil créé + + # Vérifiez si 'profile' est un objet ou une clé primaire + if isinstance(profile_role_data.get('profile'), Profile): + profile_role_data['profile'] = profile_role_data['profile'].id + + establishment_id = profile_role_data.pop('establishment').id + profile_role_data['establishment'] = establishment_id + + # Vérifiez si un ProfileRole existe déjà pour ce profile et cet établissement + existing_profile_role = ProfileRole.objects.filter( + profile_id=profile_role_data['profile'], + establishment=profile_role_data['establishment'], + role_type=profile_role_data['role_type'] + ).first() + + if existing_profile_role: + # Mettre à jour le ProfileRole existant + profile_role_serializer = ProfileRoleSerializer(existing_profile_role, data=profile_role_data) + profile_role_serializer.is_valid(raise_exception=True) + profile_role = profile_role_serializer.save() + else: + # Créer un nouveau ProfileRole si aucun n'existe + profile_role_serializer = ProfileRoleSerializer(data=profile_role_data) + profile_role_serializer.is_valid(raise_exception=True) + profile_role = profile_role_serializer.save() + elif profile_role: + # Récupérer un ProfileRole existant par son ID + profile_role = ProfileRole.objects.get(id=profile_role.id) + + if profile_role: + guardian_data['profile_role'] = profile_role + + # Vérifiez si un Guardian existe déjà pour ce ProfileRole + existing_guardian = Guardian.objects.filter(profile_role=profile_role).first() + if existing_guardian: + # Mettre à jour le Guardian existant + for key, value in guardian_data.items(): + setattr(existing_guardian, key, value) + existing_guardian.save() + guardians_ids.append(existing_guardian.id) + else: + # Créer un nouveau Guardian + guardian_instance = Guardian.objects.create(**guardian_data) + guardians_ids.append(guardian_instance.id) + return guardians_ids + def create_or_update_siblings(self, siblings_data, student_instance): + """ + Crée ou met à jour les frères et sœurs associés à un étudiant. + Supprime les frères et sœurs qui ne sont plus présents dans siblings_data. + """ + + # Si siblings_data est vide, supprimer tous les frères et sœurs associés + if not siblings_data: + student_instance.siblings.clear() # Supprime toutes les relations + return [] + + # Récupérer les IDs des frères et sœurs existants + existing_sibling_ids = set(student_instance.siblings.values_list('id', flat=True)) + + # Créer ou mettre à jour les frères et sœurs + updated_sibling_ids = [] + for sibling_data in siblings_data: + sibling_instance, created = Sibling.objects.update_or_create( + id=sibling_data.get('id'), + defaults=sibling_data + ) + updated_sibling_ids.append(sibling_instance.id) + + # Supprimer les frères et sœurs qui ne sont plus dans siblings_data + siblings_to_delete = existing_sibling_ids - set(updated_sibling_ids) + Sibling.objects.filter(id__in=siblings_to_delete).delete() + return updated_sibling_ids + + def create_or_update_languages(self, languages_data): + languages_ids = [] + for language_data in languages_data: + language_instance, created = Language.objects.update_or_create( + id=language_data.get('id'), + defaults=language_data + ) + languages_ids.append(language_instance.id) + return languages_ids + def create(self, validated_data): guardians_data = validated_data.pop('guardians', []) siblings_data = validated_data.pop('siblings', []) languages_data = validated_data.pop('spoken_languages', []) student = Student.objects.create(**validated_data) - student.guardians.set(self.get_or_create_packages(guardians_data)) - student.siblings.set(self.get_or_create_packages(siblings_data)) - student.spoken_languages.set(self.get_or_create_packages(languages_data)) + student.guardians.set(self.create_or_update_guardians(guardians_data)) + student.siblings.set(self.create_or_update_siblings(siblings_data, student)) + student.spoken_languages.set(self.create_or_update_languages(languages_data)) return student - def create_or_update_packages(self, guardians_data): - guardians_ids = [] - for guardian_data in guardians_data: - guardian_instance, created = Guardian.objects.update_or_create( - id=guardian_data.get('id'), - defaults=guardian_data - ) - guardians_ids.append(guardian_instance.id) - return guardians_ids - def update(self, instance, validated_data): guardians_data = validated_data.pop('guardians', []) siblings_data = validated_data.pop('siblings', []) languages_data = validated_data.pop('spoken_languages', []) if guardians_data: - instance.guardians.set(self.create_or_update_packages(guardians_data)) - if siblings_data: - instance.siblings.set(self.create_or_update_packages(siblings_data)) + instance.guardians.set(self.create_or_update_guardians(guardians_data)) + + sibling_ids = self.create_or_update_siblings(siblings_data, instance) + instance.siblings.set(sibling_ids) + if languages_data: - instance.spoken_languages.set(self.create_or_update_packages(languages_data)) + instance.spoken_languages.set(self.create_or_update_languages(languages_data)) for field in self.fields: try: @@ -119,13 +307,19 @@ class StudentSerializer(serializers.ModelSerializer): return obj.formatted_birth_date def get_associated_class_name(self, obj): - return obj.associated_class.atmosphereName if obj.associated_class else None + return obj.associated_class.atmosphere_name if obj.associated_class else None class RegistrationFormSerializer(serializers.ModelSerializer): student = StudentSerializer(many=False, required=False) registration_file = serializers.FileField(required=False) + sepa_file = serializers.FileField(required=False) status_label = serializers.SerializerMethodField() formatted_last_update = serializers.SerializerMethodField() + registration_files = RegistrationSchoolFileTemplateSerializer(many=True, required=False) + fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False) + discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False) + totalRegistrationFees = serializers.SerializerMethodField() + totalTuitionFees = serializers.SerializerMethodField() class Meta: model = RegistrationForm @@ -134,11 +328,19 @@ class RegistrationFormSerializer(serializers.ModelSerializer): def create(self, validated_data): student_data = validated_data.pop('student') student = StudentSerializer.create(StudentSerializer(), student_data) + fees_data = validated_data.pop('fees', []) + discounts_data = validated_data.pop('discounts', []) registrationForm = RegistrationForm.objects.create(student=student, **validated_data) + + # Associer les IDs des objets Fee et Discount au RegistrationForm + registrationForm.fees.set([fee.id for fee in fees_data]) + registrationForm.discounts.set([discount.id for discount in discounts_data]) return registrationForm def update(self, instance, validated_data): student_data = validated_data.pop('student', None) + fees_data = validated_data.pop('fees', None) + discounts_data = validated_data.pop('discounts', None) if student_data: student = instance.student StudentSerializer.update(StudentSerializer(), student, student_data) @@ -148,8 +350,17 @@ class RegistrationFormSerializer(serializers.ModelSerializer): setattr(instance, field, validated_data[field]) except KeyError: pass + + instance.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') instance.save() + # Associer les IDs des objets Fee et Discount au RegistrationForm + if fees_data is not None: + instance.fees.set([fee.id for fee in fees_data]) + + if discounts_data is not None: + instance.discounts.set([discount.id for discount in discounts_data]) + return instance def get_status_label(self, obj): @@ -162,24 +373,34 @@ class RegistrationFormSerializer(serializers.ModelSerializer): return local_time.strftime("%d-%m-%Y %H:%M") + def get_totalRegistrationFees(self, obj): + return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE)) + + def get_totalTuitionFees(self, obj): + return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.TUITION_FEE)) + class StudentByParentSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) + associated_class_name = serializers.SerializerMethodField() class Meta: model = Student - fields = ['id', 'last_name', 'first_name'] + fields = ['id', 'last_name', 'first_name', 'level', 'photo', 'associated_class_name'] def __init__(self, *args, **kwargs): super(StudentByParentSerializer, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].required = False + def get_associated_class_name(self, obj): + return obj.associated_class.atmosphere_name if obj.associated_class else None + class RegistrationFormByParentSerializer(serializers.ModelSerializer): student = StudentByParentSerializer(many=False, required=True) class Meta: model = RegistrationForm - fields = ['student', 'status'] + fields = ['student', 'status', 'sepa_file'] def __init__(self, *args, **kwargs): super(RegistrationFormByParentSerializer, self).__init__(*args, **kwargs) @@ -188,27 +409,50 @@ class RegistrationFormByParentSerializer(serializers.ModelSerializer): class GuardianByDICreationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) + associated_profile_email = serializers.SerializerMethodField() + profile = serializers.SerializerMethodField() class Meta: model = Guardian - fields = ['id', 'last_name', 'first_name', 'email', 'associated_profile'] + fields = ['id', 'last_name', 'first_name', 'associated_profile_email', 'phone', 'profile_role', 'profile'] + + def get_associated_profile_email(self, obj): + if obj.profile_role and obj.profile_role.profile: + return obj.profile_role.profile.email + return None + + def get_profile(self, obj): + if obj.profile_role and obj.profile_role.profile: + return obj.profile_role.profile.id # Retourne l'ID du profil associé + return None + +class BilanCompetenceSerializer(serializers.ModelSerializer): + class Meta: + model = BilanCompetence + fields = ['id', 'file', 'period', 'created_at'] class StudentByRFCreationSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) guardians = GuardianByDICreationSerializer(many=True, required=False) + associated_class_name = serializers.SerializerMethodField() + bilans = BilanCompetenceSerializer(many=True, read_only=True) class Meta: model = Student - fields = ['id', 'last_name', 'first_name', 'guardians'] + fields = ['id', 'last_name', 'first_name', 'guardians', 'level', 'associated_class_name', 'photo', 'bilans'] def __init__(self, *args, **kwargs): super(StudentByRFCreationSerializer, self).__init__(*args, **kwargs) for field in self.fields: self.fields[field].required = False + def get_associated_class_name(self, obj): + return obj.associated_class.atmosphere_name if obj.associated_class else None + class NotificationSerializer(serializers.ModelSerializer): notification_type_label = serializers.ReadOnlyField() class Meta: model = Notification fields = '__all__' + diff --git a/Back-End/Subscriptions/signals.py b/Back-End/Subscriptions/signals.py index 0ce2f11..1da56dc 100644 --- a/Back-End/Subscriptions/signals.py +++ b/Back-End/Subscriptions/signals.py @@ -1,6 +1,5 @@ from django.db.models.signals import post_save, post_delete, m2m_changed from django.dispatch import receiver -from django.core.cache import cache from .models import RegistrationForm, Student, Guardian from Auth.models import Profile from N3wtSchool import settings @@ -8,23 +7,6 @@ from N3wtSchool.redis_client import redis_client import logging logger = logging.getLogger(__name__) -def clear_cache(): - # Préfixes des clés à supprimer - prefixes = ['N3WT_'] - - for prefix in prefixes: - # Utiliser le motif pour obtenir les clés correspondant au préfixe - pattern = f'*{prefix}*' - logger.debug(f'pattern : {pattern}') - for key in redis_client.scan_iter(pattern): - redis_client.delete(key) - logger.debug(f'deleting : {key}') - -@receiver(post_save, sender=RegistrationForm) -@receiver(post_delete, sender=RegistrationForm) -def clear_cache_after_change(sender, instance, **kwargs): - clear_cache() - @receiver(m2m_changed, sender=Student.guardians.through) def check_orphan_reponsables(sender, **kwargs): action = kwargs.pop('action', None) diff --git a/Back-End/Subscriptions/tasks.py b/Back-End/Subscriptions/tasks.py index e89d2b7..cae3930 100644 --- a/Back-End/Subscriptions/tasks.py +++ b/Back-End/Subscriptions/tasks.py @@ -21,10 +21,10 @@ def check_for_signature_deadlines(): send_notification(dossier) def send_notification(dossier): - logger.debug(f'Dossier en attente.... {dossier} - Positionnement à l\'état A_RELANCER') + logger.debug(f'Dossier en attente.... {dossier} - Positionnement à l\'état TO_BE_FOLLOWED_UP') # Changer l'état de l'automate - updateStateMachine(dossier, 'relanceDI') + updateStateMachine(dossier, 'EVENT_FOLLOW_UP') url = settings.URL_DJANGO + 'GestionMessagerie/message' diff --git a/Back-End/Subscriptions/templates/emails/inscription.html b/Back-End/Subscriptions/templates/emails/inscription.html new file mode 100644 index 0000000..8c54573 --- /dev/null +++ b/Back-End/Subscriptions/templates/emails/inscription.html @@ -0,0 +1,53 @@ + + + + + Confirmation d'inscription + + + +
+
+ +

Confirmation d'inscription

+
+
+

Bonjour,

+

Nous vous confirmons la réception de votre demande d'inscription, vous trouverez ci-joint le lien vers la page d'authentification : {{BASE_URL}}/users/login

+

S'il s'agit de votre première connexion, veuillez procéder à l'activation de votre compte à cette url : {{BASE_URL}}/users/subscribe

+

votre identifiant est : {{ email }}

+

Merci de compléter votre dossier d'inscription en suivant les instructions fournies.

+

Cordialement,

+

L'équipe N3wt School

+
+ +
+ + \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/emails/resetPassword.html b/Back-End/Subscriptions/templates/emails/resetPassword.html new file mode 100644 index 0000000..6646ddf --- /dev/null +++ b/Back-End/Subscriptions/templates/emails/resetPassword.html @@ -0,0 +1,49 @@ + + + + + Réinitialisation du Mot de passe + + + +
+
+

Réinitialisation du Mot de passe

+
+
+

Bonjour,

+

Vous trouverez ci-joint le lien pour réinitialiser votre mot de passe : {{BASE_URL}}/users/password/reset?uuid={{code}}

+

Cordialement,

+

L'équipe N3wt School

+
+ +
+ + \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/emails/sepa.html b/Back-End/Subscriptions/templates/emails/sepa.html new file mode 100644 index 0000000..556e185 --- /dev/null +++ b/Back-End/Subscriptions/templates/emails/sepa.html @@ -0,0 +1,51 @@ + + + + + Finalisation de l'inscription + + + +
+
+

Finalisation de l'inscription

+
+
+

Bonjour,

+

Un mandat de prélèvement SEPA vous a été envoyé

+

Le document est à votre disposition sur votre espace parent : {{BASE_URL}}/users/login

+

Merci de compléter puis de signer le document afin de valider votre inscription

+

Cordialement,

+

L'équipe N3wt School

+
+ +
+ + \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/emails/subscribeDirector.html b/Back-End/Subscriptions/templates/emails/subscribeDirector.html new file mode 100644 index 0000000..69764b8 --- /dev/null +++ b/Back-End/Subscriptions/templates/emails/subscribeDirector.html @@ -0,0 +1,59 @@ + + + + + Confirmation de souscription + + + +
+
+ +

Confirmation de souscription

+
+
+

Bonjour,

+

Nous sommes ravis de vous compter parmi les utilisateurs de N3wt School et vous remercions pour votre confiance

+

Vous trouverez ci-joint le lien vers la page d'authentification : {{BASE_URL}}/users/login

+

S'il s'agit de votre première connexion, veuillez procéder à l'activation de votre compte à cette url : {{BASE_URL}}/users/subscribe

+

votre identifiant est : {{ email }}

+

Notre équipe est à votre disposition pour vous aider à tirer pleinement parti des fonctionnalités offertes par Newt School.

+

N'hésitez pas à nous contacter pour toute question ou besoin d'assistance.

+

Cordialement,

+

L'équipe N3wt School

+
+ +
+ + \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/pdfs/bilan_competences.html b/Back-End/Subscriptions/templates/pdfs/bilan_competences.html new file mode 100644 index 0000000..529c8c4 --- /dev/null +++ b/Back-End/Subscriptions/templates/pdfs/bilan_competences.html @@ -0,0 +1,112 @@ + + + + + Bilan de compétences + + + +

Bilan de compétences

+
+ Élève : {{ student.last_name }} {{ student.first_name }}
+ Niveau : {{ student.level }}
+ Classe : {{ student.class_name }}
+ Période : {{ period }}
+ Date : {{ date }} +
+ + {% for domaine in domaines %} + + + + + + + + + + + + + + {% for categorie in domaine.categories %} + + + + {% for competence in categorie.competences %} + + + {% for note in "123" %} + + {% endfor %} + + {% endfor %} + {% endfor %} + +
{{ domaine.nom }}
Compétences123
{{ categorie.nom }}
{{ competence.nom }} + {% if competence.score|stringformat:"s" == note %} + + {% endif %} +
+ {% endfor %} + +
+
+
+ Appréciation générale / Commentaire : +
+ +
+
+
+
+
+
+
+
+
+
+ Date : +   +
+
+ Signature : +   +
+
+
+ + \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html b/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html deleted file mode 100644 index 4170a06..0000000 --- a/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - {{ pdf_title }} - - - - {% load myTemplateTag %} -
-
-

{{ pdf_title }}

-
-
-
- Signé le : {{ signatureDate }}
- A : {{ signatureTime }} -
-

ELEVE

- {% with level=student|getStudentLevel %} - {% with gender=student|getStudentGender %} - NOM : {{ student.last_name }}
- PRENOM : {{ student.first_name }}
- ADRESSE : {{ student.address }}
- GENRE : {{ gender }}
- NE(E) LE : {{ student.birth_date }}
- A : {{ student.birth_place }} ({{ student.birth_postal_code }})
- NATIONALITE : {{ student.nationality }}
- NIVEAU : {{ level }}
- MEDECIN TRAITANT : {{ student.attending_physician }}
- {% endwith %} - {% endwith %} -
-

RESPONSABLES

- {% with guardians=student.getGuardians %} - {% with siblings=student.getGuardians %} - {% for guardian in guardians%} -

Guardian {{ forloop.counter }}

- NOM : {{ guardian.last_name }}
- PRENOM : {{ guardian.first_name }}
- ADRESSE : {{ guardian.address }}
- NE(E) LE : {{ guardian.birth_date }}
- MAIL : {{ guardian.email }}
- TEL : {{ guardian.phone }}
- PROFESSION : {{ guardian.profession }}
- {% endfor %} -
-

FRATRIE

- {% for sibling in siblings%} -

Frère - Soeur {{ forloop.counter }}

- NOM : {{ sibling.last_name }}
- PRENOM : {{ sibling.first_name }}
- NE(E) LE : {{ sibling.birth_date }}
- {% endfor %} -
-

MODALITES DE PAIEMENT

- {% with paymentMethod=student|getStudentPaymentMethod %} - {{ paymentMethod }}
- {% endwith %} - {% endwith %} - {% endwith %} -
-
- - \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/pdfs/fiche_eleve.html b/Back-End/Subscriptions/templates/pdfs/fiche_eleve.html new file mode 100644 index 0000000..e311472 --- /dev/null +++ b/Back-End/Subscriptions/templates/pdfs/fiche_eleve.html @@ -0,0 +1,228 @@ + + + + + Fiche élève de {{ student.last_name }} {{ student.first_name }} + + + + {% load myTemplateTag %} +
+ +
+

Fiche élève de {{ student.last_name }} {{ student.first_name }}

+ {% if student.photo %} + Photo de l'élève + {% else %} + Photo par défaut + {% endif %} +
+ + +
+
ÉLÈVE
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nom{{ student.last_name }}Prénom{{ student.first_name }}
Adresse{{ student.address }}
Genre{{ student|getStudentGender }}Né(e) le{{ student.birth_date }}
À{{ student.birth_place }} ({{ student.birth_postal_code }})Nationalité{{ student.nationality }}
Niveau{{ student|getStudentLevel }}
+
+ + +
+
RESPONSABLES
+ {% for guardian in student.getGuardians %} +
+
Responsable {{ forloop.counter }}
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Nom{{ guardian.last_name }}Prénom{{ guardian.first_name }}
Adresse{{ guardian.address }}
Email{{ guardian.email }}
Né(e) le{{ guardian.birth_date }}Téléphone{{ guardian.phone|phone_format }}
Profession{{ guardian.profession }}
+
+ {% endfor %} +
+ + +
+
FRATRIE
+ {% for sibling in student.getSiblings %} +
+
Frère/Soeur {{ forloop.counter }}
+ + + + + + + + + + + +
Nom{{ sibling.last_name }}Prénom{{ sibling.first_name }}
Né(e) le{{ sibling.birth_date }}
+
+ {% endfor %} +
+ + +
+
MODALITÉS DE PAIEMENT
+ + + + + + + + + +
Frais d'inscription{{ student|getRegistrationPaymentMethod }} en {{ student|getRegistrationPaymentPlan }}
Frais de scolarité{{ student|getTuitionPaymentMethod }} en {{ student|getTuitionPaymentPlan }}
+
+ + +
+ Fait le {{ signatureDate }} à {{ signatureTime }} +
+
+ + \ No newline at end of file diff --git a/Back-End/Subscriptions/templatetags/myTemplateTag.py b/Back-End/Subscriptions/templatetags/myTemplateTag.py index 67db926..9485195 100644 --- a/Back-End/Subscriptions/templatetags/myTemplateTag.py +++ b/Back-End/Subscriptions/templatetags/myTemplateTag.py @@ -1,18 +1,52 @@ from Subscriptions.models import RegistrationForm, Student from django import template +import re + register = template.Library() @register.filter -def getStudentPaymentMethod(pk): +def getRegistrationPaymentPlan(pk): registerForm = RegistrationForm.objects.get(student=pk) - return Student.PaymentMethod(int(registerForm.student.payment_method)).label + if registerForm.registration_payment_plan: + return registerForm.registration_payment_plan.plan_type.label + return "" + +@register.filter +def getTuitionPaymentPlan(pk): + registerForm = RegistrationForm.objects.get(student=pk) + if registerForm.tuition_payment_plan: + return registerForm.tuition_payment_plan.plan_type.label + return "" + +@register.filter +def getRegistrationPaymentMethod(pk): + registerForm = RegistrationForm.objects.get(student=pk) + if registerForm.registration_payment: + return registerForm.registration_payment.mode.label + return "" + +@register.filter +def getTuitionPaymentMethod(pk): + registerForm = RegistrationForm.objects.get(student=pk) + if registerForm.tuition_payment: + return registerForm.tuition_payment.mode.label + return "" @register.filter def getStudentLevel(pk): registerForm = RegistrationForm.objects.get(student=pk) - return Student.StudentLevel(int(registerForm.student.level)).label + level = registerForm.student.level + if level: + return level.name + return "" @register.filter def getStudentGender(pk): registerForm = RegistrationForm.objects.get(student=pk) - return Student.StudentGender(int(registerForm.student.gender)).label \ No newline at end of file + return Student.StudentGender(int(registerForm.student.gender)).label + +@register.filter +def phone_format(value): + if value.startswith("+33"): + value = value.replace("+33", "+33 ") + return re.sub(r"(\d{2})", r"\1 ", value).strip() \ No newline at end of file diff --git a/Back-End/Subscriptions/urls.py b/Back-End/Subscriptions/urls.py index b4ff426..f1044d3 100644 --- a/Back-End/Subscriptions/urls.py +++ b/Back-End/Subscriptions/urls.py @@ -1,36 +1,73 @@ from django.urls import path, re_path from . import views -from Subscriptions.views import RegisterFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegisterFeeView + +# RF +from .views import RegisterFormView, RegisterFormWithIdView, send, resend, archive +# SubClasses +from .views import StudentView, GuardianView, ChildrenListView, StudentListView, DissociateGuardianView +# Files +from .views import ( + RegistrationSchoolFileMasterView, + RegistrationSchoolFileMasterSimpleView, + RegistrationSchoolFileTemplateView, + RegistrationSchoolFileTemplateSimpleView, + RegistrationParentFileMasterSimpleView, + RegistrationParentFileMasterView, + RegistrationParentFileTemplateSimpleView, + RegistrationParentFileTemplateView, + AbsenceManagementListCreateView, + AbsenceManagementDetailView, + StudentCompetencyListCreateView, + StudentCompetencySimpleView, + search_students +) + +from .views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group +from .views import registration_file_views, get_school_file_templates_by_rf, get_parent_file_templates_by_rf urlpatterns = [ - re_path(r'^registerForms/([a-zA-z]+)$', RegisterFormListView.as_view(), name="listefichesInscriptions"), - re_path(r'^registerForm$', RegisterFormView.as_view(), name="registerForms"), - re_path(r'^registerForm/([0-9]+)$', RegisterFormView.as_view(), name="registerForms"), - - # Page de formulaire d'inscription - ELEVE - re_path(r'^student/([0-9]+)$', StudentView.as_view(), name="students"), - - # Page de formulaire d'inscription - RESPONSABLE - re_path(r'^lastGuardian$', GuardianView.as_view(), name="lastGuardian"), - - # Envoi d'un dossier d'inscription - re_path(r'^send/([0-9]+)$', views.send, name="send"), - - # Archivage d'un dossier d'inscription - re_path(r'^archive/([0-9]+)$', views.archive, name="archive"), - - # Envoi d'une relance de dossier d'inscription - re_path(r'^sendRelance/([0-9]+)$', views.relance, name="relance"), - - # Page PARENT - Liste des children - re_path(r'^children/([0-9]+)$', ChildrenListView.as_view(), name="children"), + re_path(r'^registerForms/(?P[0-9]+)/archive$', archive, name="archive"), + re_path(r'^registerForms/(?P[0-9]+)/resend$', resend, name="resend"), + re_path(r'^registerForms/(?P[0-9]+)/send$', send, name="send"), + re_path(r'^registerForms/(?P[0-9]+)$', RegisterFormWithIdView.as_view(), name="registerForm"), + re_path(r'^registerForms/(?P[0-9]+)/school_file_templates$', get_school_file_templates_by_rf, name="get_school_file_templates_by_rf"), + re_path(r'^registerForms/(?P[0-9]+)/parent_file_templates$', get_parent_file_templates_by_rf, name="get_parent_file_templates_by_rf"), + re_path(r'^registerForms$', RegisterFormView.as_view(), name="registerForms"), # Page INSCRIPTION - Liste des élèves re_path(r'^students$', StudentListView.as_view(), name="students"), + # Page de formulaire d'inscription - ELEVE + re_path(r'^students/(?P[0-9]+)$', StudentView.as_view(), name="students"), + re_path(r'^search-students', search_students, name='search_students'), + # Page PARENT - Liste des children + re_path(r'^children/(?P[0-9]+)$', ChildrenListView.as_view(), name="children"), + + # Page de formulaire d'inscription - RESPONSABLE + re_path(r'^lastGuardianId$', GuardianView.as_view(), name="lastGuardianId"), + + re_path(r'^registrationFileGroups/(?P[0-9]+)$', RegistrationFileGroupSimpleView.as_view(), name='registrationFileGroupDetail'), + re_path(r'^registrationFileGroups/(?P[0-9]+)/templates$', get_registration_files_by_group, name="get_registration_files_by_group"), + re_path(r'^registrationFileGroups$', RegistrationFileGroupView.as_view(), name='registrationFileGroups'), + + re_path(r'^registrationSchoolFileMasters/(?P[0-9]+)$', RegistrationSchoolFileMasterSimpleView.as_view(), name='registrationSchoolFileMasters'), + re_path(r'^registrationSchoolFileMasters$', RegistrationSchoolFileMasterView.as_view(), name='registrationSchoolFileMasters'), + + re_path(r'^registrationParentFileMasters/(?P[0-9]+)$', RegistrationParentFileMasterSimpleView.as_view(), name='registrationParentFileMasters'), + re_path(r'^registrationParentFileMasters$', RegistrationParentFileMasterView.as_view(), name="registrationParentFileMasters"), + + re_path(r'^registrationSchoolFileTemplates/(?P[0-9]+)$', RegistrationSchoolFileTemplateSimpleView.as_view(), name='registrationSchoolFileTemplates'), + re_path(r'^registrationSchoolFileTemplates$', RegistrationSchoolFileTemplateView.as_view(), name="registrationSchoolFileTemplates"), + + re_path(r'^registrationParentFileTemplates/(?P[0-9]+)$', RegistrationParentFileTemplateSimpleView.as_view(), name='registrationParentFileTemplates'), + re_path(r'^registrationParentFileTemplates$', RegistrationParentFileTemplateView.as_view(), name="registrationSchoolFileTregistrationParentFileTemplatesemplates"), + + re_path(r'^students/(?P[0-9]+)/guardians/(?P[0-9]+)/dissociate', DissociateGuardianView.as_view(), name='dissociate-guardian'), + + re_path(r'^absences$', AbsenceManagementListCreateView.as_view(), name="absence_list_create"), + re_path(r'^absences/(?P[0-9]+)$', AbsenceManagementDetailView.as_view(), name="absence_detail"), + + re_path(r'^studentCompetencies$', StudentCompetencyListCreateView.as_view(), name="student_competency_list_create"), + re_path(r'^studentCompetencies/(?P[0-9]+)$', StudentCompetencySimpleView.as_view(), name="student_competency_detail"), - # Frais d'inscription - re_path(r'^registerFees$', RegisterFeeView.as_view(), name="registerFees"), - re_path(r'^registerFileTemplate$', RegisterFileTemplateView.as_view(), name='registerFileTemplate'), - re_path(r'^registerFileTemplate/([0-9]+)$', RegisterFileTemplateView.as_view(), name="registerFileTemplate"), ] \ No newline at end of file diff --git a/Back-End/Subscriptions/util.py b/Back-End/Subscriptions/util.py index 9ae17a9..cc7f479 100644 --- a/Back-End/Subscriptions/util.py +++ b/Back-End/Subscriptions/util.py @@ -16,67 +16,200 @@ from enum import Enum import random import string from rest_framework.parsers import JSONParser +from PyPDF2 import PdfMerger + +import shutil +import logging + +logger = logging.getLogger(__name__) def recupereListeFichesInscription(): + """ + Retourne la liste complète des fiches d’inscription. + """ context = { "ficheInscriptions_list": bdd.getAllObjects(RegistrationForm), } return context def recupereListeFichesInscriptionEnAttenteSEPA(): - + """ + Retourne les fiches d’inscription avec paiement SEPA en attente. + """ ficheInscriptionsSEPA_list = RegistrationForm.objects.filter(modePaiement="Prélèvement SEPA").filter(etat=RegistrationForm.RegistrationFormStatus['SEPA_ENVOYE']) return ficheInscriptionsSEPA_list def _now(): + """ + Retourne la date et l’heure en cours, avec fuseau. + """ return datetime.now(ZoneInfo(settings.TZ_APPLI)) def convertToStr(dateValue, dateFormat): + """ + Convertit un objet datetime en chaîne selon un format donné. + """ return dateValue.strftime(dateFormat) def convertToDate(date_time): + """ + Convertit une chaîne en objet datetime selon le format '%d-%m-%Y %H:%M'. + """ format = '%d-%m-%Y %H:%M' datetime_str = datetime.strptime(date_time, format) return datetime_str def convertTelephone(telephoneValue, separator='-'): + """ + Reformate un numéro de téléphone en y insérant un séparateur donné. + """ return f"{telephoneValue[:2]}{separator}{telephoneValue[2:4]}{separator}{telephoneValue[4:6]}{separator}{telephoneValue[6:8]}{separator}{telephoneValue[8:10]}" def genereRandomCode(length): + """ + Génère un code aléatoire de longueur spécifiée. + """ return ''.join(random.choice(string.ascii_letters) for i in range(length)) def calculeDatePeremption(_start, nbDays): + """ + Calcule la date de fin à partir d’un point de départ et d’un nombre de jours. + """ return convertToStr(_start + timedelta(days=nbDays), settings.DATE_FORMAT) # Fonction permettant de retourner la valeur du QueryDict # QueryDict [ index ] -> Dernière valeur d'une liste # dict (QueryDict [ index ]) -> Toutes les valeurs de la liste def _(liste): + """ + Retourne la première valeur d’une liste extraite d’un QueryDict. + """ return liste[0] def getArgFromRequest(_argument, _request): + """ + Extrait la valeur d’un argument depuis la requête (JSON). + """ resultat = None data=JSONParser().parse(_request) resultat = data[_argument] return resultat -def rfToPDF(registerForm): - # Ajout du fichier d'inscriptions +def merge_files_pdf(file_paths): + """ + Fusionne plusieurs fichiers PDF et retourne le contenu fusionné en mémoire. + """ + merger = PdfMerger() + + # Ajouter les fichiers valides au merger + for file_path in file_paths: + merger.append(file_path) + + # Sauvegarder le fichier fusionné en mémoire + merged_pdf = BytesIO() + merger.write(merged_pdf) + merger.close() + + # Revenir au début du fichier en mémoire + merged_pdf.seek(0) + + return merged_pdf + +def rfToPDF(registerForm, filename): + """ + Génère le PDF d'un dossier d'inscription et l'associe au RegistrationForm. + """ + filename = filename.replace(" ", "_") data = { - 'pdf_title': "Dossier d'inscription de %s"%registerForm.student.first_name, + 'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}", 'signatureDate': convertToStr(_now(), '%d-%m-%Y'), 'signatureTime': convertToStr(_now(), '%H:%M'), - 'student':registerForm.student, + 'student': registerForm.student, } - pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data) + # Générer le PDF + pdf = renderers.render_to_pdf('pdfs/fiche_eleve.html', data) + if not pdf: + raise ValueError("Erreur lors de la génération du PDF.") - PDFFileName = "Dossier_Inscription_%s_%s.pdf"%(registerForm.student.last_name, registerForm.student.first_name) - pathFichier = Path(settings.DOCUMENT_DIR + "/" + PDFFileName) - if os.path.exists(str(pathFichier)): - print(f'File exists : {str(pathFichier)}') - os.remove(str(pathFichier)) + # Vérifier si un fichier avec le même nom existe déjà et le supprimer + if registerForm.registration_file and registerForm.registration_file.name: + # Vérifiez si le chemin est déjà absolu ou relatif + if os.path.isabs(registerForm.registration_file.name): + existing_file_path = registerForm.registration_file.name + else: + existing_file_path = os.path.join(settings.MEDIA_ROOT, registerForm.registration_file.name.lstrip('/')) - receipt_file = BytesIO(pdf.content) - registerForm.fichierInscription = File(receipt_file, PDFFileName) \ No newline at end of file + # Vérifier si le fichier existe et le supprimer + if os.path.exists(existing_file_path): + os.remove(existing_file_path) + registerForm.registration_file.delete(save=False) + else: + print(f'File does not exist: {existing_file_path}') + + # Enregistrer directement le fichier dans le champ registration_file + try: + registerForm.registration_file.save( + os.path.basename(filename), # Utiliser uniquement le nom de fichier + File(BytesIO(pdf.content)), + save=True + ) + except Exception as e: + logger.error(f"Erreur lors de la sauvegarde du fichier PDF : {e}") + raise + + return registerForm.registration_file + +def delete_registration_files(registerForm): + """ + Supprime le fichier et le dossier associés à un RegistrationForm. + """ + base_dir = f"registration_files/dossier_rf_{registerForm.pk}" + if registerForm.registration_file and os.path.exists(registerForm.registration_file.path): + os.remove(registerForm.registration_file.path) + registerForm.registration_file.delete(save=False) + + if os.path.exists(base_dir): + shutil.rmtree(base_dir) + +from datetime import datetime + +def getCurrentSchoolYear(): + """ + Retourne l'année scolaire en cours au format "YYYY-YYYY". + Exemple : Si nous sommes en octobre 2023, retourne "2023-2024". + """ + now = datetime.now() + current_year = now.year + current_month = now.month + + # Si nous sommes avant septembre, l'année scolaire a commencé l'année précédente + start_year = current_year if current_month >= 9 else current_year - 1 + return f"{start_year}-{start_year + 1}" + +def getNextSchoolYear(): + """ + Retourne l'année scolaire suivante au format "YYYY-YYYY". + Exemple : Si nous sommes en octobre 2023, retourne "2024-2025". + """ + current_school_year = getCurrentSchoolYear() + start_year, end_year = map(int, current_school_year.split('-')) + return f"{start_year + 1}-{end_year + 1}" + + +def getHistoricalYears(count=5): + """ + Retourne un tableau des années scolaires passées au format "YYYY-YYYY". + Exemple : ["2022-2023", "2021-2022", "2020-2021"]. + :param count: Le nombre d'années scolaires passées à inclure. + """ + current_school_year = getCurrentSchoolYear() + start_year = int(current_school_year.split('-')[0]) + + historical_years = [] + for i in range(1, count + 1): + historical_start_year = start_year - i + historical_years.append(f"{historical_start_year}-{historical_start_year + 1}") + + return historical_years \ No newline at end of file diff --git a/Back-End/Subscriptions/views.py b/Back-End/Subscriptions/views.py deleted file mode 100644 index 0d5ec12..0000000 --- a/Back-End/Subscriptions/views.py +++ /dev/null @@ -1,280 +0,0 @@ -from django.http.response import JsonResponse -from django.contrib.auth import login, authenticate, get_user_model -from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect -from django.utils.decorators import method_decorator -from django.core.cache import cache -from django.core.paginator import Paginator -from django.core.files import File -from django.db.models import Q # Ajout de cet import -from rest_framework.parsers import JSONParser,MultiPartParser, FormParser -from rest_framework.response import Response -from rest_framework.views import APIView -from rest_framework import status - -import json -from pathlib import Path -import os -from io import BytesIO - -import Subscriptions.mailManager as mailer -import Subscriptions.util as util -from Subscriptions.serializers import RegistrationFormSerializer, RegistrationFileSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFeeSerializer -from Subscriptions.pagination import CustomPagination -from Subscriptions.signals import clear_cache -from .models import Student, Guardian, RegistrationForm, RegistrationFee, RegistrationFile - -from Subscriptions.automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine - -from Auth.models import Profile - -from N3wtSchool import settings, renderers, bdd - -class RegisterFormListView(APIView): - pagination_class = CustomPagination - - def get_register_form(self, _filter, search=None): - """ - Récupère les fiches d'inscriptions en fonction du filtre passé. - _filter: Filtre pour déterminer l'état des fiches ('pending', 'archived', 'subscribed') - search: Terme de recherche (optionnel) - """ - if _filter == 'pending': - exclude_states = [RegistrationForm.RegistrationFormStatus.RF_VALIDATED, RegistrationForm.RegistrationFormStatus.RF_ARCHIVED] - return bdd.searchObjects(RegistrationForm, search, _excludeStates=exclude_states) - elif _filter == 'archived': - return bdd.getObjects(RegistrationForm, 'status', RegistrationForm.RegistrationFormStatus.RF_ARCHIVED) - elif _filter == 'subscribed': - return bdd.getObjects(RegistrationForm, 'status', RegistrationForm.RegistrationFormStatus.RF_VALIDATED) - return None - - def get(self, request, _filter): - - # Récupération des paramètres - search = request.GET.get('search', '').strip() - page_size = request.GET.get('page_size', None) - - # Gestion du page_size - if page_size is not None: - try: - page_size = int(page_size) - except ValueError: - page_size = settings.NB_RESULT_PER_PAGE - - # Définir le cache_key en fonction du filtre - page_number = request.GET.get('page', 1) - cache_key = f'N3WT_ficheInscriptions_{_filter}_page_{page_number}_search_{search if _filter == "pending" else ""}' - cached_page = cache.get(cache_key) - if cached_page: - return JsonResponse(cached_page, safe=False) - - # Récupérer les fiches d'inscriptions en fonction du filtre - registerForms_List = self.get_register_form(_filter, search) - - if not registerForms_List: - return JsonResponse({'error' : 'aucune donnée trouvée', 'count' :0}, safe=False) - - # Pagination - paginator = self.pagination_class() - page = paginator.paginate_queryset(registerForms_List, request) - if page is not None: - registerForms_serializer = RegistrationFormSerializer(page, many=True) - response_data = paginator.get_paginated_response(registerForms_serializer.data) - cache.set(cache_key, response_data, timeout=60*15) - return JsonResponse(response_data, safe=False) - - return JsonResponse({'error' : 'aucune donnée trouvée', 'count' :0}, safe=False) - - def post(self, request): - studentFormList_serializer=JSONParser().parse(request) - for studentForm_data in studentFormList_serializer: - # Ajout de la date de mise à jour - studentForm_data["last_update"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') - json.dumps(studentForm_data) - # Ajout du code d'inscription - code = util.genereRandomCode(12) - studentForm_data["codeLienInscription"] = code - studentForm_serializer = RegistrationFormSerializer(data=studentForm_data) - - if studentForm_serializer.is_valid(): - studentForm_serializer.save() - - return JsonResponse(studentForm_serializer.errors, safe=False) - - -@method_decorator(csrf_protect, name='dispatch') -@method_decorator(ensure_csrf_cookie, name='dispatch') -class RegisterFormView(APIView): - pagination_class = CustomPagination - - def get(self, request, _id): - registerForm=bdd.getObject(RegistrationForm, "student__id", _id) - registerForm_serializer=RegistrationFormSerializer(registerForm) - return JsonResponse(registerForm_serializer.data, safe=False) - - def post(self, request): - studentForm_data=JSONParser().parse(request) - # Ajout de la date de mise à jour - studentForm_data["last_update"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') - json.dumps(studentForm_data) - # Ajout du code d'inscription - code = util.genereRandomCode(12) - studentForm_data["codeLienInscription"] = code - - guardiansId = studentForm_data.pop('idGuardians', []) - studentForm_serializer = RegistrationFormSerializer(data=studentForm_data) - - if studentForm_serializer.is_valid(): - di = studentForm_serializer.save() - - # Mise à jour de l'automate - updateStateMachine(di, 'creationDI') - - # Récupération du reponsable associé - for guardianId in guardiansId: - guardian = Guardian.objects.get(id=guardianId) - di.student.guardians.add(guardian) - di.save() - - return JsonResponse(studentForm_serializer.data, safe=False) - - return JsonResponse(studentForm_serializer.errors, safe=False) - - def put(self, request, id): - studentForm_data=JSONParser().parse(request) - status = studentForm_data.pop('status', 0) - studentForm_data["last_update"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M')) - registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) - - if status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW: - # Le parent a complété le dossier d'inscription, il est soumis à validation par l'école - json.dumps(studentForm_data) - util.rfToPDF(registerForm) - # Mise à jour de l'automate - updateStateMachine(registerForm, 'saisiDI') - elif status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED: - # L'école a validé le dossier d'inscription - # Mise à jour de l'automate - updateStateMachine(registerForm, 'valideDI') - - - studentForm_serializer = RegistrationFormSerializer(registerForm, data=studentForm_data) - if studentForm_serializer.is_valid(): - studentForm_serializer.save() - return JsonResponse(studentForm_serializer.data, safe=False) - - return JsonResponse(studentForm_serializer.errors, safe=False) - - def delete(self, request, id): - register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) - if register_form != None: - student = register_form.student - student.guardians.clear() - student.profiles.clear() - student.delete() - clear_cache() - - return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False) - - return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False) - -class StudentView(APIView): - def get(self, request, _id): - student = bdd.getObject(_objectName=Student, _columnName='id', _value=_id) - student_serializer = StudentSerializer(student) - return JsonResponse(student_serializer.data, safe=False) - -class GuardianView(APIView): - def get(self, request): - lastGuardian = bdd.getLastId(Guardian) - return JsonResponse({"lastid":lastGuardian}, safe=False) - -def send(request, id): - register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) - if register_form != None: - student = register_form.student - guardian = student.getMainGuardian() - email = guardian.email - errorMessage = mailer.sendRegisterForm(email) - if errorMessage == '': - register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') - # Mise à jour de l'automate - updateStateMachine(register_form, 'envoiDI') - - return JsonResponse({"errorMessage":errorMessage}, safe=False) - - return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False) - -def archive(request, id): - register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) - if register_form != None: - register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') - # Mise à jour de l'automate - updateStateMachine(register_form, 'archiveDI') - - return JsonResponse({"errorMessage":''}, safe=False) - - return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False) - -def relance(request, id): - register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) - if register_form != None: - student = register_form.student - guardian = student.getMainGuardian() - email = guardian.email - errorMessage = mailer.envoieRelanceDossierInscription(email, register_form.codeLienInscription) - if errorMessage == '': - register_form.status=RegistrationForm.RegistrationFormStatus.RF_SENT - register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') - register_form.save() - - return JsonResponse({"errorMessage":errorMessage}, safe=False) - - return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False) - -# API utilisée pour la vue parent -class ChildrenListView(APIView): - # Récupération des élèves d'un parent - # idProfile : identifiant du profil connecté rattaché aux fiches d'élèves - def get(self, request, _idProfile): - students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__profilAssocie__id', _value=_idProfile) - students_serializer = RegistrationFormByParentSerializer(students, many=True) - return JsonResponse(students_serializer.data, safe=False) - -# API utilisée pour la vue de création d'un DI -class StudentListView(APIView): - # Récupération de la liste des élèves inscrits ou en cours d'inscriptions - def get(self, request): - students = bdd.getAllObjects(_objectName=Student) - students_serializer = StudentByRFCreationSerializer(students, many=True) - return JsonResponse(students_serializer.data, safe=False) - -# API utilisée pour la vue de personnalisation des frais d'inscription pour la structure -class RegisterFeeView(APIView): - def get(self, request): - tarifs = bdd.getAllObjects(RegistrationFee) - tarifs_serializer = RegistrationFeeSerializer(tarifs, many=True) - return JsonResponse(tarifs_serializer.data, safe=False) - -class RegisterFileTemplateView(APIView): - parser_classes = (MultiPartParser, FormParser) - - def get(self, request): - fichiers = RegistrationFile.objects.all() - serializer = RegistrationFormSerializer(fichiers, many=True) - return Response(serializer.data) - - def post(self, request): - serializer = RegistrationFormSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, _id): - fichierInscription = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=_id) - if fichierInscription is not None: - fichierInscription.file.delete() # Supprimer le fichier uploadé - fichierInscription.delete() - return JsonResponse({'message': 'La suppression du fichier d\'inscription a été effectuée avec succès'}, safe=False) - else: - return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False) diff --git a/Back-End/Subscriptions/views/__init__.py b/Back-End/Subscriptions/views/__init__.py new file mode 100644 index 0000000..abab71c --- /dev/null +++ b/Back-End/Subscriptions/views/__init__.py @@ -0,0 +1,47 @@ +from .register_form_views import RegisterFormView, RegisterFormWithIdView, send, resend, archive, get_school_file_templates_by_rf, get_parent_file_templates_by_rf +from .registration_file_views import ( + RegistrationSchoolFileMasterView, + RegistrationSchoolFileMasterSimpleView, + RegistrationSchoolFileTemplateView, + RegistrationSchoolFileTemplateSimpleView, + RegistrationParentFileMasterView, + RegistrationParentFileMasterSimpleView, + RegistrationParentFileTemplateSimpleView, + RegistrationParentFileTemplateView +) +from .registration_file_group_views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group +from .student_views import StudentView, StudentListView, ChildrenListView, search_students +from .guardian_views import GuardianView, DissociateGuardianView +from .absences_views import AbsenceManagementDetailView, AbsenceManagementListCreateView +from .student_competencies_views import StudentCompetencyListCreateView, StudentCompetencySimpleView + +__all__ = [ + 'RegisterFormView', + 'RegisterFormWithIdView', + 'send', + 'resend', + 'archive', + 'RegistrationSchoolFileTemplateView', + 'RegistrationSchoolFileTemplateSimpleView', + 'RegistrationParentFileMasterSimpleView', + 'RegistrationParentFileMasterView', + 'RegistrationSchoolFileMasterView', + 'RegistrationSchoolFileMasterSimpleView', + 'RegistrationParentFileTemplateSimpleView', + 'RegistrationParentFileTemplateView', + 'RegistrationFileGroupView', + 'RegistrationFileGroupSimpleView', + 'get_registration_files_by_group', + 'get_school_file_templates_by_rf', + 'get_parent_file_templates_by_rf' + 'StudentView', + 'StudentListView', + 'ChildrenListView', + 'GuardianView', + 'DissociateGuardianView', + 'AbsenceManagementDetailView', + 'AbsenceManagementListCreateView', + 'StudentCompetencyListCreateView', + 'StudentCompetencySimpleView', + 'search_students' +] diff --git a/Back-End/Subscriptions/views/absences_views.py b/Back-End/Subscriptions/views/absences_views.py new file mode 100644 index 0000000..9d79be0 --- /dev/null +++ b/Back-End/Subscriptions/views/absences_views.py @@ -0,0 +1,50 @@ +from django.http.response import JsonResponse +from rest_framework.views import APIView +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from django.utils.decorators import method_decorator +from Subscriptions.serializers import AbsenceManagementSerializer +from Subscriptions.models import AbsenceManagement +from N3wtSchool.bdd import delete_object + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class AbsenceManagementListCreateView(APIView): + def get(self, request): + absences = AbsenceManagement.objects.all() + serializer = AbsenceManagementSerializer(absences, many=True) + return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK) + + def post(self, request): + serializer = AbsenceManagementSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_201_CREATED) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class AbsenceManagementDetailView(APIView): + def get(self, request, id): + try: + absence = AbsenceManagement.objects.get(id=id) + serializer = AbsenceManagementSerializer(absence) + return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK) + except AbsenceManagement.DoesNotExist: + return JsonResponse({"error": "Absence not found"}, safe=False, status=status.HTTP_404_NOT_FOUND) + + def put(self, request, id): + try: + absence = AbsenceManagement.objects.get(id=id) + serializer = AbsenceManagementSerializer(absence, data=request.data) + if serializer.is_valid(): + serializer.save() + return JsonResponse(serializer.data, safe=False, status=status.HTTP_200_OK) + return JsonResponse(serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + except AbsenceManagement.DoesNotExist: + return JsonResponse({"error": "Absence not found"}, safe=False, status=status.HTTP_404_NOT_FOUND) + + def delete(self, request, id): + return delete_object(AbsenceManagement, id) \ No newline at end of file diff --git a/Back-End/Subscriptions/views/guardian_views.py b/Back-End/Subscriptions/views/guardian_views.py new file mode 100644 index 0000000..de449c9 --- /dev/null +++ b/Back-End/Subscriptions/views/guardian_views.py @@ -0,0 +1,111 @@ +from django.http.response import JsonResponse +from rest_framework import status +from rest_framework.views import APIView +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi + +from Subscriptions.models import Guardian, Student, RegistrationForm +from Auth.models import ProfileRole +from N3wtSchool import bdd + +import Subscriptions.util as util + +class GuardianView(APIView): + """ + Gestion des responsables légaux. + """ + + @swagger_auto_schema( + operation_description="Récupère le dernier ID de responsable légal créé", + operation_summary="Récupèrer le dernier ID de responsable légal créé", + responses={ + 200: openapi.Response( + description="Dernier ID du responsable légal", + schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'lastid': openapi.Schema( + type=openapi.TYPE_INTEGER, + description="Dernier ID créé" + ) + } + ) + ) + } + ) + def get(self, request): + lastGuardian = bdd.getLastId(Guardian) + return JsonResponse({"lastid":lastGuardian}, safe=False) + +class DissociateGuardianView(APIView): + """ + Vue pour dissocier un Guardian d'un Student. + """ + + def put(self, request, student_id, guardian_id): + try: + # Récupérer l'étudiant + student = Student.objects.get(id=student_id) + + # Récupérer le guardian + guardian = Guardian.objects.get(id=guardian_id) + + # Vérifier s'il y a d'autres guardians associés au student + other_guardians = student.guardians.exclude(id=guardian_id) + if not other_guardians.exists(): + return JsonResponse( + {"error": "Impossible de dissocier ce responsable car il n'en existe aucun autre rattaché à l'élève."}, + status=status.HTTP_400_BAD_REQUEST + ) + + # Supprimer la relation entre le student et le guardian + student.guardians.remove(guardian) + + # Mettre à jour le responsable principal (par exemple, le premier autre guardian) + new_main_guardian = other_guardians.first() + if new_main_guardian: + # Logique pour définir le nouveau responsable principal + print(f"Le guardian {new_main_guardian} devient le responsable principal.") + + isGuardianDeleted = False + # Vérifier si le guardian n'est plus associé à aucun élève + if guardian.student_set.count() == 0: # Utilise la relation ManyToMany inverse + print(f'Le guardian {guardian} n\'est plus rattaché à aucun élève : on le supprime') + isGuardianDeleted = True + + # Vérifier si le guardian a un ProfileRole associé + if guardian.profile_role: + print(f'Suppression du ProfileRole associé au guardian {guardian}') + guardian.profile_role.delete() + + # Vérifier si le Profile n'a plus de ProfileRole associés + profile = guardian.profile_role.profile + if not ProfileRole.objects.filter(profile=profile).exists(): + print(f'Le profile {profile} n\'a plus de rôle associé : on le supprime') + profile.delete() + + # Supprimer le guardian + guardian.delete() + + return JsonResponse( + { + "message": f"Le guardian {guardian.last_name} {guardian.first_name} a été dissocié de l'étudiant {student.last_name} {student.first_name}.", + "isGuardianDeleted": isGuardianDeleted + }, + status=status.HTTP_200_OK + ) + except Student.DoesNotExist: + return JsonResponse( + {"error": "Étudiant non trouvé."}, + status=status.HTTP_404_NOT_FOUND + ) + except Guardian.DoesNotExist: + return JsonResponse( + {"error": "Guardian non trouvé."}, + status=status.HTTP_404_NOT_FOUND + ) + except Exception as e: + return JsonResponse( + {"error": f"Une erreur est survenue : {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py new file mode 100644 index 0000000..18479f5 --- /dev/null +++ b/Back-End/Subscriptions/views/register_form_views.py @@ -0,0 +1,581 @@ +from django.http.response import JsonResponse +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from django.utils.decorators import method_decorator +from rest_framework.views import APIView +from rest_framework.decorators import action, api_view +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi + +import json +import os +from django.core.files import File + +import N3wtSchool.mailManager as mailer +import Subscriptions.util as util + +from Subscriptions.serializers import RegistrationFormSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileTemplateSerializer +from Subscriptions.pagination import CustomSubscriptionPagination +from Subscriptions.models import ( + Guardian, + RegistrationForm, + RegistrationSchoolFileTemplate, + RegistrationFileGroup, + RegistrationParentFileTemplate, + StudentCompetency +) +from Subscriptions.automate import updateStateMachine +from School.models import EstablishmentCompetency +from Establishment.models import Establishment + +from N3wtSchool import settings, bdd +from django.db.models import Q + + + +import logging +logger = logging.getLogger(__name__) + +# /Subscriptions/registerForms +class RegisterFormView(APIView): + """ + Gère la liste des dossiers d’inscription, lecture et création. + """ + pagination_class = CustomSubscriptionPagination + + @swagger_auto_schema( + manual_parameters=[ + openapi.Parameter('filter', openapi.IN_QUERY, description="filtre", type=openapi.TYPE_STRING, enum=['current_year', 'next_year', 'historical'], required=True), + openapi.Parameter('search', openapi.IN_QUERY, description="search", type=openapi.TYPE_STRING, required=False), + openapi.Parameter('page_size', openapi.IN_QUERY, description="limite de page lors de la pagination", type=openapi.TYPE_INTEGER, required=False), + openapi.Parameter('establishment_id', openapi.IN_QUERY, description="ID de l'établissement", type=openapi.TYPE_INTEGER, required=True), + ], + responses={200: RegistrationFormSerializer(many=True)}, + operation_description="Récupère les dossier d'inscriptions en fonction du filtre passé.", + operation_summary="Récupérer les dossier d'inscriptions", + examples={ + "application/json": [ + { + "id": 1, + "student": { + "id": 1, + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "2010-01-01" + }, + "status": "current_year", + "last_update": "10-02-2025 10:00" + }, + { + "id": 2, + "student": { + "id": 2, + "first_name": "Jane", + "last_name": "Doe", + "date_of_birth": "2011-02-02" + }, + "status": "historical", + "last_update": "09-02-2025 09:00" + } + ] + } + ) + def get(self, request): + """ + Récupère les fiches d'inscriptions en fonction du filtre passé. + """ + # Récupération des paramètres + filter = request.GET.get('filter', '').strip() + page_size = request.GET.get('page_size', None) + establishment_id = request.GET.get('establishment_id', None) + search = request.GET.get('search', '').strip() # <-- Ajout du paramètre search + + # Gestion du page_size + if page_size is not None: + try: + page_size = int(page_size) + except ValueError: + page_size = settings.NB_RESULT_SUBSCRIPTIONS_PER_PAGE + + # Récupérer les années scolaires + current_year = util.getCurrentSchoolYear() + next_year = util.getNextSchoolYear() + historical_years = util.getHistoricalYears() + + # Récupérer les dossiers d'inscriptions en fonction du filtre + registerForms_List = None + if filter == 'current_year': + registerForms_List = RegistrationForm.objects.filter(school_year=current_year) + elif filter == 'next_year': + registerForms_List = RegistrationForm.objects.filter(school_year=next_year) + elif filter == 'historical': + registerForms_List = RegistrationForm.objects.filter(school_year__in=historical_years) + else: + registerForms_List = None + + if registerForms_List: + registerForms_List = registerForms_List.filter(establishment=establishment_id) + # Ajout du filtre search sur le nom et prénom de l'élève + if search: + registerForms_List = registerForms_List.filter( + Q(student__first_name__icontains=search) | + Q(student__last_name__icontains=search) + ) + registerForms_List = registerForms_List.order_by('-last_update') + + if not registerForms_List: + return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False) + + # Pagination + paginator = self.pagination_class() + page = paginator.paginate_queryset(registerForms_List, request) + if page is not None: + registerForms_serializer = RegistrationFormSerializer(page, many=True) + response_data = paginator.get_paginated_response(registerForms_serializer.data) + return JsonResponse(response_data, safe=False) + + return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False) + + @swagger_auto_schema( + request_body=RegistrationFormSerializer, + responses={200: RegistrationFormSerializer()}, + operation_description="Crée un dossier d'inscription.", + operation_summary="Créer un dossier d'inscription", + examples={ + "application/json": { + "student": { + "id": 1, + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "2010-01-01" + }, + "status": "current_year", + "last_update": "10-02-2025 10:00", + "codeLienInscription": "ABC123XYZ456" + } + } + ) + @method_decorator(csrf_protect, name='dispatch') + @method_decorator(ensure_csrf_cookie, name='dispatch') + def post(self, request): + """ + Crée un dossier d'inscription. + """ + regiterFormData = request.data.copy() + logger.info(f"Création d'un dossier d'inscription {request}") + # Ajout de la date de mise à jour + regiterFormData["last_update"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') + # Ajout du code d'inscription + code = util.genereRandomCode(12) + regiterFormData["codeLienInscription"] = code + + guardiansId = regiterFormData.pop('idGuardians', []) + registerForm_serializer = RegistrationFormSerializer(data=regiterFormData) + fileGroupId = regiterFormData.pop('fileGroup', None) + + if registerForm_serializer.is_valid(): + di = registerForm_serializer.save() + + # Mise à jour de l'automate + updateStateMachine(di, 'EVENT_INIT') + + # Récupération du reponsable associé + for guardianId in guardiansId: + guardian = Guardian.objects.get(id=guardianId) + di.student.guardians.add(guardian) + di.save() + if fileGroupId: + di.fileGroup = RegistrationFileGroup.objects.get(id=fileGroupId) + di.save() + + return JsonResponse(registerForm_serializer.data, safe=False) + else: + logger.error(f"Erreur lors de la validation des données {regiterFormData}") + + return JsonResponse(registerForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + +# /Subscriptions/registerForms/{id} +class RegisterFormWithIdView(APIView): + """ + Gère la lecture, création, modification et suppression d’un dossier d’inscription. + """ + pagination_class = CustomSubscriptionPagination + + @swagger_auto_schema( + responses={200: RegistrationFormSerializer()}, + operation_description="Récupère un dossier d'inscription donné.", + operation_summary="Récupérer un dossier d'inscription", + examples={ + "application/json": { + "id": 1, + "student": { + "id": 1, + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "2010-01-01" + }, + } + } + ) + def get(self, request, id): + """ + Récupère un dossier d'inscription donné. + """ + registerForm = bdd.getObject(RegistrationForm, "student__id", id) + if registerForm is None: + return JsonResponse({"errorMessage":'Le dossier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + registerForm_serializer = RegistrationFormSerializer(registerForm) + return JsonResponse(registerForm_serializer.data, safe=False) + + @swagger_auto_schema( + request_body=RegistrationFormSerializer, + responses={200: RegistrationFormSerializer()}, + operation_description="Modifie un dossier d'inscription donné.", + operation_summary="Modifier un dossier d'inscription", + examples={ + "application/json": { + "id": 1, + "student": { + "id": 1, + "first_name": "John", + "last_name": "Doe", + "date_of_birth": "2010-01-01" + }, + "status": "under_review", + "last_update": "10-02-2025 10:00" + } + } + ) + @method_decorator(csrf_protect, name='dispatch') + @method_decorator(ensure_csrf_cookie, name='dispatch') + def put(self, request, id): + """ + Modifie un dossier d'inscription donné. + """ + + studentForm_data = request.data.get('data', '{}') + + try: + data = json.loads(studentForm_data) + except json.JSONDecodeError: + return JsonResponse({"error": "Invalid JSON format in 'data'"}, status=status.HTTP_400_BAD_REQUEST) + + # Extraire le fichier photo + photo_file = request.FILES.get('photo') + + # Extraire le fichier photo + sepa_file = request.FILES.get('sepa_file') + + # Ajouter la photo aux données de l'étudiant + if photo_file: + data['student']['photo'] = photo_file + + if sepa_file: + data['sepa_file'] = sepa_file + + # Gérer le champ `_status` + _status = data.pop('status', 0) + _status = int(_status) + + # Récupérer le dossier d'inscription + registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) + if not registerForm: + return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND) + + studentForm_serializer = RegistrationFormSerializer(registerForm, data=data, partial=True) + if studentForm_serializer.is_valid(): + studentForm_serializer.save() + + # Sauvegarder la photo si elle est présente dans la requête + if photo_file: + student = registerForm.student + + # Vérifier si une photo existante est déjà associée à l'étudiant + if student.photo and student.photo.name: + # Construire le chemin complet du fichier existant + if os.path.isabs(student.photo.name): + existing_file_path = student.photo.name + else: + existing_file_path = os.path.join(settings.MEDIA_ROOT, student.photo.name.lstrip('/')) + + # Vérifier si le fichier existe et le supprimer + if os.path.exists(existing_file_path): + os.remove(existing_file_path) + student.photo.delete(save=False) + else: + print(f'File does not exist: {existing_file_path}') + + # Sauvegarder la nouvelle photo + student.photo.save(photo_file.name, photo_file, save=True) + else: + return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST) + + if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW: + # Le parent a rempli le dossier d'inscription sans sélectionner "Prélèvement par Mandat SEPA" + # L'école doit désormais valider le dossier d'inscription + try: + # Génération de la fiche d'inscription au format PDF + base_dir = os.path.join(settings.MEDIA_ROOT, f"registration_files/dossier_rf_{registerForm.pk}") + os.makedirs(base_dir, exist_ok=True) + + # Fichier PDF initial + initial_pdf = f"{base_dir}/Inscription_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf" + registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf) + registerForm.save() + + # Mise à jour de l'automate + # Vérification de la présence du fichier SEPA + if registerForm.sepa_file: + # Mise à jour de l'automate pour SEPA + updateStateMachine(registerForm, 'EVENT_SIGNATURE_SEPA') + else: + # Mise à jour de l'automate pour une signature classique + updateStateMachine(registerForm, 'EVENT_SIGNATURE') + except Exception as e: + return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + elif _status == RegistrationForm.RegistrationFormStatus.RF_SENT: + if registerForm.status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW: + updateStateMachine(registerForm, 'EVENT_REFUSE') + util.delete_registration_files(registerForm) + elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT: + # Sauvegarde du mandat SEPA + student = registerForm.student + guardian = student.getMainGuardian() + email = guardian.profile_role.profile.email + errorMessage = mailer.sendMandatSEPA(email, registerForm.establishment.pk) + if errorMessage != '': + return JsonResponse({"errorMessage": errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST) + registerForm.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M') + updateStateMachine(registerForm, 'EVENT_SEND_SEPA') + elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND: + # Le parent a rempli le dossier d'inscription en sélectionnant "Prélèvement par Mandat SEPA" + # L'école doit désormais envoyer le mandat SEPA pour poursuivre l'inscription + updateStateMachine(registerForm, 'EVENT_WAITING_FOR_SEPA') + + elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED: + # Vérifier si le paramètre fusion est activé via l'URL + fusion = data.get('fusionParam', False) + if fusion: + # Fusion des documents + # Récupération des fichiers schoolFileTemplates + school_file_paths = RegistrationSchoolFileTemplate.get_files_from_rf(registerForm.pk) + + # Récupération des fichiers parentFileTemplates + parent_file_templates = RegistrationParentFileTemplate.get_files_from_rf(registerForm.pk) + + # Initialisation de la liste des fichiers à fusionner + fileNames = [] + + # Ajout du fichier registration_file en première position + if registerForm.registration_file: + fileNames.append(registerForm.registration_file.path) + + # Ajout des fichiers schoolFileTemplates + fileNames.extend(school_file_paths) + + # Ajout des fichiers parentFileTemplates + fileNames.extend(parent_file_templates) + + # Création du fichier PDF fusionné + merged_pdf_content = util.merge_files_pdf(fileNames) + + # Mise à jour du champ registration_file avec le fichier fusionné + registerForm.fusion_file.save( + f"dossier_complet.pdf", + File(merged_pdf_content), + save=True + ) + # Valorisation des StudentCompetency pour l'élève + try: + student = registerForm.student + cycle = None + if student.level: + cycle = student.level.cycle.number + if cycle: + # Récupérer les EstablishmentCompetency de l'établissement et du cycle de l'élève + establishment_competencies = EstablishmentCompetency.objects.filter( + establishment=registerForm.establishment, + custom_category__domain__cycle=cycle + ) | EstablishmentCompetency.objects.filter( + establishment=registerForm.establishment, + competency__category__domain__cycle=cycle + ) + establishment_competencies = establishment_competencies.distinct() + + establishment = registerForm.establishment + evaluation_frequency = establishment.evaluation_frequency # 1=Trimestre, 2=Semestre, 3=Année + school_year = registerForm.school_year # ex: "2024_2025" + + establishment_competencies = establishment_competencies.distinct() + + periods = [] + if evaluation_frequency == 1: # Trimestre + periods = [f"T{i+1}_{school_year}" for i in range(3)] + elif evaluation_frequency == 2: # Semestre + periods = [f"S{i+1}_{school_year}" for i in range(2)] + elif evaluation_frequency == 3: # Année + periods = [f"A_{school_year}"] + + for ec in establishment_competencies: + for period in periods: + StudentCompetency.objects.get_or_create( + student=student, + establishment_competency=ec, + period=period + ) + except Exception as e: + logger.error(f"Erreur lors de la valorisation des StudentCompetency: {e}") + + updateStateMachine(registerForm, 'EVENT_VALIDATE') + + # Retourner les données mises à jour + return JsonResponse(studentForm_serializer.data, safe=False) + + @swagger_auto_schema( + responses={204: 'No Content'}, + operation_description="Supprime un dossier d'inscription donné.", + operation_summary="Supprimer un dossier d'inscription" + ) + @method_decorator(csrf_protect, name='dispatch') + @method_decorator(ensure_csrf_cookie, name='dispatch') + def delete(self, request, id): + """ + Supprime un dossier d'inscription donné. + """ + register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) + if register_form != None: + student = register_form.student + student.guardians.clear() + student.profiles.clear() + student.registration_files.clear() + student.delete() + + return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False) + + return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND) + +@swagger_auto_schema( + method='get', + responses={200: openapi.Response('Success', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING) + } + ))}, + operation_description="Envoie le dossier d'inscription par e-mail", + operation_summary="Envoyer un dossier d'inscription" +) +@api_view(['GET']) +def send(request,id): + """Envoie le dossier d'inscription par e-mail.""" + register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) + if register_form != None: + student = register_form.student + guardian = student.getMainGuardian() + email = guardian.profile_role.profile.email + errorMessage = mailer.sendRegisterForm(email, register_form.establishment.pk) + if errorMessage == '': + register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') + updateStateMachine(register_form, 'EVENT_SEND') + return JsonResponse({"message": f"Le dossier d'inscription a bien été envoyé à l'addresse {email}"}, safe=False) + return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST) + return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + +@swagger_auto_schema( + method='get', + responses={200: openapi.Response('Success', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING) + } + ))}, + operation_description="Archive le dossier d'inscription", + operation_summary="Archiver un dossier d'inscription" +) +@api_view(['GET']) +def archive(request,id): + """Archive le dossier d'inscription.""" + register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) + if register_form != None: + register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') + updateStateMachine(register_form, 'EVENT_ARCHIVE') + return JsonResponse({"message": "Le dossier a été archivé avec succès"}, safe=False) + return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + +@swagger_auto_schema( + method='get', + responses={200: openapi.Response('Success', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING) + } + ))}, + operation_description="Relance un dossier d'inscription par e-mail", + operation_summary="Relancer un dossier d'inscription" +) +@api_view(['GET']) +def resend(request,id): + """Relance un dossier d'inscription par e-mail.""" + register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id) + if register_form != None: + student = register_form.student + guardian = student.getMainGuardian() + email = guardian.email + errorMessage = mailer.envoieRelanceDossierInscription(email, register_form.codeLienInscription) + if errorMessage == '': + register_form.status=RegistrationForm.RegistrationFormStatus.RF_SENT + register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M') + register_form.save() + return JsonResponse({"message": f"Le dossier a été renvoyé à l'adresse {email}"}, safe=False) + return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST) + return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + +@swagger_auto_schema( + method='get', + responses={200: openapi.Response('Success', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING) + } + ))}, + operation_description="Récupère les fichiers à signer d'un dossier d'inscription donné", + operation_summary="Récupérer les fichiers à signer d'un dossier d'inscription donné" +) +@api_view(['GET']) +def get_school_file_templates_by_rf(request, id): + try: + # Récupérer les templates associés au RegistrationForm donné + templates = RegistrationSchoolFileTemplate.objects.filter(registration_form=id) + + # Sérialiser les données + serializer = RegistrationSchoolFileTemplateSerializer(templates, many=True) + + # Retourner les données sérialisées + return JsonResponse(serializer.data, safe=False) + except RegistrationSchoolFileTemplate.DoesNotExist: + return JsonResponse({'error': 'Aucun template trouvé pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND) + +@swagger_auto_schema( + method='get', + responses={200: openapi.Response('Success', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING) + } + ))}, + operation_description="Récupère les pièces à fournir d'un dossier d'inscription donné", + operation_summary="Récupérer les pièces à fournir d'un dossier d'inscription donné" +) +@api_view(['GET']) +def get_parent_file_templates_by_rf(request, id): + try: + # Récupérer les pièces à fournir associés au RegistrationForm donné + parent_files = RegistrationParentFileTemplate.objects.filter(registration_form=id) + + # Sérialiser les données + serializer = RegistrationParentFileTemplateSerializer(parent_files, many=True) + + # Retourner les données sérialisées + return JsonResponse(serializer.data, safe=False) + except RegistrationParentFileTemplate.DoesNotExist: + return JsonResponse({'error': 'Aucune pièce à fournir trouvée pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND) diff --git a/Back-End/Subscriptions/views/registration_file_group_views.py b/Back-End/Subscriptions/views/registration_file_group_views.py new file mode 100644 index 0000000..c9c42d5 --- /dev/null +++ b/Back-End/Subscriptions/views/registration_file_group_views.py @@ -0,0 +1,131 @@ +from django.http.response import JsonResponse +from drf_yasg.utils import swagger_auto_schema +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status +from rest_framework.decorators import action, api_view +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi + +from Subscriptions.serializers import RegistrationFileGroupSerializer +from Subscriptions.models import RegistrationFileGroup, RegistrationSchoolFileMaster +from N3wtSchool import bdd + +class RegistrationFileGroupView(APIView): + @swagger_auto_schema( + operation_description="Récupère tous les groupes de fichiers d'inscription", + responses={200: RegistrationFileGroupSerializer(many=True)} + ) + def get(self, request): + """ + Récupère tous les groupes de fichiers d'inscription. + """ + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + groups = RegistrationFileGroup.objects.all() + if groups: + groups = groups.filter(establishment_id=establishment_id).distinct() + serializer = RegistrationFileGroupSerializer(groups, many=True) + return Response(serializer.data) + + @swagger_auto_schema( + operation_description="Crée un nouveau groupe de fichiers d'inscription", + request_body=RegistrationFileGroupSerializer, + responses={ + 201: RegistrationFileGroupSerializer, + 400: "Données invalides" + } + ) + def post(self, request): + """ + Crée un nouveau groupe de fichiers d'inscription. + """ + serializer = RegistrationFileGroupSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class RegistrationFileGroupSimpleView(APIView): + @swagger_auto_schema( + operation_description="Récupère un groupe de fichiers d'inscription spécifique", + responses={ + 200: RegistrationFileGroupSerializer, + 404: "Groupe non trouvé" + } + ) + def get(self, request, id): + """ + Récupère un groupe de fichiers d'inscription spécifique. + """ + group = bdd.getObject(_objectName=RegistrationFileGroup, _columnName='id', _value=id) + if group is None: + return JsonResponse({"errorMessage": "Le groupe de fichiers n'a pas été trouvé"}, + status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationFileGroupSerializer(group) + return JsonResponse(serializer.data) + + @swagger_auto_schema( + operation_description="Met à jour un groupe de fichiers d'inscription", + request_body=RegistrationFileGroupSerializer, + responses={ + 200: RegistrationFileGroupSerializer, + 400: "Données invalides", + 404: "Groupe non trouvé" + } + ) + def put(self, request, id): + """ + Met à jour un groupe de fichiers d'inscription existant. + """ + group = bdd.getObject(_objectName=RegistrationFileGroup, _columnName='id', _value=id) + if group is None: + return JsonResponse({'erreur': "Le groupe de fichiers n'a pas été trouvé"}, + status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationFileGroupSerializer(group, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + @swagger_auto_schema( + operation_description="Supprime un groupe de fichiers d'inscription", + responses={ + 204: "Suppression réussie", + 404: "Groupe non trouvé" + } + ) + def delete(self, request, id): + """ + Supprime un groupe de fichiers d'inscription. + """ + group = bdd.getObject(_objectName=RegistrationFileGroup, _columnName='id', _value=id) + if group is not None: + group.delete() + return JsonResponse({'message': 'La suppression du groupe a été effectuée avec succès'}, + status=status.HTTP_204_NO_CONTENT) + return JsonResponse({'erreur': "Le groupe de fichiers n'a pas été trouvé"}, + status=status.HTTP_404_NOT_FOUND) + +@swagger_auto_schema( + method='get', + responses={200: openapi.Response('Success', schema=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'message': openapi.Schema(type=openapi.TYPE_STRING) + } + ))}, + operation_description="Récupère les fichiers d'inscription d'un groupe donné", + operation_summary="Récupèrer les fichiers d'inscription d'un groupe donné" +) +@api_view(['GET']) +def get_registration_files_by_group(request, id): + try: + group = RegistrationFileGroup.objects.get(id=id) + templateMasters = RegistrationSchoolFileMaster.objects.filter(groups=group) + templates_data = list(templateMasters.values()) + return JsonResponse(templates_data, safe=False) + except RegistrationFileGroup.DoesNotExist: + return JsonResponse({'error': 'Le groupe de fichiers n\'a pas été trouvé'}, status=404) \ No newline at end of file diff --git a/Back-End/Subscriptions/views/registration_file_views.py b/Back-End/Subscriptions/views/registration_file_views.py new file mode 100644 index 0000000..15f22bb --- /dev/null +++ b/Back-End/Subscriptions/views/registration_file_views.py @@ -0,0 +1,372 @@ +from django.http.response import JsonResponse +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from rest_framework.parsers import MultiPartParser, FormParser +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status + +from Subscriptions.serializers import RegistrationSchoolFileMasterSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileMasterSerializer, RegistrationParentFileTemplateSerializer +from Subscriptions.models import RegistrationSchoolFileMaster, RegistrationSchoolFileTemplate, RegistrationParentFileMaster, RegistrationParentFileTemplate +from N3wtSchool import bdd + +class RegistrationSchoolFileMasterView(APIView): + @swagger_auto_schema( + operation_description="Récupère tous les masters de templates d'inscription pour un établissement donné", + manual_parameters=[ + openapi.Parameter( + 'establishment_id', + openapi.IN_QUERY, + description="ID de l'établissement", + type=openapi.TYPE_INTEGER, + required=True + ) + ], + responses={200: RegistrationSchoolFileMasterSerializer(many=True)} + ) + def get(self, request): + establishment_id = request.GET.get('establishment_id') + if not establishment_id: + return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST) + + # Filtrer les masters liés à l'établissement via groups.establishment + masters = RegistrationSchoolFileMaster.objects.filter( + groups__establishment__id=establishment_id + ).distinct() + serializer = RegistrationSchoolFileMasterSerializer(masters, many=True) + return Response(serializer.data) + + @swagger_auto_schema( + operation_description="Crée un nouveau master de template d'inscription", + request_body=RegistrationSchoolFileMasterSerializer, + responses={ + 201: RegistrationSchoolFileMasterSerializer, + 400: "Données invalides" + } + ) + def post(self, request): + serializer = RegistrationSchoolFileMasterSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class RegistrationSchoolFileMasterSimpleView(APIView): + @swagger_auto_schema( + operation_description="Récupère un master de template d'inscription spécifique", + responses={ + 200: RegistrationSchoolFileMasterSerializer, + 404: "Master non trouvé" + } + ) + def get(self, request, id): + master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id) + if master is None: + return JsonResponse({"errorMessage":'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationSchoolFileMasterSerializer(master) + return JsonResponse(serializer.data, safe=False) + + @swagger_auto_schema( + operation_description="Met à jour un master de template d'inscription existant", + request_body=RegistrationSchoolFileMasterSerializer, + responses={ + 200: RegistrationSchoolFileMasterSerializer, + 400: "Données invalides", + 404: "Master non trouvé" + } + ) + def put(self, request, id): + master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id) + if master is None: + return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationSchoolFileMasterSerializer(master, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + @swagger_auto_schema( + operation_description="Supprime un master de template d'inscription", + responses={ + 204: "Suppression réussie", + 404: "Master non trouvé" + } + ) + def delete(self, request, id): + master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id) + if master is not None: + master.delete() + return JsonResponse({'message': 'La suppression du master de template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT) + else: + return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + +class RegistrationSchoolFileTemplateView(APIView): + @swagger_auto_schema( + operation_description="Récupère tous les templates d'inscription pour un établissement donné", + manual_parameters=[ + openapi.Parameter( + 'establishment_id', + openapi.IN_QUERY, + description="ID de l'établissement", + type=openapi.TYPE_INTEGER, + required=True + ) + ], + responses={200: RegistrationSchoolFileTemplateSerializer(many=True)} + ) + def get(self, request): + establishment_id = request.GET.get('establishment_id') + if not establishment_id: + return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST) + + # Filtrer les templates liés à l'établissement via master.groups.establishment + templates = RegistrationSchoolFileTemplate.objects.filter( + master__groups__establishment__id=establishment_id + ).distinct() + serializer = RegistrationSchoolFileTemplateSerializer(templates, many=True) + return Response(serializer.data) + + @swagger_auto_schema( + operation_description="Crée un nouveau template d'inscription", + request_body=RegistrationSchoolFileTemplateSerializer, + responses={ + 201: RegistrationSchoolFileTemplateSerializer, + 400: "Données invalides" + } + ) + def post(self, request): + serializer = RegistrationSchoolFileTemplateSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class RegistrationSchoolFileTemplateSimpleView(APIView): + @swagger_auto_schema( + operation_description="Récupère un template d'inscription spécifique", + responses={ + 200: RegistrationSchoolFileTemplateSerializer, + 404: "Template non trouvé" + } + ) + def get(self, request, id): + template = bdd.getObject(_objectName=RegistrationSchoolFileTemplate, _columnName='id', _value=id) + if template is None: + return JsonResponse({"errorMessage":'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationSchoolFileTemplateSerializer(template) + return JsonResponse(serializer.data, safe=False) + + @swagger_auto_schema( + operation_description="Met à jour un template d'inscription existant", + request_body=RegistrationSchoolFileTemplateSerializer, + responses={ + 200: RegistrationSchoolFileTemplateSerializer, + 400: "Données invalides", + 404: "Template non trouvé" + } + ) + def put(self, request, id): + template = bdd.getObject(_objectName=RegistrationSchoolFileTemplate, _columnName='id', _value=id) + if template is None: + return JsonResponse({'erreur': 'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationSchoolFileTemplateSerializer(template, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response({'message': 'Template mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + @swagger_auto_schema( + operation_description="Supprime un template d'inscription", + responses={ + 204: "Suppression réussie", + 404: "Template non trouvé" + } + ) + def delete(self, request, id): + template = bdd.getObject(_objectName=RegistrationSchoolFileTemplate, _columnName='id', _value=id) + if template is not None: + template.delete() + return JsonResponse({'message': 'La suppression du template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT) + else: + return JsonResponse({'erreur': 'Le template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + +class RegistrationParentFileMasterView(APIView): + @swagger_auto_schema( + operation_description="Récupère tous les fichiers parents pour un établissement donné", + manual_parameters=[ + openapi.Parameter( + 'establishment_id', + openapi.IN_QUERY, + description="ID de l'établissement", + type=openapi.TYPE_INTEGER, + required=True + ) + ], + responses={200: RegistrationParentFileMasterSerializer(many=True)} + ) + def get(self, request): + establishment_id = request.GET.get('establishment_id') + if not establishment_id: + return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST) + + # Filtrer les fichiers parents liés à l'établissement + templates = RegistrationParentFileMaster.objects.filter( + groups__establishment__id=establishment_id + ).distinct() + serializer = RegistrationParentFileMasterSerializer(templates, many=True) + return Response(serializer.data) + + @swagger_auto_schema( + operation_description="Crée un nouveau fichier parent", + request_body=RegistrationParentFileMasterSerializer, + responses={ + 201: RegistrationParentFileMasterSerializer, + 400: "Données invalides" + } + ) + def post(self, request): + serializer = RegistrationParentFileMasterSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class RegistrationParentFileMasterSimpleView(APIView): + @swagger_auto_schema( + operation_description="Récupère un fichier parent spécifique", + responses={ + 200: RegistrationParentFileMasterSerializer, + 404: "Fichier parent non trouvé" + } + ) + def get(self, request, id): + template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id) + if template is None: + return JsonResponse({"errorMessage":'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationParentFileMasterSerializer(template) + return JsonResponse(serializer.data, safe=False) + + @swagger_auto_schema( + operation_description="Met à jour un fichier parent existant", + request_body=RegistrationParentFileMasterSerializer, + responses={ + 200: RegistrationParentFileMasterSerializer, + 400: "Données invalides", + 404: "Fichier parent non trouvé" + } + ) + def put(self, request, id): + template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id) + if template is None: + return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationParentFileMasterSerializer(template, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response({'message': 'Fichier parent mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + @swagger_auto_schema( + operation_description="Supprime un fichier parent", + responses={ + 204: "Suppression réussie", + 404: "Fichier parent non trouvé" + } + ) + def delete(self, request, id): + template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id) + if template is not None: + template.delete() + return JsonResponse({'message': 'La suppression du fichier parent a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT) + else: + return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + +class RegistrationParentFileTemplateView(APIView): + @swagger_auto_schema( + operation_description="Récupère tous les templates parents pour un établissement donné", + manual_parameters=[ + openapi.Parameter( + 'establishment_id', + openapi.IN_QUERY, + description="ID de l'établissement", + type=openapi.TYPE_INTEGER, + required=True + ) + ], + responses={200: RegistrationParentFileTemplateSerializer(many=True)} + ) + def get(self, request): + establishment_id = request.GET.get('establishment_id') + if not establishment_id: + return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST) + + # Filtrer les templates parents liés à l'établissement via master.groups.establishment + templates = RegistrationParentFileTemplate.objects.filter( + master__groups__establishment__id=establishment_id + ).distinct() + serializer = RegistrationParentFileTemplateSerializer(templates, many=True) + return Response(serializer.data) + + @swagger_auto_schema( + operation_description="Crée un nouveau template d'inscription", + request_body=RegistrationParentFileTemplateSerializer, + responses={ + 201: RegistrationParentFileTemplateSerializer, + 400: "Données invalides" + } + ) + def post(self, request): + serializer = RegistrationParentFileTemplateSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class RegistrationParentFileTemplateSimpleView(APIView): + @swagger_auto_schema( + operation_description="Récupère un template d'inscription spécifique", + responses={ + 200: RegistrationParentFileTemplateSerializer, + 404: "Template non trouvé" + } + ) + def get(self, request, id): + template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id) + if template is None: + return JsonResponse({"errorMessage":'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + serializer = RegistrationParentFileTemplateSerializer(template) + return JsonResponse(serializer.data, safe=False) + + @swagger_auto_schema( + operation_description="Met à jour un template d'inscription existant", + request_body=RegistrationParentFileTemplateSerializer, + responses={ + 200: RegistrationParentFileTemplateSerializer, + 400: "Données invalides", + 404: "Template non trouvé" + } + ) + def put(self, request, id): + template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id) + if template is None: + return JsonResponse({'erreur': 'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + + serializer = RegistrationParentFileTemplateSerializer(template, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + return Response({'message': 'Template mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + @swagger_auto_schema( + operation_description="Supprime un template d'inscription", + responses={ + 204: "Suppression réussie", + 404: "Template non trouvé" + } + ) + def delete(self, request, id): + template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id) + if template is not None: + template.delete() + return JsonResponse({'message': 'La suppression du template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT) + else: + return JsonResponse({'erreur': 'Le template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) diff --git a/Back-End/Subscriptions/views/student_competencies_views.py b/Back-End/Subscriptions/views/student_competencies_views.py new file mode 100644 index 0000000..b803249 --- /dev/null +++ b/Back-End/Subscriptions/views/student_competencies_views.py @@ -0,0 +1,220 @@ +from django.http.response import JsonResponse +from rest_framework.views import APIView +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema +from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from django.utils.decorators import method_decorator +from Subscriptions.models import StudentCompetency, Student +from Common.models import Domain +from Subscriptions.models import BilanCompetence +from datetime import date +from N3wtSchool.renderers import render_to_pdf +from django.core.files import File +from io import BytesIO +import os +import logging +from django.conf import settings + +logger = logging.getLogger(__name__) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class StudentCompetencyListCreateView(APIView): + def get(self, request): + student_id = request.GET.get('student_id') + period = request.GET.get('period') + if not student_id: + return JsonResponse({'error': 'student_id requis'}, status=400) + try: + student = Student.objects.get(id=student_id) + except Student.DoesNotExist: + return JsonResponse({'error': 'Élève introuvable'}, status=404) + + # Filtrer par student ET period si period est fourni + filter_kwargs = {'student': student} + if period: + filter_kwargs['period'] = period + + student_competencies = StudentCompetency.objects.filter(**filter_kwargs).select_related( + 'establishment_competency', + 'establishment_competency__competency', + 'establishment_competency__competency__category', + 'establishment_competency__competency__category__domain', + 'establishment_competency__custom_category', + 'establishment_competency__custom_category__domain', + ) + + result = [] + total_competencies = 0 + domaines = Domain.objects.all() + for domaine in domaines: + domaine_dict = { + "domaine_id": domaine.id, + "domaine_nom": domaine.name, + "categories": [] + } + categories = domaine.categories.all() + for categorie in categories: + categorie_dict = { + "categorie_id": categorie.id, + "categorie_nom": categorie.name, + "competences": [] + } + # On ne boucle que sur les compétences du student pour cette catégorie + for sc in student_competencies: + ec = sc.establishment_competency + # Cas compétence de référence + if ec.competency and ec.competency.category_id == categorie.id: + comp = ec.competency + categorie_dict["competences"].append({ + "competence_id": ec.id, # <-- retourne l'id de l'EstablishmentCompetency + "nom": comp.name, + "score": sc.score, + "comment": sc.comment or "", + "period":sc.period or "" + }) + total_competencies += 1 + # Cas compétence custom + elif ec.competency is None and ec.custom_category_id == categorie.id: + categorie_dict["competences"].append({ + "competence_id": ec.id, # <-- retourne l'id de l'EstablishmentCompetency + "nom": ec.custom_name, + "score": sc.score, + "comment": sc.comment or "", + "period":sc.period or "" + }) + total_competencies += 1 + if categorie_dict["competences"]: + domaine_dict["categories"].append(categorie_dict) + if domaine_dict["categories"]: + result.append(domaine_dict) + + return JsonResponse({ + "count": total_competencies, + "data": result + }, safe=False, status=200) + + def put(self, request): + """ + Met à jour en masse les notes des compétences d'un élève. + Attend une liste d'objets {"competenceId": ..., "grade": ...} + """ + data = request.data + if not isinstance(data, list): + return JsonResponse({"error": "Une liste est attendue."}, status=400) + updated = [] + errors = [] + updated_competency_ids = set() + for item in data: + comp_id = item.get("competenceId") + grade = item.get("grade") + student_id = item.get('studentId') + period = item.get('period') + if comp_id is None or grade is None: + errors.append({"competenceId": comp_id, "error": "champ manquant"}) + continue + try: + sc = StudentCompetency.objects.get( + establishment_competency_id=comp_id, + student_id=student_id, + period=period + ) + sc.score = grade + sc.save() + updated.append(comp_id) + updated_competency_ids.add(sc.id) + except StudentCompetency.DoesNotExist: + errors.append({"competenceId": comp_id, "error": "not found"}) + + # Génération du PDF si au moins une compétence a été mise à jour + if updated: + student = Student.objects.get(id=student_id) + # On ne prend que les StudentCompetency mis à jour pour la génération du PDF + student_competencies = StudentCompetency.objects.filter( + student=student, + period=period, + id__in=updated_competency_ids + ).select_related( + 'establishment_competency', + 'establishment_competency__competency', + 'establishment_competency__competency__category', + 'establishment_competency__competency__category__domain', + 'establishment_competency__custom_category', + 'establishment_competency__custom_category__domain', + ) + + result = [] + domaines = Domain.objects.all() + for domaine in domaines: + domaine_dict = { + "nom": domaine.name, + "categories": [] + } + categories = domaine.categories.all() + for categorie in categories: + categorie_dict = { + "nom": categorie.name, + "competences": [] + } + for sc in student_competencies: + ec = sc.establishment_competency + if ec.competency and ec.competency.category_id == categorie.id: + comp = ec.competency + categorie_dict["competences"].append({ + "nom": comp.name, + "score": sc.score, + "comment": sc.comment or "", + }) + elif ec.competency is None and ec.custom_category_id == categorie.id: + categorie_dict["competences"].append({ + "nom": ec.custom_name, + "score": sc.score, + "comment": sc.comment or "", + }) + if categorie_dict["competences"]: + domaine_dict["categories"].append(categorie_dict) + if domaine_dict["categories"]: + result.append(domaine_dict) + + context = { + "student": { + "first_name": student.first_name, + "last_name": student.last_name, + "level": student.level, + "class_name": student.associated_class.atmosphere_name, + }, + "period": period, + "date": date.today().strftime("%d/%m/%Y"), + "domaines": result, + } + + pdf_result = render_to_pdf('pdfs/bilan_competences.html', context) + + try: + filename = f"bilan_competences_{student.last_name}_{student.first_name}_{period}.pdf" + for existing_bilan in BilanCompetence.objects.filter(student=student, period=period): + if existing_bilan.file and existing_bilan.file.name: + file_path = existing_bilan.file.path + if os.path.exists(file_path): + os.remove(file_path) + existing_bilan.delete() + + bilan = BilanCompetence.objects.create( + student=student, + period=period + ) + bilan.file.save( + os.path.basename(filename), + File(BytesIO(pdf_result.content)), + save=True + ) + except Exception as e: + logger.error(f"Erreur lors de la sauvegarde du fichier PDF : {e}") + raise + return JsonResponse({"updated": updated, "errors": errors}, status=200) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class StudentCompetencySimpleView(APIView): + def get(self, request, id): + return JsonResponse("ok", safe=False, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/Back-End/Subscriptions/views/student_views.py b/Back-End/Subscriptions/views/student_views.py new file mode 100644 index 0000000..6a43ba9 --- /dev/null +++ b/Back-End/Subscriptions/views/student_views.py @@ -0,0 +1,151 @@ +from django.http.response import JsonResponse +from rest_framework.views import APIView +from rest_framework import status +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from django.db.models import Q + +from Subscriptions.serializers import StudentByRFCreationSerializer, RegistrationFormByParentSerializer, StudentSerializer +from Subscriptions.models import Student, RegistrationForm + +from N3wtSchool import bdd + +class StudentView(APIView): + """ + Gère la lecture d’un élève donné. + """ + @swagger_auto_schema( + operation_summary="Récupérer les informations d'un élève", + operation_description="Retourne les détails d'un élève spécifique à partir de son ID", + responses={ + 200: openapi.Response('Détails de l\'élève', StudentSerializer), + 404: openapi.Response('Élève non trouvé') + }, + manual_parameters=[ + openapi.Parameter( + 'id', openapi.IN_PATH, + description="ID de l'élève", + type=openapi.TYPE_INTEGER, + required=True + ) + ] + ) + def get(self, request, id): + student = bdd.getObject(_objectName=Student, _columnName='id', _value=id) + if student is None: + return JsonResponse({"errorMessage":'Aucun élève trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND) + student_serializer = StudentSerializer(student) + return JsonResponse(student_serializer.data, safe=False) + +# API utilisée pour la vue de création d'un DI +class StudentListView(APIView): + """ + Pour la vue de création d’un dossier d’inscription : liste les élèves disponibles. + """ + @swagger_auto_schema( + operation_summary="Lister tous les élèves", + operation_description="Retourne la liste de tous les élèves inscrits ou en cours d'inscription", + responses={ + 200: openapi.Response('Liste des élèves', StudentByRFCreationSerializer(many=True)) + }, + manual_parameters=[ + openapi.Parameter( + 'establishment_id', openapi.IN_QUERY, + description="ID de l'établissement", + type=openapi.TYPE_INTEGER, + required=True + ) + ] + ) + # Récupération de la liste des élèves inscrits ou en cours d'inscriptions + def get(self, request): + establishment_id = request.GET.get('establishment_id', None) + status_filter = request.GET.get('status', None) # Nouveau filtre optionnel + + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + students_qs = Student.objects.filter(registrationform__establishment_id=establishment_id) + + if status_filter: + students_qs = students_qs.filter(registrationform__status=status_filter) + + students_qs = students_qs.distinct() + students_serializer = StudentByRFCreationSerializer(students_qs, many=True) + return JsonResponse(students_serializer.data, safe=False) + + +# API utilisée pour la vue parent +class ChildrenListView(APIView): + """ + Pour la vue parent : liste les élèves rattachés à un profil donné. + """ + @swagger_auto_schema( + operation_summary="Lister les élèves d'un parent", + operation_description="Retourne la liste des élèves associés à un profil parent spécifique", + responses={ + 200: openapi.Response('Liste des élèves du parent', RegistrationFormByParentSerializer(many=True)) + }, + manual_parameters=[ + openapi.Parameter( + 'id', openapi.IN_PATH, + description="ID du profil parent", + type=openapi.TYPE_INTEGER, + required=True + ) + ] + ) + # Récupération des élèves d'un parent + # idProfile : identifiant du profil connecté rattaché aux fiches d'élèves + def get(self, request, id): + establishment_id = request.GET.get('establishment_id', None) + if establishment_id is None: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__profile_role__profile__id', _value=id) + if students: + students = students.filter( + establishment=establishment_id, + status__in=[ + RegistrationForm.RegistrationFormStatus.RF_SENT, + RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW, + RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT, + RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND, + RegistrationForm.RegistrationFormStatus.RF_VALIDATED + ] + ).distinct() + students_serializer = RegistrationFormByParentSerializer(students, many=True) + return JsonResponse(students_serializer.data, safe=False) + +def search_students(request): + """ + API pour rechercher des étudiants en fonction d'un terme de recherche (nom/prénom) et d'un établissement. + """ + query = request.GET.get('q', '').strip() + establishment_id = request.GET.get('establishment_id', None) + + if not query: + return JsonResponse([], safe=False) + + if not establishment_id: + return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST) + + # Recherche sur Student (nom ou prénom) et filtrage par établissement via RegistrationForm + students = Student.objects.filter( + Q(last_name__icontains=query) | Q(first_name__icontains=query), + registrationform__establishment_id=establishment_id + ).distinct() + + results = [ + { + 'id': student.id, + 'first_name': student.first_name, + 'last_name': student.last_name, + 'level': getattr(student.level, 'name', ''), + 'associated_class_name': student.associated_class.atmosphere_name if student.associated_class else '', + 'photo': student.photo.url if student.photo else None + } + for student in students + ] + + return JsonResponse(results, safe=False) \ No newline at end of file diff --git a/Back-End/__version__.py b/Back-End/__version__.py index f102a9c..3b93d0b 100644 --- a/Back-End/__version__.py +++ b/Back-End/__version__.py @@ -1 +1 @@ -__version__ = "0.0.1" +__version__ = "0.0.2" diff --git a/Back-End/clean_migration_dir.py b/Back-End/clean_migration_dir.py new file mode 100644 index 0000000..c097142 --- /dev/null +++ b/Back-End/clean_migration_dir.py @@ -0,0 +1,27 @@ +import os +import shutil + +APPS = [ + "Establishment", + "Settings", + "Subscriptions", + "Planning", + "GestionNotification", + "GestionMessagerie", + "GestionEmail", + "Auth", + "School", + "Common" +] + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + +for app in APPS: + migrations_path = os.path.join(BASE_DIR, app, "migrations") + if os.path.isdir(migrations_path): + print(f"Suppression du dossier : {migrations_path}") + shutil.rmtree(migrations_path) + else: + print(f"Aucun dossier de migration trouvé pour {app}") + +print("Nettoyage terminé.") \ No newline at end of file diff --git a/Back-End/competences/Cycle1.json b/Back-End/competences/Cycle1.json new file mode 100644 index 0000000..5d34443 --- /dev/null +++ b/Back-End/competences/Cycle1.json @@ -0,0 +1,1415 @@ +{ + "domaines": [ + { + "nom": "Mobiliser le langage dans toutes ses dimensions", + "categories": [ + { + "nom": "Oser entrer en communication", + "competences": [ + { + "nom": "Communiquer avec les adultes et les autres enfants par le langage en se faisant comprendre.", + "fin_cycle": true + }, + { + "nom": "Dire de mémoire et de manière expressive plusieurs comptines et poésies.", + "fin_cycle": true + }, + { + "nom": "Participer en répétant seulement (comptines…)." + }, + { + "nom": "Participer à la tâche langagière en répétant les paroles d'un pair." + }, + { + "nom": "Prendre la parole pour répondre à une question." + }, + { + "nom": "Quitter le regard de l'enseignant pour regarder son (ses) interlocuteur(s) ; parler pour être entendu (force et articulation)." + }, + { + "nom": "Endosser des postures de locuteur/interlocuteur : accepter les tours de parole, attendre pour prendre la parole ; écouter ses pairs." + }, + { + "nom": "Prendre en compte son (ses) interlocuteur (s) dans le système d'énonciation (je, tu, il/elle, vous, nous …)." + }, + { + "nom": "Répéter, insister, transformer, adapter, reformuler son propos pour être entendu et compris." + }, + { + "nom": "Participer à la régulation de l'avancée du propos du groupe pardes formules comme « 0n l'a déjà dit … »." + }, + { + "nom": "Coopérer en complétant, en ajoutant des éléments, en s'opposant aux propos de ses pairs." + }, + { + "nom": "Faire usage de son statut de locuteur/interlocuteur à l'intérieur du groupe par des régulations verbales des prises de paroles accordées." + }, + { + "nom": "Récapituler ce qui vient d'être dit à la demande de l'enseignant." + }, + { + "nom": "Se distancier en comprenant et commençant à faire de l'humour." + } + ] + }, + { + "nom": "Comprendre et apprendre", + "competences": [ + { + "nom": "Pratiquer divers usages de la langue orale : raconter, décrire, évoquer, expliquer, questionner, proposer des solutions, discuter un point de vue.", + "fin_cycle": true + }, + { + "nom": "Expliquer comment il opère en situation de réalisation en énonçant quelques mots clés décrivant son action et/ou les manières." + }, + { + "nom": "Expliquer comment il opère en situation de réalisation en décrivant chacune des actions." + }, + { + "nom": "Expliquer comment réaliser quelque chose après l'avoir effectué en s'appuyant sur des traces de l'activité (productions, maquette, photos, dessins…) en listant des actions et/ou des manières de faire." + }, + { + "nom": "Expliquer comment réaliser quelque chose après l'avoir effectué en s'appuyant sur des traces de l'activité (productions, maquette, photos, dessins…) en enchaînant le déroulement des actions." + }, + { + "nom": "Interpréter son activité au vu du résultat produit." + }, + { + "nom": "Interpréter une réussite/ un échec en expliquant les causes ou en expliquant les conséquences d'une activité, de l'utilisation d'un outil." + }, + { + "nom": "Anticiper le résultat d'une action, d'un geste, d'une procédure." + }, + { + "nom": "Décrire en faisant la liste d'éléments constitutifs ; matériaux, matériels, propriétés, qualités…." + }, + { + "nom": "Relater une succession d'événements organisés." + }, + { + "nom": "Relater une succession d'actions pour décrire un parcours, une procédure, une technique…." + }, + { + "nom": "Relater une succession de lieux pour décrire un itinéraire, un déplacement." + }, + { + "nom": "Rapprocher par les points communs ou contraster par les points de différences …." + }, + { + "nom": "Situer les uns par rapport aux autres les éléments composant une image, un motif, un objet pour donner à voir (construction d'une image mentale) en s'appuyant sur des photos, des dessins, des schématisations." + }, + { + "nom": "Décrire pour anticiper une installation, une organisation…." + }, + { + "nom": "Raconter des actions vécues par le personnage central en manipulant le matériel à disposition : marottes + décor ou en tournant les pages de l'album." + }, + { + "nom": "Raconter en faisant parler les personnages en utilisant des marottes." + }, + { + "nom": "Alterner récit et dialogues en introduisant correctement les dialogues et nommant la personne/personnage qu'il fait parler." + }, + { + "nom": "Enchainer les actions et les émotions des personnages pour retracer tout le scénario et l'intrigue de l'histoire." + }, + { + "nom": "Utiliser le système des temps approprié : imparfait, passé simple (il prendit, il prenda …) dans le récit." + }, + { + "nom": "Enchaîner judicieusement les phrases avec des connecteurs adaptés et variés (les noter)." + }, + { + "nom": "Raconter une histoire en randonnée en inventant un nouvel épisode crédible à partir d'un nouveau personnage, ou d'un nouvel élément." + }, + { + "nom": "Inventer une histoire à partir de quelques éléments (images/ objets…) ou à partir des illustrations d'un album non connu." + }, + { + "nom": "Nommer des objets, du matériel, des matériaux, des personnes, des rôles." + }, + { + "nom": "Nommer des actions, des gestes." + }, + { + "nom": "Nommer des propriétés, des qualités, des effets produits." + }, + { + "nom": "Nommer des relations spatiales, temporelles et logiques." + }, + { + "nom": "Lister, énumérer les éléments caractéristiques de formes, d'objets…." + }, + { + "nom": "Situer les uns par rapport aux autres les éléments composant une image, un objet ... pour donner à voir (construction d'une image mentale)." + }, + { + "nom": "Situer dans l'espace personnes, objets et actions." + }, + { + "nom": "Situer dans le temps les actions, les évolutions." + }, + { + "nom": "Orienter un trajet dans l'espace." + }, + { + "nom": "Rapprocher par les points communs (éléments ou caractéristiques)." + }, + { + "nom": "Contraster par les points de différences (éléments ou caractéristiques)." + }, + { + "nom": "Apprécier des écarts (plus/moins /le meilleur /le pire ...)." + }, + { + "nom": "Classer, catégoriser en utilisant des termes génériques (fruits, légumes, véhicules …)." + }, + { + "nom": "Opposer des caractéristiques par l'utilisation de mots contraires." + }, + { + "nom": "Justifier un choix, une décision, une action, un comportement …." + }, + { + "nom": "Expliquer un déroulement." + }, + { + "nom": "Dire les manières de…." + }, + { + "nom": "Expliquer les causes, les conséquences, la condition." + }, + { + "nom": "Dire les procédures pour faire, pour jouer." + }, + { + "nom": "Prévoir une installation, une organisation." + }, + { + "nom": "Prévoir une succession d'actions, d'activités." + }, + { + "nom": "Prévoir des résultats, des effets, des évènements, des actions, des réactions." + }, + { + "nom": "Prévoir une procédure de réalisation." + }, + { + "nom": "A partir de sa propre expérience ou de son savoir, évoquer une émotion, un fait, une sensation, une action à partir d'œuvres musicales ou visuelles, de danses, de jeu de mimes." + }, + { + "nom": "A partir de sa propre expérience ou de son savoir, justifier la reconnaissance d'une émotion/sensation dans un mouvement, une mélodie, une représentation." + }, + { + "nom": "A partir de sa propre expérience ou sde son savoir, modifier légèrement une règle, une histoire, un rôle." + }, + { + "nom": "A partir de sa propre expérience ou de son savoir, imaginer les raisons de…." + } + ] + }, + { + "nom": "Échanger et réfléchir avec les autres", + "competences": [ + { + "nom": "M'exprimer dans un langage oral syntaxiquement correct et précis.", + "fin_cycle": true + }, + { + "nom": "Utiliser le lexique appris en classe de façon appropriée.", + "fin_cycle": true + }, + { + "nom": "Reformuler mon propos pour me faire mieux comprendre.", + "fin_cycle": true + }, + { + "nom": "Reformuler le propos d'autrui.", + "fin_cycle": true + }, + { + "nom": "Utiliser des « mots phrases »." + }, + { + "nom": "Juxtaposer deux mots pour se faire comprendre." + }, + { + "nom": "élaborer des phrases avec un groupe nominal simple (i pour il) et un groupe verbal simple ou dans des structures simples : il faut, c'est …." + }, + { + "nom": "élaborer des phrases déclaratives simples autour d'un groupe nominal et d'un groupe verbal." + }, + { + "nom": "élaborer des phrases plus longues avec expansion du groupe verbal : COD / COI." + }, + { + "nom": "élaborer des phrases plus longues avec expansion du groupe nominal : adjectif / relative/ complément du nom." + }, + { + "nom": "élaborer des phrases plus longues avec GN + GV + complément de phrases : compléments circonstanciels (temps, lieu, cause…)." + }, + { + "nom": "élaborer des phrases complexes avec propositions subordonnées." + }, + { + "nom": "S'appuyer beaucoup sur des verbes très fréquents (dire, faire, mettre, aller, prendre, avoir, être…) et des pronoms pour s'exprimer." + }, + { + "nom": "S'emparer du vocabulaire donné en classe et l'utiliser à bon escient dans les tâches langagières." + }, + { + "nom": "Corriger, reprendre son propos pour remplacer un mot par un autre plus précis, plus expert." + }, + { + "nom": "Employer un vocabulaire de base (vie quotidienne à l'école) suffisamment développé pour être précis dans ses prises de parole et dans les activités ordinaires de la classe." + }, + { + "nom": "Réutiliser dans un autre contexte les mots appris dans un certains contexte, en classe." + }, + { + "nom": "Utiliser régulièrement des adjectifs et des adverbes pour spécifier son propos." + }, + { + "nom": "Utiliser des connecteurs logiques, temporels." + } + ] + }, + { + "nom": "Commencer à réfléchir sur la langue et acquérir une conscience phonologique", + "competences": [ + { + "nom": "Repérer des régularités dans la langue à l'oral en français (éventuellement dans une autre langue).", + "fin_cycle": true + }, + { + "nom": "Discriminer des sons (syllabes, sons-voyelles, quelques sons-consonnes) dans des mots ou des syllabes.", + "fin_cycle": true + }, + { + "nom": "Distinguer et manipuler des syllabes : scander les syllabes constitutives d'un mot, comprendre qu'on peut en supprimer, en ajouter, en inverser.", + "fin_cycle": true + }, + { + "nom": "Repérer et produire des rimes, des assonances.", + "fin_cycle": true + }, + { + "nom": "Synchroniser le débit de la comptine ou jeu de doigts récité avec la gestuelle associée." + }, + { + "nom": "Réciter comptines et vire-langues en prêtant attention aux assonances, aux allitérations et à l'articulation en jeu." + }, + { + "nom": "Scander et dénombrer les syllabes phoniques d'un mot en respectant les variations régionales." + }, + { + "nom": "Reconnaître et discriminer une syllabe dans une liste de mots, dans un texte." + }, + { + "nom": "Trouver les mots pour produire de nouvelles rimes et assonances." + }, + { + "nom": "Pratiquer des opérations sur les syllabes de mots : enlever, ajouter, inverser, localiser, substituer (avec augmentation progressive de la longueur des mots à transformer)." + }, + { + "nom": "Produire des pseudo-mots par combinaison de syllabes." + }, + { + "nom": "Isoler et discriminer un phonème dont l'articulation peut être maintenue (voyelle, /s/, /f/, /z/ etc.)." + }, + { + "nom": "Localiser et coder la place d'un phonème dans le mot (première, deuxième… syllabe/ début, milieu ou fin de mot)." + }, + { + "nom": "Distinguer des sons proches (f/v, s/ch, s/z, ch/f etc…)." + }, + { + "nom": "Je découvre l'existence d'autres langues." + }, + { + "nom": "Je répète quelques mots d'une autre langue." + } + ] + }, + { + "nom": "Écouter de l'écrit et comprendre.", + "competences": [ + { + "nom": "Comprendre des textes écrits sans autre aide que le langage entendu.", + "fin_cycle": true + }, + { + "nom": "Réception de langage écrit pour en comprendre le contenu." + }, + { + "nom": "Montrer du plaisir à écouter des histoires." + }, + { + "nom": "Fréquenter spontanément et régulièrement l'espace lecture." + }, + { + "nom": "Solliciter l'adulte pour qu'il lui lise ou relise un livre." + }, + { + "nom": "S'insérer dans l'histoire au fil de la lecture par l'adulte : répéter, mimer, commenter, questionner." + }, + { + "nom": "Pointer sur l'image (illustration/photo) des éléments en lien avec le texte." + }, + { + "nom": "Identifier les éléments clés de l'histoire : personnage principal, personnages secondaires, actions, lieu…." + }, + { + "nom": "Identifier des informations susceptibles de répondre à un questionnement." + }, + { + "nom": "Replacer quelques scènes clés de l'histoire lue dans son scénario partiellement recomposé avec des images." + }, + { + "nom": "Trouver l'image qui illustre le début (nœud) et la fin (dénouement) de l'histoire et justifier son choix." + }, + { + "nom": "Identifier les émotions des personnages en prenant appui sur les mots du texte." + }, + { + "nom": "Prêter des intentions à des personnages archétypaux et en déduire des actions à venir dans l'histoire lue." + }, + { + "nom": "En cours de lecture, anticiper un déroulement d'actions dans des scénarios connus (la ruse, la colère, la peur du noir…)." + }, + { + "nom": "établir des liens entre des histoires lues (personnages, scénario)." + }, + { + "nom": "Reformuler l'histoire avec ses propres mots (avec ou sans outils/supports)." + }, + { + "nom": "Imaginer un autre épisode (avec autre personnage), une autre fin (si…)." + }, + { + "nom": "Dire ce qu'il aurait fait à la place de tel personnage à un moment clé de l'histoire." + }, + { + "nom": "Pouvoir redire les mots d'une phrase écrite après sa lecture par l'adulte, les mots du titre connu d'un livre ou d'un texte.", + "fin_cycle": true + } + ] + }, + { + "nom": "Découvrir la fonction de l'écrit", + "competences": [ + { + "nom": "Manifester de la curiosité par rapport à la compréhension et à la production d'écrit.", + "fin_cycle": true + }, + { + "nom": "S'intéresser : regarder, feuilleter les écrits présents dans la classe (prénoms, imagiers, cahier de vie…)." + }, + { + "nom": "Chercher des repères dans les caractéristiques du support." + }, + { + "nom": "émettre des hypothèses sur les écrits affichés dans la classe (prénoms, date…)." + }, + { + "nom": "Reconnaître des écrits utilisés fréquemment dans le quotidien de la classe." + }, + { + "nom": "Se référer spontanément aux écrits présents dans la classe (mots, répertoires de comptines, textes, cahiers de vie, de littérature, de sciences…) en vue d'une utilisation particulière." + }, + { + "nom": "Expliquer la fonction et les usages des écrits utilisés les plus fréquemment en classe." + }, + { + "nom": "Reconnaître des écrits utilisés moins fréquemment ou liés à un environnement élargi (l'école, la famille, le quartier)." + }, + { + "nom": "Différencier et catégoriser différents types de livres selon des critères de fonction (expliquer, raconter…), d'auteurs." + }, + { + "nom": "Différencier des types d'écrits et associer un écrit à un projet d'écriture ou de communication." + }, + { + "nom": "Proposer spontanément de recourir à l'écrit pour trouver ou transmettre une information." + } + ] + }, + { + "nom": "Commencer à produire des écrits et en découvrir le fonctionnement", + "competences": [ + { + "nom": "Participer verbalement à la production d'un écrit. Savoir que l'on n'écrit pas comme on parle.", + "fin_cycle": true + }, + { + "nom": "Faire des propositions de fragment de l'information." + }, + { + "nom": "Faire des propositions de d'organisation du texte." + }, + { + "nom": "Evoquer le destinataire : ce qu'il sait, pense, croit, se demande." + }, + { + "nom": "Répéter à l'identique l'énoncé à écrire." + }, + { + "nom": "Utiliser des formats syntaxiques simples pour produire un énoncé qui peut s'écrire." + }, + { + "nom": "Ralentir son débit pour s'adapter au rythme de l'écriture en s'approchant du découpage de la chaîne parlée en mots." + }, + { + "nom": "Formuler ou reformuler son propos pour respecter les règles de l'écrit : négation (ne…pas), suppression de reprise pronominale (le loup, il…) etc." + }, + { + "nom": "Parler de ce qu'on « ne peut pas dire quand on écrit »." + }, + { + "nom": "Choisir le lexique de spécialité utilisé dans la classe et complexifier l'organisation syntaxique de son énoncé (compléments, relatives, conjonctives...)." + }, + { + "nom": "Prendre en compte des phénomènes textuels de cohérence/cohésion dans le suivi du propos." + }, + { + "nom": "Demander des relectures." + }, + { + "nom": "Faire des propositions de corrections pour se rapprocher de la forme écrite (syntaxe, vocabulaire, concordance des temps)." + }, + { + "nom": "Participer à l'écriture de certains mots." + }, + { + "nom": "Participer à la gestion de marques typographiques." + }, + { + "nom": "Utiliser des termes métalinguistiques (début, fin, phrase, mots, lignes, lettres) pour participer à la gestion de l'écriture." + } + ] + }, + { + "nom": "Découvrir le principe alphabétique", + "competences": [ + { + "nom": "Reconnaitre mon prénom écrit en lettres capitales, en script ou en cursive. Connaitre le nom deslettres qui le composent.", + "fin_cycle": true + }, + { + "nom": "Reconnaitre les lettres de l'alphabet, connaitre leur nom, savoir que le nom d'une lettre peut êtredifférent du son qu'elle transcrit.", + "fin_cycle": true + }, + { + "nom": "Connaître les correspondances entre les trois manières d'écrire les lettres : cursive, script, capitales d'imprimerie et commencer à faire le lien avec le son qu'elles codent.", + "fin_cycle": true + }, + { + "nom": "Copier en cursive un mot ou une très courte phrase dont le sens est connu.", + "fin_cycle": true + }, + { + "nom": "Différencier dessins, écritures, graphismes, pictogrammes, symbole et signes." + }, + { + "nom": "Identifier son prénom en prenant des repères visuels (forme de majuscule, longueur, point sur un I, accent, graphie particulière comme le X ou le H, dernière lettre, trait d'union…)." + }, + { + "nom": "Identifier des mots, en prenant appui par exemple sur la longueur en sachant qu'elle correspond à la longueur de l'énoncé oral." + }, + { + "nom": "Identifier des mots, en prenant appui par exemple sur les lettres et leur ordre puis, en fonction de la lettre, en grande section, sa valeur sonore." + }, + { + "nom": "Faire correspondre les trois écritures en tracé manuscrit et sur traitement de texte. Passer d'une écriture à une autre : capitale d'imprimerie, script et cursive." + }, + { + "nom": "Reconnaître et nommer la majorité des lettres de l'alphabet." + }, + { + "nom": "Marquer l'espace entre chaque mot pour écrire un titre, une phrase." + }, + { + "nom": "Utiliser le lexique qui permet de nommer les unités de la langue : mot, lettre, syllabe, son, phrase, texte, ligne, majuscule." + }, + { + "nom": "Décomposer le mot en syllabes, en isolant la syllabe qu'il écrit, en énonçant le nom de la lettre et sa valeur sonore." + } + ] + }, + { + "nom": "Commencer à écrire tout seul", + "competences": [ + { + "nom": "Ecrire mon prénom en écriture cursive sans modèle.", + "fin_cycle": true + }, + { + "nom": "Copier à l'aide d'un clavier.", + "fin_cycle": true + }, + { + "nom": "Ecrire seul un mot en utilisant des lettres ou groupes de lettres empruntés aux mots connus.", + "fin_cycle": true + }, + { + "nom": "Produire un tracé avec une intention." + }, + { + "nom": "Prendre des repères dans l'espace feuille." + }, + { + "nom": "Gérer l'espace graphique (aller de gauche à droite et maintenir un alignement)." + }, + { + "nom": "Adopter une posture confortable." + }, + { + "nom": "Tenir de façon adaptée l'instrument d'écriture." + }, + { + "nom": "Tracer chaque lettre." + }, + { + "nom": "Enchaîner plusieurs lettres." + }, + { + "nom": "Enchaîner plusieurs lettres en ne levant qu'à bon escient l'instrument d'écriture." + }, + { + "nom": "S'exercer à des transcriptions de mots, phrases, courts textes connus, à leur saisie sur ordinateur." + }, + { + "nom": "Produire des suites de lettres : pseudos lettres ou (et) des lettres sans valeur sonore." + }, + { + "nom": "Utiliser le nom des lettres pour encoder un mot : par exemple KKO pour cacao." + }, + { + "nom": "Utiliser des mots connus mémorisés ou retrouvés dans les outils de la classe (affichages, répertoires de mots, textes de référence…)." + }, + { + "nom": "Utiliser un morceau de mot connu." + }, + { + "nom": "Utiliser un code quel qu'il soit : un phonogramme « j'ai dessiné une dent pour faire le son [an] » ; une lettre (o pour écrire « mo ») qui a la valeur symbolique d'unité entendue dans la prononciation du mot." + }, + { + "nom": "Encoder un mot en prenant appui sur la syllabe : par exemple AAE pour malade." + }, + { + "nom": "Encoder un mot en prenant appui sur la syllabe et le phonème : FOTO pour écrire « photo » ; LIVER pour l'hiver." + } + ] + } + ] + }, + { + "nom": "Agir, s'exprimer, comprendre à travers l'activité physique", + "categories": [ + { + "nom": "Agir dans l'espace, dans la durée et sur les objets", + "competences": [ + { + "nom": "Courir, sauter, lancer de différentes façons, dans des espaces et avec des matériels variés, dans un but précis.", + "fin_cycle": true + }, + { + "nom": "Ajuster et enchaîner ses actions et ses déplacements en fonction d'obstacles à franchir ou de la trajectoire d'objets sur lesquels agir.", + "fin_cycle": true + }, + { + "nom": "S'engager dans l'activité dans la durée et explorer différents possibles, à partir d'objets manipulables." + }, + { + "nom": "Adapter son geste pour donner des trajectoires différentes à des projectiles variés et répondre aux consignes données." + }, + { + "nom": "Ajuster ses actions et ses déplacements en fonction de la trajectoire de l'objet qu'un autre lui envoie." + }, + { + "nom": "Trouver des manières de faire efficaces pour mieux atteindre les buts proposés et chercher à progresser en fonction des effets ou des scores obtenus." + }, + { + "nom": "Investir un espace aménagé et explorer différents cheminements ou différentes actions." + }, + { + "nom": "Proposer différentes solutions ou reproduire celles d'un autre, sur un parcours orienté, pour s'adapter aux obstacles rencontrés." + }, + { + "nom": "Enchainer, dans la continuité, une succession d'actions différentes en respectant les contraintes de réalisation ou les critères de réussite proposés." + }, + { + "nom": "Anticiper et mettre en œuvre un projet d'action en fonction des effets ou des résultats obtenus afin d'atteindre le but recherché." + } + ] + }, + { + "nom": "Adapter ses équilibres et ses déplacements à des environnements et des contraintes variées", + "competences": [ + { + "nom": "Me déplacer avec aisance et en sécurité dans des environnements variés, naturels ou aménagés.", + "fin_cycle": true + }, + { + "nom": "Ajuster et enchaîner mes actions et ses déplacements en fonction d'obstacles à franchir ou de la trajectoire d'objets sur lesquels agir.", + "fin_cycle": true + }, + { + "nom": "S'engager dans l'activité et élaborer des itinéraires ou des actions en réponse à un aménagement donné." + }, + { + "nom": "Oser proposer, reproduire ou inventer des actions nouvelles, remettant en jeu les repères habituels." + }, + { + "nom": "Se risquer à des déséquilibres afin de réaliser des « acrobaties » et montrer à d'autres ses trouvailles, ses propres « exploits »." + }, + { + "nom": "Anticiper, réaliser, montrer à d'autres un projet de parcours, constitué de l'enchainement d'une courte séquence d'actions, se déroulant dans un espace orienté." + }, + { + "nom": "Utiliser des engins inhabituels, en cherchant à réguler les déséquilibres que ceux-ci occasionnent." + }, + { + "nom": "Trouver des moyens efficaces d'actions et de propulsion pour se déplacer dans un espace aménagé." + }, + { + "nom": "Piloter des engins, en prenant des repères sur le milieu, afin de réaliser des itinéraires précis, des trajectoires prévues." + }, + { + "nom": "Prélever des indices dans un espace plus large ou inconnu, prendre en compte des moyens de guidage ou d'orientation pour anticiper et réaliser un projet d'action." + }, + { + "nom": "Entrer dans l'eau et participer aux jeux proposés." + }, + { + "nom": "S'immerger totalement et ouvrir les yeux dans l'eau." + }, + { + "nom": "Abandonner les appuis plantaires pour se déplacer par appuis manuels ou se laisser flotter." + }, + { + "nom": "Se déplacer, tête dans l'eau, en s'aidant des bras et des jambes et explorer le volume aquatique." + } + ] + }, + { + "nom": "Communiquer avec les autres au travers d'actions à visée expressive ou artistique", + "competences": [ + { + "nom": "Construire et conserver une séquence d'actions et de déplacements, en relation avec d'autres partenaires, avec ou sans support musical.", + "fin_cycle": true + }, + { + "nom": "Coordonner ses gestes et ses déplacements avec ceux des autres, lors de rondes et jeux chantés.", + "fin_cycle": true + }, + { + "nom": "Explorer différents possibles à partir d'inducteurs variés, matériels ou imaginaires." + }, + { + "nom": "Transformer son mouvement par l'exploration de contrastes de vitesses, d'énergies, de niveaux, de dissociations." + }, + { + "nom": "Inventer, apprendre et reproduire une courte phrase dansée, constituée d'une séquence d'actions et de déplacements qu'il a pu globalement mémoriser." + }, + { + "nom": "Inscrire ses actions et ses déplacements en relation avec les autres, dans un espace scénique commun orienté, dans le cadre d'un projet présenté à des spectateurs." + }, + { + "nom": "Accorder ses gestes et ses déplacements avec ceux des autres pour évoluer collectivement selon une disposition spatiale simple." + }, + { + "nom": "Synchroniser sa voix, ses frappés, ses gestes ou ses déplacements avec la pulsation, avec le tempo ou en relation avec des évènements sonores facilement perceptibles." + }, + { + "nom": "Anticiper les changements d'orientation ou de mode de groupement en fonction du support musical, tout en respectant la disposition spatiale." + }, + { + "nom": "Repérer la chronologie des actions ou des rôles dans différentes danses collectives, montrées ou transmises à d'autres." + } + ] + }, + { + "nom": "Collaborer, coopérer, s'opposer", + "competences": [ + { + "nom": "Courir, sauter, lancer de différentes façons, dans des espaces et avec des matériels variés, dans un but précis.", + "fin_cycle": true + }, + { + "nom": "Coopérer, exercer des rôles différents complémentaires, s'opposer, élaborer des stratégies pour viser un but ou un effet commun.", + "fin_cycle": true + }, + { + "nom": "S'inscrire dans des règles collectives afin d'atteindre, par des actions en parallèle, un but ou un effet commun." + }, + { + "nom": "Reconnaître son appartenance à une équipe donnée et à y exercer différents rôles complémentaires." + }, + { + "nom": "Se repérer dans un espace orienté pour s'opposer au projet d'un adversaire ou d'une équipe tenant simultanément un rôle antagoniste." + }, + { + "nom": "élaborer des stratégies individuelles ou collectives pour rechercher les manières de faire les plus efficaces." + }, + { + "nom": "S'inscrire dans des formes de jeu collectives visant à déplacer, à faire glisser, à porter le corps d'un autre ou un objet lourd ou volumineux." + }, + { + "nom": "Entrer en contact avec le corps d'un partenaire pour explorer différentes formes d'actions simples et mesurer les effets produits." + }, + { + "nom": "S'opposer à un adversaire, par la médiation d'un objet que celui-ci veut s'approprier ou défendre." + }, + { + "nom": "Organiser ses actions et ses saisies sur le corps de l'adversaire en fonction d'une intention précise ou d'une stratégie déterminée." + } + ] + } + ] + }, + { + "nom": "Agir, s'exprimer, comprendre à travers les activités artistiques", + "categories": [ + { + "nom": "Les productions plastiques et visuelles", + "competences": [ + { + "nom": "Choisir différents outils, médiums, supports en fonction d'un projet ou d'une consigne et les utiliser en adaptant son geste.", + "fin_cycle": true + }, + { + "nom": "Pratiquer le dessin pour représenter ou illustrer, en étant fidèle au réel ou à un modèle, ou en inventant.", + "fin_cycle": true + }, + { + "nom": "Réaliser une composition personnelle en reproduisant des graphismes. Créer des graphismes nouveaux.", + "fin_cycle": true + }, + { + "nom": "Réaliser des compositions plastiques, seul ou en petit groupe, en choisissant et combinant des matériaux, en réinvestissant des techniques et des procédés.", + "fin_cycle": true + }, + { + "nom": "Décrire une image et exprimer son ressenti ou sa compréhension en utilisant un vocabulaire adapté.", + "fin_cycle": true + }, + { + "nom": "Proposer des solutions dans des situations de projet, de création, de résolution de problèmes.", + "fin_cycle": true + }, + { + "nom": "S'engager spontanément dans l'exploration libre, puis guidée, de différents outils et sur des supports variés." + }, + { + "nom": "Commencer à représenter ou à illustrer ce qu'il voit, ce dont il se souvient ou ce qu'il imagine." + }, + { + "nom": "Faire des choix d'outils et de procédés en fonction d'une intention donnée." + }, + { + "nom": "S'exprimer sur sa production, sur celle d'un autre ou à propos d'une œuvre d'artiste." + }, + { + "nom": "Commencer à mettre en mots ce qu'il a voulu évoquer ou représenter." + }, + { + "nom": "Commenter les effets produits, et les situer par rapport à ses intentions initiales." + }, + { + "nom": "Contrôler et varier l'amplitude du geste pour s'adapter au format du support, produire des tracés de plus en plus diversifiés et plus précis." + }, + { + "nom": "Reproduire, assembler, organiser, enchaîner des motifs graphiques puis en créer de nouveaux." + }, + { + "nom": "S'exprimer sur ses propres tracés et nommer les éléments graphiques produits." + }, + { + "nom": "Repérer des motifs graphiques sur différents supports ou dans son environnement pour constituer un répertoire." + }, + { + "nom": "Commencer à mettre en mots ses procédures lors d'échanges entre pairs." + }, + { + "nom": "Commencer à décrire une organisation produite ou observée." + }, + { + "nom": "Explorer et s'approprier différents médiums, outils et matériaux." + }, + { + "nom": "Faire des choix de médiums (craie, encre, peinture…), de matériaux, d'outils et de supports en fonction de son intention." + }, + { + "nom": "S'exprimer sur sa production et / ou ses découvertes." + }, + { + "nom": "Mémoriser et réinvestir un lexique approprié pour décrire les actions et / ou les effets produits." + }, + { + "nom": "Mettre en mots la relation entre ce qu'il a fait et ce qu'il souhaitait faire." + }, + { + "nom": "Observer des images fixes et animées, dire ce qu'il voit, ce qu'il imagine." + }, + { + "nom": "Comparer pour commencer à classer en repérant les différences et les ressemblances entre des images fixes et animées selon des critères simples." + }, + { + "nom": "Commencer à établir et verbaliser des liens entre des images sélectionnées." + }, + { + "nom": "Entrer dans une lecture plus fine des images : lister les éléments narratifs et plastiques." + }, + { + "nom": "Classer des images en déterminant des critères simples." + }, + { + "nom": "Utiliser un lexique adapté pour décrire ce qu'il voit, dire son ressenti ou traduire sa compréhension." + }, + { + "nom": "Transformer des images en respectant une consigne." + } + ] + }, + { + "nom": "Univers sonores", + "competences": [ + { + "nom": "Avoir mémorisé un répertoire varié de comptines et de chansons et les interpréter de manière expressive.", + "fin_cycle": true + }, + { + "nom": "Jouer avec sa voix pour explorer des variantes de timbre, d'intensité, de hauteur, de nuance.", + "fin_cycle": true + }, + { + "nom": "Repérer et reproduire, corporellement ou avec des instruments, des formules rythmiques simples.", + "fin_cycle": true + }, + { + "nom": "Proposer des solutions dans des situations de projet, de création, de résolution de problèmes, avec son corps, sa voix ou des objets sonores.", + "fin_cycle": true + }, + { + "nom": "Parler d'un extrait musical et exprimer son ressenti ou sa compréhension en utilisant un vocabulaire adapté.", + "fin_cycle": true + }, + { + "nom": "Oser jouer avec sa voix seul ou en groupe pour reproduire un motif musical, une phrase (chanson, comptine)." + }, + { + "nom": "Mémoriser des comptines et des chansons pour chanter en chœur avec ses pairs." + }, + { + "nom": "S'exprimer sur sa production et sur celles de ses pairs." + }, + { + "nom": "Enrichir son bagage lexical spécifique au travers des chants et le réinvestir dans d'autres contextes." + }, + { + "nom": "Mobiliser son attention lors de moments d'écoute." + }, + { + "nom": "Faire des propositions musicales enrichies par les écoutes." + }, + { + "nom": "Développer un vocabulaire pour nommer les paramètres du son." + }, + { + "nom": "Réinvestir le vocabulaire acquis lors des diverses activités de production." + }, + { + "nom": "Explorer différents instruments de musique, des objets sonores, les trier, les catégoriser (type de timbres et durée du son…)." + }, + { + "nom": "Explorer son corps au travers de percussions corporelles." + }, + { + "nom": "Faire le lien entre le geste et le son, le maîtriser en vue de produire un son attendu." + }, + { + "nom": "S'exprimer sur sa production et sur celles de ses pairs." + }, + { + "nom": "Développer un vocabulaire musical des actions liées au geste producteur ou caractérisant les principes sonores." + } + ] + }, + { + "nom": "Le spectacle vivant", + "competences": [ + { + "nom": "Proposer des solutions dans des situations de projet, de création, de résolution de problèmes, avec son corps, sa voix ou des objets sonores.", + "fin_cycle": true + }, + { + "nom": "Oser mettre en jeu son corps avec et face aux autres : en imitant ce que fait l'enseignant, un artiste ou un pair, en inventant ou en assemblant des propositions après avoir fait un choix." + }, + { + "nom": "Occuper un espace et y évoluer." + }, + { + "nom": "Transformer ses façons usuelles d'agir et de se déplacer." + }, + { + "nom": "S'inscrire dans l'espace et le temps d'une production collective." + }, + { + "nom": "Devenir un spectateur actif et attentif." + }, + { + "nom": "Témoigner de sa sensibilité à la portée poétique et esthétique du mouvement." + }, + { + "nom": "Exprimer intentionnellement des émotions par le visage ou par le corps." + } + ] + } + ] + }, + { + "nom": "Acquérir les premiers outils mathématiques", + "categories": [ + { + "nom": "Explorer des formes, des grandeurs, des suites organisées", + "competences": [ + { + "nom": "Classer des objets en fonction de caractéristiques liées à leur forme.", + "fin_cycle": true + }, + { + "nom": "Reproduire, dessiner des formes planes.", + "fin_cycle": true + }, + { + "nom": "Classer ou ranger des objets selon un critère de longueur ou de masse ou de contenance.", + "fin_cycle": true + }, + { + "nom": "Reconnaitre quelques solides (cube, pyramide, boule, cylindre).", + "fin_cycle": true + }, + { + "nom": "Savoir nommer quelques formes planes (carré, triangle, cercle ou disque, rectangle) et ce dans toutes leurs orientations et configurations.", + "fin_cycle": true + }, + { + "nom": "Reproduire un assemblage à partir d'un modèle (puzzle, pavage, assemblage de solides).", + "fin_cycle": true + }, + { + "nom": "Identifier une organisation régulière et poursuivre son application.", + "fin_cycle": true + }, + { + "nom": "Reconnaitre globalement des solides par la vue et par le toucher." + }, + { + "nom": "Reconnaitre globalement des formes planes par la vue." + }, + { + "nom": "Reconnaître, distinguer des solides puis des formes planes." + }, + { + "nom": "Appréhender les objets selon le critère d'une grandeur particulière (sa longueur, sa masse ou son volume)." + }, + { + "nom": "Comparer deux objets selon une seule de ces grandeurs (lorsque cela est possible) en ayant recours à un troisième objet de référence pour pouvoir faire cette comparaison : ranger des tours de cubes empilés de la plus courte à la plus longue (domaine des longueurs)." + }, + { + "nom": "Comparer deux objets selon une seule de ces grandeurs (lorsque cela est possible) en ayant recours à un troisième objet de référence pour pouvoir faire cette comparaison :trier des objets en plaçant les plus lourds sous une étagère et les plus légers sur cette étagère (domaine des masses)." + }, + { + "nom": "Comparer deux objets selon une seule de ces grandeurs (lorsque cela est possible) en ayant recours à un troisième objet de référence pour pouvoir faire cette comparaison : trier des objets en plaçant les plus gros dans un grand carton et les plus petits dans une boîte (domaine des volumes)." + }, + { + "nom": "Comparer deux objets selon une seule de ces grandeurs (lorsque cela est possible) en ayant recours à un troisième objet de référence pour pouvoir faire cette comparaison : trier des objets en plaçant les plus gros dans un grand carton et les plus petits dans une boîte (domaine des volumes)." + }, + { + "nom": "Comparer deux objets selon une seule de ces grandeurs (lorsque cela est possible) en ayant recours à un troisième objet de référence pour pouvoir faire cette comparaison : choisir des formes en vue de recouvrir une surface (domaine des aires)." + }, + { + "nom": "Appréhender la notion d'alignement." + }, + { + "nom": "Organiser des suites d'objets en fonction de critères de formes et de couleurs à partir d'algorithmes simples." + }, + { + "nom": "Reconnaître un rythme dans une suite organisée." + }, + { + "nom": "Continuer cette suite." + }, + { + "nom": "Inventer des « rythmes » de plus en plus compliqués." + }, + { + "nom": "Compléter des manques dans une suite organisée." + } + ] + } + ] + }, + { + "nom": "Explorer le monde", + "categories": [ + { + "nom": "Se repérer dans le temps", + "competences": [ + { + "nom": "Situer des événements vécus les uns par rapport aux autres et en les repérant dans la journée, la semaine, le mois ou une saison.", + "fin_cycle": true + }, + { + "nom": "Ordonner une suite de photographies ou d'images, pour rendre compte d'une situation vécue ou d'un récit fictif entendu, en marquant de manière exacte succession et simultanéité.", + "fin_cycle": true + }, + { + "nom": "Utiliser des marqueurs temporels adaptés (puis, pendant, avant, après…) dans des récits, descriptions ou explications.", + "fin_cycle": true + }, + { + "nom": "Associer les moments de la journée avec des activités régulières de la classe." + }, + { + "nom": "Dire ce qu'on a fait avant et après une activité." + }, + { + "nom": "Se repérer dans les premiers éléments chronologiques sur un temps court (la demi-journée) et utiliser correctement les mots « matin », « apres-midi », « soir »." + }, + { + "nom": "Utiliser correctement les mots « jour » et « mois »." + }, + { + "nom": "Connaitre la suite des noms des jours, de la semaine et savoir dire « celui qui précède » et « celui qui suit » un jour donné." + }, + { + "nom": "Utiliser des marques temporelles dans le langage, notamment pour situer ce dont on parle par rapport au moment où l'on parle (hier, aujourd'hui, demain, plus tard…)." + }, + { + "nom": "Utiliser les formes des verbes adaptées (présent, futur, passé) même si la conjugaison exacte fait encore défaut." + }, + { + "nom": "Utiliser le vocabulaire adapté pour traduire une relation entre deux faits, deux moments : avant, après, pendant, bien avant, bien après, en même temps, plus tôt que, plus tard, dans deux jours." + }, + { + "nom": "Utiliser divers outils (comptage régulier, sablier, horloge…) pour comparer des durées." + } + ] + }, + { + "nom": "Se repérer dans l'espace", + "competences": [ + { + "nom": "Situer des objets par rapport à soi, entre eux, par rapport à des objets repères.", + "fin_cycle": true + }, + { + "nom": "Se situer par rapport à d'autres, par rapport à des objets repères.", + "fin_cycle": true + }, + { + "nom": "Dans un environnement bien connu, réaliser un trajet, un parcours à partir de sa représentation (dessin ou codage).", + "fin_cycle": true + }, + { + "nom": "Élaborer des premiers essais de représentation plane, communicables (construction d'un code commun).", + "fin_cycle": true + }, + { + "nom": "Orienter et utiliser correctement une feuille de papier, un livre ou autre support d'écrit, en fonction de consignes, d'un but ou d'un projet précis.", + "fin_cycle": true + }, + { + "nom": "Utiliser des marqueurs spatiaux adaptés (devant, derrière, droite, gauche, dessus, dessous…) dans des récits, des descriptions ou explications.", + "fin_cycle": true + }, + { + "nom": "Se repérer dans l'espace de la classe." + }, + { + "nom": "Identifier les espaces communs de l'école (salle de classe, salle de jeux, couloirs, dortoir, salle de restauration, cour…) et s'y déplacer en autonomie." + }, + { + "nom": "Se déplacer en respectant des règles ou consignes." + }, + { + "nom": "Utiliser des locutions spatiales en particulier celles fondées sur des oppositions : sur/sous, dedans/dehors, à côté de/loin de…." + }, + { + "nom": "Reconnaître et utiliser des représentations d'espaces connus." + }, + { + "nom": "Se repérer dans une page et utiliser le vocabulaire usuel (haut et bas notamment, gauche et droite)." + }, + { + "nom": "Coder des déplacements, des emplacements sur un « plan » connu ou une photographie d'un espace vécu (salle de classe, salle de jeux, cour de récréation…)." + }, + { + "nom": "Repérer sa droite et sa gauche." + }, + { + "nom": "Décrire des positions dans l'espace : positions par rapport à soi ; positions relatives de deux objets ou deux personnes l'un(e) par rapport à l'autre." + }, + { + "nom": "Parler d'espaces lointains (hors du vécu) en employant un vocabulaire adapté pour décrire des habitats, des monuments, des paysages (en fonction de ce qui a été travaillé en classe)." + }, + { + "nom": "S'initier à une attitude responsable (respect des lieux, de la vie, connaissance de l'impact de certains comportements sur l'environnement, etc.)." + } + ] + }, + { + "nom": "Découvrir le monde du vivant", + "competences": [ + { + "nom": "Reconnaître et décrire les principales étapes du développement d'un animal ou d'un végétal, dans une situation d'observation du réel ou sur des images fixes ou animées.", + "fin_cycle": true + }, + { + "nom": "Connaître les besoins essentiels de quelques animaux et végétaux.", + "fin_cycle": true + }, + { + "nom": "Situer et nommer les différentes parties du corps humain, sur soi ou sur une représentation.", + "fin_cycle": true + }, + { + "nom": "Connaître et mettre en œuvre quelques règles d'hygiène corporelle et d'une vie saine.", + "fin_cycle": true + }, + { + "nom": "Commencer à adopter une attitude responsable en matière de respect des lieux et de protection du vivant.", + "fin_cycle": true + }, + { + "nom": "Situer et nommer quelques parties du corps sur lui-même." + }, + { + "nom": "Situer et nommer quelques parties du visage et du corps sur lui-même et sur une représentation." + }, + { + "nom": "Lister les parties du corps nécessaires à une première représentation d'un être humain (tête, corps, bras, jambes, pieds, mains)." + }, + { + "nom": "Situer et nommer les parties du visage, du corps et quelques articulations (cheville, genou, coude, hanche, épaule, nuque) sur lui-même ou sur une représentation." + }, + { + "nom": "évoluer de traces éparses à un dessin plus représentatif du corps humain." + }, + { + "nom": "Dessiner un être humain complet (pieds, jambes, bassin, torse, bras, tête avec éventuellement quelques détails sur le visage)." + }, + { + "nom": "Dessiner un être humain complet avec des parties de son visage. Les membres commencent à prendre de l'épaisseur." + }, + { + "nom": "Se représenter avec un corps articulé en mouvement (en train de courir ou de sauter…)." + }, + { + "nom": "Respecter les règles d'hygiène après invitation, avec l'aide de l'adulte." + }, + { + "nom": "Réaliser les premiers gestes qui garantissent son hygiène corporelle." + }, + { + "nom": "Demander de l'aide pour répondre à ses besoins physiologiques." + }, + { + "nom": "Gérer ses besoins physiologiques de façon autonome." + }, + { + "nom": "Réguler et anticiper ses besoins physiologiques pour ne pas arrêter les activités prévues." + }, + { + "nom": "énoncer les règles d'hygiène corporelle et de vie saine." + }, + { + "nom": "établir des premiers liens entre ce qu'il consomme et les conséquences possibles sur sa santé." + }, + { + "nom": "Reconnaître et nommer les animaux observés en classe et participer à l'entretien des élevages en fournissant la « nourriture » nécessaire, en assurant le nettoyage." + }, + { + "nom": "Savoir que les animaux ont besoin de se nourrir et de boire pour vivre." + }, + { + "nom": "Savoir que les animaux ont besoin de boire et d'une nourriture adaptée à leur régime alimentaire." + }, + { + "nom": "Savoir que les animaux ont besoin de respirer, de dormir." + }, + { + "nom": "Savoir que les animaux grandissent et se transforment." + }, + { + "nom": "Connaître les principales étapes du développement d'un animal (naissance, croissance, reproduction, vieillissement, mort)." + }, + { + "nom": "Observer et repérer les naissances dans les élevages." + }, + { + "nom": "Savoir qu'en général, la reproduction animale nécessite un mâle et une femelle." + }, + { + "nom": "Associer des modes de reproduction à des types d'animaux : Le bébé se développe dans le ventre de la femelle." + }, + { + "nom": "Associer des modes de reproduction à des types d'animaux : Le bébé se développe dans un œuf, à l'extérieur de la femelle." + }, + { + "nom": "Savoir que chez certains animaux, les femelles peuvent avoir des bébés sans l'intervention d'un mâle." + }, + { + "nom": "Reconnaître et nommer les plantes observées en classe et participer à l'entretien des plantations en fournissant l'arrosage et l'entretien nécessaires." + }, + { + "nom": "Savoir que les plantes ont des besoins : eau, lumière et nourriture pour se développer." + }, + { + "nom": "Savoir que les végétaux sont vivants, que les plantes grandissent et se transforment." + }, + { + "nom": "Reconnaître les principales étapes du développement d'un végétal." + }, + { + "nom": "établir des premiers liens entre fleur, fruit et graine." + } + ] + }, + { + "nom": "Explorer la matière", + "competences": [ + { + "nom": "Choisir, utiliser et savoir désigner des outils et des matériaux adaptés à une situation, à des actions techniques spécifiques (plier, couper, coller, assembler, actionner…).", + "fin_cycle": true + }, + { + "nom": "Découvrir et manipuler des matériaux existants ou fabriqués en classe (ex : pâte à sel, pâte à tarte…)." + }, + { + "nom": "Identifier quelques matériaux et les différencier en grandes familles (les papiers, les cartons, les tissus, les pâtes…)." + }, + { + "nom": "Trier, comparer des matériaux en fonction de caractéristiques physiques accessibles par les 5 sens (couleur, forme, taille, odeur, bruit, masse, texture, dureté) ou d'autres propriétés physiques (opaque, transparent, translucide ; attiré ou non par l'aimant ; perméable, imperméable…)." + }, + { + "nom": "Connaître d'autres propriétés physiques des matériaux (perméabilité, magnétisme, transparence…)." + }, + { + "nom": "Classer des objets selon le matériau qui les compose (manipulation) selon une propriété commune (formes, goût, texture…), selon leurs usages." + }, + { + "nom": "Repérer des transformations de matériaux sous l'effet de la chaleur (sécher, durcir, fondre…), de l'eau (mouiller, dissoudre…), de l'air (déplacer, gonfler…), d'actions mécaniques avec des mains (froisser, plier…) et avec des outils (découper, percer…)." + }, + { + "nom": "Agir de manière raisonnée sur un matériau, choisir le bon matériau en fonction d'un besoin, d'un effet attendu, d'un projet." + }, + { + "nom": "Modifier une procédure si nécessaire pour l'adapter au résultat attendu." + }, + { + "nom": "Prendre conscience du caractère réversible (ou non) de certaines actions." + }, + { + "nom": "Lister les actions et l'ordre de réalisation, les transformations accomplies et les outils nécessaires." + } + ] + }, + { + "nom": "Utiliser, fabriquer, manipuler des objets", + "competences": [ + { + "nom": "Réaliser des constructions ; construire des maquettes simples en fonction de plans ou d'instructions de montage.", + "fin_cycle": true + }, + { + "nom": "Prendre en compte les risques de l'environnement familier proche (objets et comportements dangereux, produits toxiques).", + "fin_cycle": true + }, + { + "nom": "Réaliser des montages de plus en plus complexes avec une intention repérable." + }, + { + "nom": "Réaliser des montages de plus en plus complexes avec une intention formulée." + }, + { + "nom": "Réaliser une construction, reconstituer un objet en disposant d'un modèle de référence qu'il peut manipuler ou observer." + }, + { + "nom": "Réaliser une construction, reconstituer un objet à partir d'un modèle représenté (photographie, dessin, schéma)." + }, + { + "nom": "Réaliser une construction, reconstituer un objet à partir d'illustrations des étapes de la construction, de représentations avec différentes vues (en éclaté, en perspective, de plusieurs points de vue…)." + }, + { + "nom": "Représenter par le dessin/schéma un montage qu'il a réalisé." + }, + { + "nom": "Réaliser des photographies caractéristiques des différentes étapes du montage." + }, + { + "nom": "Reconnaître les différents systèmes de fermeture des vêtements ou des chaussures (bouton, bouton-pression, fermeture éclair, lacets, bande autoagrippante…)." + }, + { + "nom": "Découvrir la fonction d'usage (boutonner, attacher, fermer, fixer…) et réaliser les gestes adaptés (utiliser efficacement une fermeture à glissière, un boutonpression, etc.)." + }, + { + "nom": "Reconnaître, identifier et nommer quelques objets parmi une famille d'objets." + }, + { + "nom": "Trier, comparer en fonction des usages." + }, + { + "nom": "Utiliser de manière raisonnée, choisir le bon outil en fonction d'un besoin, d'un effet attendu, d'un contexte d'utilisation." + }, + { + "nom": "Identifier et nommer les risques liés à certaines activités ou à certains outils utilisés (chuter, se pincer, se couper, s'étouffer, s'électrocuter, se brûler…)." + }, + { + "nom": "Adapter et justifier son comportement en fonction des risques identifiés." + }, + { + "nom": "Reconnaître certains produits toxiques ou dangereux et le justifier grâce aux indications visuelles présentes." + }, + { + "nom": "Alerter un adulte en cas de danger pour lui-même ou pour un camarade." + } + ] + }, + { + "nom": "Utiliser des outils numériques", + "competences": [ + { + "nom": "Utiliser des objets numériques : appareil photo, tablette, ordinateur.", + "fin_cycle": true + }, + { + "nom": "Agir sur une tablette numérique (allumer, éteindre, choisir une application : l'ouvrir, l'utiliser et la fermer)." + }, + { + "nom": "Choisir l'outil numérique qui convient en fonction d'un besoin (photographier, filmer, enregistrer la voix, copier du texte…)." + }, + { + "nom": "Manipuler une souris d'ordinateur pour pointer un élément, cliquer sur un élément, déplacer un élément." + }, + { + "nom": "Repérer des lettres sur un clavier (ordinateur ou tablette)." + }, + { + "nom": "Utiliser les touches de direction (haut, bas, gauche, droite) pour déplacer un personnage dans un jeu éducatif." + }, + { + "nom": "Copier, écrire à l'aide d'un clavier (ordinateur ou tablette) : son prénom, des mots, le titre d'un livre, des phrases, de courts textes." + } + ] + } + ] + }, + { + "nom": "Apprendre ensemble et vivre ensemble", + "categories": [] + } + ] +} \ No newline at end of file diff --git a/Back-End/competences/Cycle2.json b/Back-End/competences/Cycle2.json new file mode 100644 index 0000000..e89115d --- /dev/null +++ b/Back-End/competences/Cycle2.json @@ -0,0 +1,3460 @@ +{ + "domaines": [ + { + "nom": "Français", + "categories": [ + { + "nom": "Langage oral", + "competences": [ + { + "nom": "Conserver une attention soutenue lors de situations d'écoute ou d'échanges et manifester, si besoin et à bon escient, son incompréhension.", + "fin_cycle": true + }, + { + "nom": "Dans les différentes situations de communication, produire des énoncés clairs en tenant compte de l'objet du propos et des interlocuteurs.", + "fin_cycle": true + }, + { + "nom": "Pratiquer les formes de discours attendues – notamment raconter, décrire, expliquer – dans des situations où les attentes sont explicites ; en particulier raconter seul un récit étudié en classe.", + "fin_cycle": true + }, + { + "nom": "Participer avec pertinence à un échange (questionner, répondre à une interpellation, exprimer un accord ou un désaccord, apporter un complément, …) .", + "fin_cycle": true + }, + { + "nom": "Maintenir une attention orientée en fonction du but." + }, + { + "nom": "Concentrer son attention en faisant abstraction d'éléments distracteurs possibles (présence d'images, bruit, objets à toucher...).", + "niveau": "CP" + }, + { + "nom": "S'engager dans l'écoute en manifestant une attention active.", + "niveau": "CE1" + }, + { + "nom": "S'engager dans l'écoute en manifestant une attention constructive.", + "niveau": "CE2" + }, + { + "nom": "Repérer et mémoriser des informations importantes. Les relier entre elles pour leur donner du sens." + }, + { + "nom": "Commencer à utiliser certaines stratégies de mémorisation et de traitement de l'information orale qui font l'objet d'un enseignement explicite.", + "niveau": "CP" + }, + { + "nom": "Utiliser certaines stratégies de mémorisation et de traitement de l'information orale qui font l'objet d'un enseignement explicite.", + "niveau": "CE1" + }, + { + "nom": "Mobiliser des références culturelles nécessaires pour comprendre le message ou le texte." + }, + { + "nom": "Mémoriser le vocabulaire entendu dans les textes." + }, + { + "nom": "Repérer d'éventuelles difficultés de compréhension." + }, + { + "nom": "Prendre en compte des récepteurs ou interlocuteurs." + }, + { + "nom": "Mobiliser des techniques qui font qu'on est écouté." + }, + { + "nom": "Organiser son discours." + }, + { + "nom": "Mémoriser des textes." + }, + { + "nom": "Lire à haute voix." + }, + { + "nom": "Adapter son discours et sa posture (intensité, hauteur de la voix) en fonction de la situation d'énonciation (raconter, décrire, expliquer, argumenter, prescrire, ordonner) et de son auditoire (personne/groupe, adulte/pair).", + "niveau": "CP" + }, + { + "nom": "Adapter son discours et sa posture (intensité, hauteur de la voix) en fonction de la situation d'énonciation (raconter, décrire, expliquer, argumenter, prescrire, ordonner) et de son auditoire (une personne/ groupe, adulte/ pair) avec un guidage du professeur de moins en moins présent.", + "niveau": "CE1" + }, + { + "nom": "Construire des énoncés et les retransmettre dans le souci d'une bonne compréhension.", + "niveau": "CE2" + }, + { + "nom": "Respecter des règles organisant les échanges." + }, + { + "nom": "Ecouter les interlocuteurs. Attendre la fin des propos avant de répondre. Ne pas crier pour se faire entendre.", + "niveau": "CP" + }, + { + "nom": "Prendre part à des échanges et écouter les autres.", + "niveau": "CE2" + }, + { + "nom": "Prendre conscience et tenir compte des enjeux." + }, + { + "nom": "Utiliser aisément la prise de parole pour saluer, demander, acquiescer, approuver, refuser, réfuter, s'engager, questionner, proposer, émettre des hypothèses.", + "niveau": "CP" + }, + { + "nom": "Prendre aisément la parole dans des situations de plus en plus variées.", + "niveau": "CE1" + }, + { + "nom": "Organiser son propos." + }, + { + "nom": "Rapporter, rendre compte, raconter, décrire en organisant son propos grâce à l'aide d'organisateurs du discours.", + "niveau": "CE1" + }, + { + "nom": "Utiliser le vocabulaire mémorisé." + }, + { + "nom": "Disposer d'un lexique mobilisable en situation de production langagière.", + "niveau": "CP" + }, + { + "nom": "Disposer d'un lexique de plus en plus varié et structuré, mobilisable en situation d'expression orale.", + "niveau": "CE1" + }, + { + "nom": "Repérer le respect ou non des règles organisant les échanges dans les propos d'un pair." + }, + { + "nom": "Prendre en compte des règles explicites établies collectivement." + }, + { + "nom": "Se corriger après écoute." + } + ] + }, + { + "nom": "Lecture et compréhension de l'écrit", + "competences": [ + { + "nom": "Identifier des mots rapidement : décoder aisément des mots inconnus réguliers, reconnaître des mots fréquents et des mots irréguliers mémorisés.", + "fin_cycle": true + }, + { + "nom": "Lire et comprendre des textes variés, adaptés à la maturité et à la culture scolaire des élèves.", + "fin_cycle": true + }, + { + "nom": "Lire à voix haute avec fluidité, après préparation, un texte d'une demi-page (1 400 à 1 500 signes) ; participer à une lecture dialoguée après préparation.", + "fin_cycle": true + }, + { + "nom": "Lire au moins cinq à dix œuvres en classe par an.", + "fin_cycle": true + }, + { + "nom": "Savoir discriminer de manière auditive et savoir analyser les constituants des mots (conscience phonologique)." + }, + { + "nom": "Discriminer les différents phonèmes de la langue.", + "niveau": "CP" + }, + { + "nom": "Réaliser des manipulations simples sur les syllabes et sur les phonèmes (retrait, ajout, substitution, déplacements...).", + "niveau": "CP" + }, + { + "nom": "Discriminer et localiser dans des mots, les différents phonèmes de la langue, en particulier les phonèmes proches (par exemple, [f]–[v], [ch]-[j], [k]-[g]).", + "niveau": "CE1" + }, + { + "nom": "Réaliser des manipulations sur les phonèmes (retrait, ajout, substitution, déplacements...). Réinvestir ces compétences en situation de rédaction.", + "niveau": "CE1" + }, + { + "nom": "Savoir discriminer de manière visuelle et connaître le nom des lettres ainsi que le son qu'elles produisent." + }, + { + "nom": "Nommer et discriminer visuellement les lettres et les graphèmes qu'elles forment quel que soit le type d'écriture utilisé (écriture scripte, cursive, majuscules d'imprimerie).", + "niveau": "CP" + }, + { + "nom": "Distinguer le nom d'une lettre ou d'un groupe de lettres du phonème qui lui correspond.", + "niveau": "CP" + }, + { + "nom": "Connaître les graphèmes et les associer aux phonèmes.", + "niveau": "CE1" + }, + { + "nom": "Connaître et savoir utiliser l'ordre alphabétique.", + "niveau": "CE1" + }, + { + "nom": "Repérer dans un mot la présence de lettres muettes en appui sur ses connaissances en grammaire.", + "niveau": "CE2" + }, + { + "nom": "Établir les correspondances graphophonologiques ; combinatoire (produire des syllabes simples et complexes)." + }, + { + "nom": "Fusionner les graphèmes étudiés pour lire des syllabes et des mots.", + "niveau": "CP" + }, + { + "nom": "En lien avec le décodage, encoder avec exactitude des syllabes et des mots réguliers dont les graphèmes ont été étudiés.", + "niveau": "CP" + }, + { + "nom": "Fusionner l'ensemble des graphèmes pour lire et écrire des syllabes et des mots complexes.", + "niveau": "CE1" + }, + { + "nom": "Utiliser la voie graphophonologique pour lire des mots inconnus en conservant une fluidité dans la lecture.", + "niveau": "CE2" + }, + { + "nom": "Mémoriser les composantes du code." + }, + { + "nom": "Connaîttre l'ensemble des correspondances graphèmes-phonèmes.", + "niveau": "CP" + }, + { + "nom": "Décoder avec exactitude les mots nouveaux ainsi que ceux dont le décodage n'a pas encore été automatisé.", + "niveau": "CP" + }, + { + "nom": "Connaître l'ensemble des correspondances graphèmes-phonèmes et décode avec exactitude l'ensemble des mots nouveaux dont le décodage n'a pas encore été automatisé.", + "niveau": "CE1" + }, + { + "nom": "En lien avec le vocabulaire et l'orthographe, réinvestir ses connaissances pour analyser la formation des mots.", + "niveau": "CE2" + }, + { + "nom": "Mémoriser des mots fréquents (notamment en situation scolaire) et irréguliers." + }, + { + "nom": "Reconnaîttre directement les mots fréquents dont les graphèmes ont été étudiés et les mots courants n'ayant pas de correspondance graphème/phonème régulières, les plus fréquents (par exemple, femme, yeux, monsieur, fils, sept, compter, automne, football, clown, week-end, igloo ...).", + "niveau": "CP" + }, + { + "nom": "Identifier les mots ayant des parties communes (par exemple, « -age », « -eur », « -ette » et prendre appui sur la reconnaissance des familles de mots et des affixes pour identifier plus rapidement les mots.", + "niveau": "CP" + }, + { + "nom": "Reconnaître directement les mots les plus fréquents et les mots irréguliers. Savoir les orthographier.", + "niveau": "CE1" + }, + { + "nom": "Reconnaître directement les mots fréquents et les mots irréguliers. Les orthographier.", + "niveau": "CE2" + }, + { + "nom": "Solliciter majoritairement la voie directe pour identifier les mots dans la lecture d'un texte.", + "niveau": "CE2" + }, + { + "nom": "Savoir mobiliser la compétence de décodage." + }, + { + "nom": "Mobiliser le décodage des mots avec une aisance suffisante pour mettre en œuvre des stratégies de compréhension de ce qui a été lu (phrases et texte court fortement déchiffrables à l'aide des CGP étudiées).", + "niveau": "CP" + }, + { + "nom": "Mobiliser le décodage des mots avec une aisance suffisante pour mettre en œuvre des stratégies de compréhension de ce qui a été lu.", + "niveau": "CE1" + }, + { + "nom": "Le décodage est automatisé.", + "niveau": "CE2" + }, + { + "nom": "Mettre en œuvre (de manière guidée, puis autonome) une démarche explicite pour découvrir et comprendre un texte." + }, + { + "nom": "Comprendre un récit lu par le professeur d'un degré de complexité supérieur à celui que l'élève est capable de lire seul silencieusement.", + "niveau": "CP" + }, + { + "nom": "Comprendre un texte d'une dizaine de lignes lu en autonomie.", + "niveau": "CP" + }, + { + "nom": "Comprendre un texte d'une vingtaine de lignes, lu en autonomie.", + "niveau": "CE1" + }, + { + "nom": "Se confronter à des textes plus complexes du point de vue de la langue et des connaissances culturelles véhiculées.", + "niveau": "CE1" + }, + { + "nom": "Comprendre des textes lus en autonomie grâce à des processus de compréhension qui s'automatisent.", + "niveau": "CE2" + }, + { + "nom": "Savoir parcourir le texte de manière rigoureuse." + }, + { + "nom": "Être capable de faire des inférences." + }, + { + "nom": "Savoir mettre en relation sa lecture avec les éléments de sa propre culture." + }, + { + "nom": "Savoir mobiliser ses expériences antérieures de lecture (lien avec les lectures personnelles, les expériences vécues et des connaissances qui en sont issues (sur des univers, des personnages types)." + }, + { + "nom": "S'appuyer sur une première connaissance des caractéristiques de personnages-types (la sorcière, l'ogre, la princesse...) pour comprendre ce qui fait agir des personnages d'une histoire et réalise des inférences.", + "niveau": "CP" + }, + { + "nom": "S'appuyer sur une connaissance des caractéristiques de personnages-types pour comprendre ce qui fait agir les personnages et infèrer.", + "niveau": "CE1" + }, + { + "nom": "Disposer de références construites sur des réseaux de textes : le récit policier, le récit historique, fantastique, etc.", + "niveau": "CE1" + }, + { + "nom": "Connaître les textes patrimoniaux adaptés à son âge.", + "niveau": "CE1" + }, + { + "nom": "Connaître les caractéristiques de personnages-types de plus en plus diversifiés. Disposer de références construites sur des réseaux de textes.", + "niveau": "CE2" + }, + { + "nom": "Partager une culture commune autour de textes patrimoniaux adaptés à son âge.", + "niveau": "CE2" + }, + { + "nom": "Savoir mobiliser des champs lexicaux portant sur l'univers évoqué par les textes." + }, + { + "nom": "Catégoriser des mots selon différents critères (réseaux sémantiques, synonymes, antonymes, mots de la même famille).", + "niveau": "CE1" + }, + { + "nom": "Prendre appui sur le décodage et le contexte pour comprendre le sens d'un mot.", + "niveau": "CE1" + }, + { + "nom": "Connaître des mots appartenant à des champs lexicaux de plus en plus étoffés et diversifiés.", + "niveau": "CE2" + }, + { + "nom": "Savoir justifier son interprétation ou ses réponses, s'appuyer sur le texte et sur les autres connaissances mobilisées." + }, + { + "nom": "Rechercher et repérer dans un texte lu par le professeur ou lu en autonomie l'endroit où l'information a été trouvée (compréhension de l'explicite).", + "niveau": "CP" + }, + { + "nom": "Verbaliser un raisonnement simple permettant de justifier une inférence.", + "niveau": "CE1" + }, + { + "nom": "Rechercher et repérer dans un texte lu en autonomie l'endroit où l'information a été trouvée (compréhension de l'explicite).", + "niveau": "CE1" + }, + { + "nom": "Respecter ce que l'auteur a écrit, justifier son interprétation en citant le texte ou en surlignant.", + "niveau": "CE2" + }, + { + "nom": "Expliciter l'utilisation de connaissances ne se trouvant pas dans le texte.", + "niveau": "CE2" + }, + { + "nom": "Être capable de formuler ses difficultés, d'esquisser une analyse de leurs motifs, de demander de l'aide." + }, + { + "nom": "Exprimer son incompréhension d'un mot du texte décodé ou entendu.", + "niveau": "CP" + }, + { + "nom": "Repérer une rupture dans l'élaboration du sens de ce qui est lu, relire puis demander de l'aide si nécessaire.", + "niveau": "CP" + }, + { + "nom": "Repérer une rupture dans l'élaboration du sens de ce qui est lu, relire puis essayer de réparer la perte de sens.", + "niveau": "CE1" + }, + { + "nom": "Repérer une rupture dans l'élaboration du sens de ce qui est lu, met en œuvre des stratégies de récupération du sens.", + "niveau": "CE2" + }, + { + "nom": "Maintenir une attitude active et réflexive, une vigilance relative à l'objectif (compréhension, buts de la lecture)." + }, + { + "nom": "Comprendre que la compréhension résulte d'une activité d'élaboration qui demande un engagement.", + "niveau": "CP" + }, + { + "nom": "Verbaliser ses procédures dans des échanges avec le professeur et les autres élèves.", + "niveau": "CE1" + }, + { + "nom": "Mettre en œuvre un engagement dont on a compris le sens depuis le CP.", + "niveau": "CE2" + }, + { + "nom": "Réaliser une lecture orientée vers un but.", + "niveau": "CE2" + }, + { + "nom": "Savoir lire en visant différents objectifs :- lire pour réaliser quelque chose ;- lire pour découvrir ou valider des informations sur… ;- lire une histoire pour la comprendre et la raconter à son tour ;- lire pour enrichir son vocabulaire ;- lire pour le plaisir de lire." + }, + { + "nom": "Verbaliser simplement l'objectif de sa lecture.", + "niveau": "CP" + }, + { + "nom": "Verbaliser ses objectifs de lecture avant de s'engager dans la lecture de textes plus diversifiés et plus complexes qu'au CP.", + "niveau": "CE1" + }, + { + "nom": "Savoir décoder et comprendre un texte." + }, + { + "nom": "Après préparation, lire un texte adapté à son niveau de lecture avec fluidité.", + "niveau": "CE1" + }, + { + "nom": "Lire un texte avec fluidité.", + "niveau": "CE2" + }, + { + "nom": "Identifier les marques de ponctuation et les prendre en compte." + }, + { + "nom": "Après préparation, lire un texte d'une dizaine de lignes adapté à son niveau de lecture en respectant la ponctuation de fin de phrase.", + "niveau": "CP" + }, + { + "nom": "Lire des textes (récits, documentaires, textes prescriptifs, etc.) adaptés à son niveau de lecture en respectant la ponctuation (les différents points et les virgules).", + "niveau": "CE1" + }, + { + "nom": "Lire un texte en respectant l'ensemble des marques de ponctuation.", + "niveau": "CE2" + }, + { + "nom": "Montrer sa compréhension par une lecture expressive." + }, + { + "nom": "Lire ou relire un texte connu en portant attention aux différences d'intonation entre récit et discours.", + "niveau": "CP" + }, + { + "nom": "Lire un texte en portant attention aux différences d'intonation entre récit et discours.", + "niveau": "CE1" + }, + { + "nom": "Restituer les différences d'intonation, adapter le rythme et le ton de sa voix à l'état mental du personnage.", + "niveau": "CE2" + } + ] + }, + { + "nom": "Écriture", + "competences": [ + { + "nom": "Copier ou transcrire, dans une écriture lisible, un texte d'une dizaine de lignes en respectant la mise en page, la ponctuation, l'orthographe et en soignant la présentation.", + "fin_cycle": true + }, + { + "nom": "Rédiger un texte d'environ une demi-page, cohérent, organisé, ponctué, pertinent par rapport à la visée et au destinataire.", + "fin_cycle": true + }, + { + "nom": "Améliorer un texte, notamment son orthographe, en tenant compte d'indications.", + "fin_cycle": true + }, + { + "nom": "Maîtriser des gestes de l'écriture cursive exécutés avec une vitesse et une sûreté croissantes." + }, + { + "nom": "Gérer l'espace graphique, respecter les normes de l'écriture cursive.", + "niveau": "CP" + }, + { + "nom": "Ecrire de façon lisible, avec fluidité. Tracer quelques majuscules en cursive.", + "niveau": "CP" + }, + { + "nom": "Avoir une écriture fluide.", + "niveau": "CE1" + }, + { + "nom": "Connaître le tracé des majuscules en cursive.", + "niveau": "CE1" + }, + { + "nom": "Conserver les habitudes d'écriture construites depuis le début du cycle.", + "niveau": "CE2" + }, + { + "nom": "Transcrire un texte avec les correspondances entre diverses écritures des lettres (du scripte vers la cursive)." + }, + { + "nom": "Connaîttre les correspondances entre les écritures et passer de l'une à l'autre à l'écrit en se référant à un outil.", + "niveau": "CP" + }, + { + "nom": "Connaître les correspondances entre les écritures en particulier pour les lettres en miroir (p/q, b/d).", + "niveau": "CE1" + }, + { + "nom": "Utiliser des stratégies de copie pour dépasser la copie lettre à lettre : prise d'indices, mémorisation de mots ou groupes de mots." + }, + { + "nom": "Recopier sans erreur des phrases courtes et simples en mémorisant des mots et groupes de mots (et non en recopiant lettre à lettre). Disposer de stratégies efficaces.", + "niveau": "CP" + }, + { + "nom": "Recopier sans erreur en variant les stratégies de mémorisation des mots et groupes de mots. Disposer de stratégies efficaces.", + "niveau": "CE1" + }, + { + "nom": "Respecter la mise en page des textes proposés (demandes ou informations adressées aux parents ; synthèses d'activités ; outils de référence ; résumés de leçons ; poèmes et chansons à mémoriser ; anthologie personnelle de textes, ...)." + }, + { + "nom": "Recopier en respectant les mises en page définies par le type de texte.", + "niveau": "CP" + }, + { + "nom": "Recopier en respectant des mises en page plus complexes.", + "niveau": "CE1" + }, + { + "nom": "Relire pour vérifier la conformité orthographique." + }, + { + "nom": "Comparer sa production écrite au modèle, identifier les erreurs puis commencer à les rectifier.", + "niveau": "CP" + }, + { + "nom": "Relire son écrit et le corriger en fonction du texte et des indications du professeur.", + "niveau": "CE1" + }, + { + "nom": "Relire sa production et la corriger.", + "niveau": "CE2" + }, + { + "nom": "Manier le traitement de texte pour la mise en page de courts textes." + }, + { + "nom": "Taper au clavier quelques lignes en respectant des signes de ponctuation : la virgule, le point, l'apostrophe et les guillemets.", + "niveau": "CP" + }, + { + "nom": "Taper au clavier quelques lignes en respectant les signes de ponctuation.", + "niveau": "CE1" + }, + { + "nom": "Identifier les caractéristiques propres à différents genres ou formes de textes." + }, + { + "nom": "Repérer quelques caractéristiques formelles et textuelles de certains textes : narratifs, informatifs, poétiques, injonctifs, argumentatifs, lettres.", + "niveau": "CP" + }, + { + "nom": "Connaître quelques caractéristiques formelles et textuelles des textes : narratifs, informatifs, poétiques, injonctifs, argumentatifs, lettres.", + "niveau": "CE1" + }, + { + "nom": "Mettre en œuvre une démarche d'écriture de textes : trouver et organiser des idées, élaborer des phrases qui s'enchaînent avec cohérence, écrire ces phrases (démarche progressive : d'abord guidée, puis autonome)." + }, + { + "nom": "Ecrire un groupe de mots ou une phrase simple en réponse à une question ou une consigne.", + "niveau": "CP" + }, + { + "nom": "Produire un court texte de 3 à 5 phrases (à partir d'une structure donnée, d'une image ou d'une série d'images).", + "niveau": "CP" + }, + { + "nom": "Rédiger des écrits courts en autonomie en respectant la démarche enseignée.", + "niveau": "CE1" + }, + { + "nom": "Ecrire dans tous les enseignements et fréquemment (écrits de travail, écrits intermédiaires, traduction d'un raisonnement, d'une pensée).", + "niveau": "CE1" + }, + { + "nom": "Ecrire seul en respectant la démarche enseignée.", + "niveau": "CE2" + }, + { + "nom": "Acquérir quelques connaissances sur la langue : mémoire orthographique des mots, règles d'accord, ponctuation, organisateurs du discours, ..." + }, + { + "nom": "Mobiliser la connaissance des CGP.", + "niveau": "CP" + }, + { + "nom": "Orthographier correctement les mots fréquents et quelques formes verbales.", + "niveau": "CP" + }, + { + "nom": "Commencer à respecter les accords, en genre et en nombre, que l'on entend au sein du groupe nominal restreint.", + "niveau": "CP" + }, + { + "nom": "Marquer le point final et la majuscule.", + "niveau": "CP" + }, + { + "nom": "Commencer à utiliser des organisateurs du discours pour lier ses phrases et ses idées à l'oral puis à l'écrit.", + "niveau": "CP" + }, + { + "nom": "Orthographier correctement les mots fréquents et les accords étudiés (se reporter à la partie « étude de la langue »).", + "niveau": "CE1" + }, + { + "nom": "Respecter la ponctuation.", + "niveau": "CE1" + }, + { + "nom": "Orthographier correctement les mots fréquents et les accords étudiés.", + "niveau": "CE2" + }, + { + "nom": "Respecter la ponctuation et organiser son discours.", + "niveau": "CE2" + }, + { + "nom": "Mobiliser des outils à disposition dans la classe liés à l'étude de la langue (affiches, cahiers, ouvrages, …)." + }, + { + "nom": "Savoir où chercher les mots outils, les mots fréquents et les règles pour orthographier un mot.", + "niveau": "CE1" + }, + { + "nom": "Savoir où chercher les mots fréquents étudiés et les règles pour orthographier un mot.", + "niveau": "CE2" + }, + { + "nom": "Repérer des dysfonctionnements dans les textes écrits (omissions, incohérences, redites, ...) pour améliorer son écrit." + }, + { + "nom": "Repérer les dysfonctionnements de son texte par la relecture à voix haute du professeur.", + "niveau": "CE1" + }, + { + "nom": "Repérer les dysfonctionnements de son texte par la relecture.", + "niveau": "CE2" + }, + { + "nom": "Mobiliser des connaissances portant sur le genre d'écrit à produire et sur la langue." + }, + { + "nom": "Utiliser les caractéristiques formelles de certains genres d'écrits : poésie, fiche d'identité, recette, notamment.", + "niveau": "CP" + }, + { + "nom": "Utiliser les caractéristiques propres aux genres d'écrits étudiés.", + "niveau": "CE2" + }, + { + "nom": "Exercer une vigilance orthographique et mobiliser les acquisitions travaillées lors des leçons de grammaire, d'abord sur des points désignés par le professeur, puis progressivement étendue." + }, + { + "nom": "Améliorer son texte avec l'aide du professeur.", + "niveau": "CP" + }, + { + "nom": "Améliorer son texte avec l'aide du professeur sur les points étudiés en grammaire.", + "niveau": "CE1" + }, + { + "nom": "Améliorer son texte avec l'aide du professeur en tenant compte d'une typologie d'erreurs.", + "niveau": "CE2" + }, + { + "nom": "Utiliser des outils aidant à la correction : outils élaborés dans la classe, guide de relecture, …." + }, + { + "nom": "Utiliser le cahier de références et les affichages de la classe pour corriger certaines erreurs orthographiques (mots outils, correspondances graphophonologiques).", + "niveau": "CP" + }, + { + "nom": "Utiliser le cahier de références et les affichages de la classe pour corriger certaines erreurs orthographiques en fonction d'un code de correction.", + "niveau": "CE1" + }, + { + "nom": "Utiliser le cahier de références et les affichages de la classe pour corriger son texte.", + "niveau": "CE2" + }, + { + "nom": "Commencer à utiliser le correcteur orthographique du traitement de textes.", + "niveau": "CE2" + } + ] + }, + { + "nom": "Étude de la langue (grammaire, orthographe, lexique)", + "competences": [ + { + "nom": "Orthographier les mots les plus fréquents (notamment en situation scolaire) et les mots invariables mémorisés.", + "fin_cycle": true + }, + { + "nom": "Raisonner pour réaliser les accords dans le groupe nominal d'une part (déterminant, nom, adjectif), entre le verbe et son sujet d'autre part (cas simples : sujet placé avant le verbe et proche de lui ; sujet composé d'un groupe nominal comportant au plus un adjectif).", + "fin_cycle": true + }, + { + "nom": "Utiliser ses connaissances sur la langue pour mieux s'exprimer à l'oral, pour mieux comprendre des mots et des textes, pour améliorer des textes écrits.", + "fin_cycle": true + }, + { + "nom": "Connaître les correspondances graphophonologiques." + }, + { + "nom": "Connaître l'ensemble des correspondances graphophonologiques et les mobiliser en situation de lecture et d'écriture.", + "niveau": "CP" + }, + { + "nom": "Mobiliser l'ensemble des correspondances graphophonologiques en situation de lecture et d'écriture.", + "niveau": "CE1" + }, + { + "nom": "Connaître la valeur sonore de certaines lettres (s - c - g) selon le contexte." + }, + { + "nom": "Commencer à prendre en compte l'environnement des lettres, en situation de lecture, et à un premier niveau, à l'écrit.", + "niveau": "CP" + }, + { + "nom": "Prendre en compte l'environnement des lettres, en situation de lecture et d'écriture.", + "niveau": "CE1" + }, + { + "nom": "Connaître la composition de certains graphèmes selon la lettre qui suit (an/am, en/em, on/om, in/im)." + }, + { + "nom": "Prendre en compte ces compositions en situation de lecture et commencer à les mobiliser à l'écrit (dictée).", + "niveau": "CP" + }, + { + "nom": "Prendre en compte ces compositions en situation de lecture, les mobiliser à l'écrit.", + "niveau": "CE1" + }, + { + "nom": "Mobiliser des mots en fonction des lectures et des activités conduites, pour mieux parler, mieux comprendre, mieux écrire." + }, + { + "nom": "Constituer des répertoires.", + "niveau": "CP" + }, + { + "nom": "Préciser le sens d'un mot d'après son contexte.", + "niveau": "CP" + }, + { + "nom": "Constituer des répertoires à partir des leçons conduites sur les mots rencontrés en lecture. Préciser le sens d'un mot d'après son contexte.", + "niveau": "CE1" + }, + { + "nom": "Savoir trouver des synonymes, des antonymes, des mots de la même famille lexicale, sans que ces notions ne constituent des objets d'apprentissage." + }, + { + "nom": "Commencer à catégoriser les mots selon différents critères et à les mettre en résonnance. Commencer à faire des liens : champs lexicaux, réseaux sémantiques, synonymes, antonymes, mots de la même famille.", + "niveau": "CP" + }, + { + "nom": "Catégoriser les mots selon différents critères et les mettre en résonnance, faire des liens : champs lexicaux, réseaux sémantiques, synonymes, antonymes, mots de la même famille.", + "niveau": "CE1" + }, + { + "nom": "Poursuivre la catégorisation des mots selon différents critères et les met en réseaux : champs lexicaux, réseaux sémantiques, synonymes, antonymes, mots de la même famille.", + "niveau": "CE2" + }, + { + "nom": "Percevoir les niveaux de langue familier, courant, soutenu." + }, + { + "nom": "Percevoir, en situation de réception, le niveau de langue familier.", + "niveau": "CP" + }, + { + "nom": "Percevoir et utiliser les codes oraux adaptés en fonction des contextes.", + "niveau": "CE1" + }, + { + "nom": "Être capable de consulter un dictionnaire et de se repérer dans un article, sur papier ou en version numérique." + }, + { + "nom": "Connaître l'ordre alphabétique.", + "niveau": "CP" + }, + { + "nom": "Consulter des articles de dictionnaire adaptés.", + "niveau": "CE1" + }, + { + "nom": "Mémoriser l'orthographe du lexique le plus couramment employé :- vocabulaire des activités scolaires et des domaines disciplinaires ;- vocabulaire de l'univers familier à l'élève : maison, famille, jeu, vie quotidienne, sensations, sentiments." + }, + { + "nom": "Connaître l'orthographe des mots étudiés et rencontrés fréquemment dans la classe.", + "niveau": "CP" + }, + { + "nom": "Mémoriser les principaux mots invariables." + }, + { + "nom": "Connaître l'orthographe des mots irréguliers étudiés et rencontrés fréquemment dans la classe", + "niveau": "CP" + }, + { + "nom": "Connaître l'orthographe des mots étudiés.", + "niveau": "CE1" + }, + { + "nom": "Être capable de regrouper des mots par séries (familles de mots, mots reliés par des analogies morphologiques)." + }, + { + "nom": "Pour lire et écrire, prendre en compte une connaissance de la composition morphologique et étymologique des mots à un premier niveau.", + "niveau": "CP" + }, + { + "nom": "Raisonner à un premier niveau, en fonction des catégories de mots et de la morphologie pour orthographier correctement les mots.", + "niveau": "CE1" + }, + { + "nom": "Raisonner en fonction des catégories de mots et de la morphologie pour orthographier correctement les mots.", + "niveau": "CE2" + }, + { + "nom": "Identifier la phrase, en distinguer les principaux constituants et les hiérarchiser." + }, + { + "nom": "Comprendre que la phrase est un groupe de mots ordonnés, porteur de sens. Etre attentif à l'ordre des mots.", + "niveau": "CP" + }, + { + "nom": "Disposer d'une première connaissance des constituants d'une phrase simple.", + "niveau": "CE1" + }, + { + "nom": "Reconnaître les principaux constituants de la phrase :- le sujet ;- le verbe (connaissance des propriétés permettant de l'identifier) ;- les compléments." + }, + { + "nom": "Comprendre que certains éléments de la phrase (sujet-verbe, mots à l'intérieur du groupe nominal) fonctionnent ensemble et constituent un système.", + "niveau": "CP" + }, + { + "nom": "Identifier le sujet et le verbe. Repérer d'autres constituants de la phrase sans en connaître la terminologie.", + "niveau": "CE1" + }, + { + "nom": "Différencier les principales classes de mots :- le nom ;- l'article défini, l'article indéfini ;- l'adjectif ;- le verbe ;- le pronom personnel sujet ;- les mots invariables." + }, + { + "nom": "Commencer à identifier quelques natures différentes.", + "niveau": "CP" + }, + { + "nom": "Identifier et nommer des classes de mots : noms, verbes, déterminants, adjectifs.", + "niveau": "CE1" + }, + { + "nom": "Reconnaître le groupe nominal." + }, + { + "nom": "Reconnaître les trois types de phrases : déclaratives, interrogatives et impératives." + }, + { + "nom": "Reconnaître les formes négative et exclamative, et savoir effectuer des transformations." + }, + { + "nom": "En situation d'écoute, s'appuyer sur le sens pour reconnaître le type et la forme d'une phrase.", + "niveau": "CP" + }, + { + "nom": "Utiliser les signes de ponctuation à l'écrit pour commencer à reconnaître les types de phrases.", + "niveau": "CE1" + }, + { + "nom": "Utiliser la ponctuation de fin de phrase ( ! ?) et les signes du discours rapporté (« »)." + }, + { + "nom": "S'appuyer sur les signes de ponctuation pour construire du sens et rend sa lecture orale plus expressive.", + "niveau": "CE1" + }, + { + "nom": "Être capable de mobiliser les « mots de la grammaire » pour résoudre des problèmes d'orthographe, d'écriture et de lecture." + }, + { + "nom": "Commencer à utiliser certains « mots de la grammaire » (la nomenclature ne fait pas l'objet d'un apprentissage systématique).", + "niveau": "CP" + }, + { + "nom": "Comprendre :- le fonctionnement du groupe nominal dans la phrase ;- la notion de « chaîne d'accords » pour déterminant / nom / adjectif (singulier/pluriel ; masculin/féminin)." + }, + { + "nom": "Comprendre que le nom est porteur de « genre » et de « nombre » en écoutant des transformations de phrases à l'oral puis en les observant à l'écrit.", + "niveau": "CP" + }, + { + "nom": "Utiliser :- des marques d'accord pour les noms et adjectifs épithètes : nombre (-s) et genre (-e) ;- d'autres formes de pluriel (-ail/-aux ; -al/-aux…) ;- des marques du féminin quand elles s'entendent dans les noms (lecteur/lectrice…) et les adjectifs (joyeux/joyeuse…)." + }, + { + "nom": "Commencer à produire en situation d'écrit des groupes nominaux corrects (déterminant/nom) notamment en situation de dictée.", + "niveau": "CP" + }, + { + "nom": "Oraliser correctement des pluriels irréguliers (les noms d'animaux par exemple).", + "niveau": "CP" + }, + { + "nom": "Ecrire correctement les groupes nominaux en respectant les accords en genre et en nombre en situation de dictée. Commencer à mobiliser ces connaissances dans l'écriture de textes.", + "niveau": "CE1" + }, + { + "nom": "Identifier la relation sujet – verbe (identification dans des situations simples)." + }, + { + "nom": "Commencer à identifier la relation sujet-verbe à partir de l'observation des effets des transformations liées aux temps et au changement de personne.", + "niveau": "CP" + }, + { + "nom": "Identifier la relation sujet-verbe à partir de l'observation des effets des transformations liées aux temps et au changement de personne.", + "niveau": "CE1" + }, + { + "nom": "Identifier le radical et la terminaison." + }, + { + "nom": "Commencer à identifier la composition des verbes par l'observation et la comparaison.", + "niveau": "CE1" + }, + { + "nom": "Trouver l'infinitif d'un verbe conjugué." + }, + { + "nom": "Mémoriser le présent, l'imparfait, le futur, le passé composé pour :- être et avoir ;- les verbes du 1er groupe ;- les verbes irréguliers du 3ème groupe (faire, aller, dire, venir, pouvoir, voir, vouloir, prendre)." + }, + { + "nom": "Dans un premier temps (préalable à la maîtrise orthographique), oraliser correctement les formes verbales et les transformations opérées sur des phrases.", + "niveau": "CP" + }, + { + "nom": "Opérer des classements de formes verbales pour constituer des outils collectifs basés sur le repérage d'analogies. Commencer à en mémoriser certaines.", + "niveau": "CP" + }, + { + "nom": "Mémoriser de manière plus systématique qu'au CP les formes verbales correctement prononcées en appui sur des outils analogiques.", + "niveau": "CE1" + }, + { + "nom": "Distinguer temps simples et temps composés." + } + ] + } + ] + }, + { + "nom": "Langues vivantes (étrangères ou régionales)", + "categories": [ + { + "nom": "Compréhension de l'oral (écouter et comprendre)", + "competences": [ + { + "nom": "Suivre le fil d'une histoire adaptée à l'âge des élèves et suffisamment étayée" + }, + { + "nom": "Reconnaître des mots familiers, à condition qu'ils soient prononcés clairement et lentement dans un contexte clairement défini, quotidien et familier (petites comptines, courtes chansons, albums de littérature jeunesse, saynètes)." + }, + { + "nom": "Reconnaître des mots, des noms et des chiffres connus, dans des enregistrements courts et simples, à condition que la prononciation soit lente et claire." + }, + { + "nom": "Associer un document oral à un document visuel parmi deux propositions très contrastées." + }, + { + "nom": "Isoler des informations simples dans un message clair." + }, + { + "nom": "Repérer des indices sonores simples dans une variété de supports simples (comptines, chansons, contes, légendes, dialogues, etc.)." + }, + { + "nom": "Associer un document oral à un visuel." + }, + { + "nom": "Comprendre quelques mots familiers et expressions très courantes" + }, + { + "nom": "Reconnaître des consignes, des jours de la semaine, des dates, des nombres connus, des prix, à condition qu'ils soient prononcés clairement et lentement dans un contexte clairement défini, quotidien et familier." + }, + { + "nom": "Repérer des mots ou expressions courtes et simples à condition qu'ils soient prononcés en face-à-face, accompagnés de visuels ou de gestes et éventuellement répétés." + }, + { + "nom": "Comprendre l'ensemble des consignes utilisées en classe." + }, + { + "nom": "Comprendre des mots familiers et des expressions courtes dans une conversation simple à condition que l'interlocuteur parle lentement et très distinctement." + }, + { + "nom": "Comprendre et agir" + }, + { + "nom": "Montrer sa compréhension par une action ou une gestuelle adaptée." + }, + { + "nom": "Associer un document oral à un visuel." + }, + { + "nom": "Suivre des consignes courtes et simples." + } + ] + }, + { + "nom": "Compréhension de l'écrit (lire)", + "competences": [ + { + "nom": "Reconnaître des mots ou des messages écrits familiers" + }, + { + "nom": "Reconnaître des mots familiers accompagnés d'images comme, par exemple, dans un menu de restaurant illustré par des photos ou dans un livre d'images utilisant un vocabulaire familier." + }, + { + "nom": "Comprendre quelques affichages, des panneaux simples et illustrés de la vie quotidienne." + }, + { + "nom": "Reconnaître et comprendre de petits messages simples et brefs sur une carte postale, une lettre, un courriel, des annonces ou de courts récits illustrés, au sujet d'activités quotidiennes." + }, + { + "nom": "Trouver des informations dans des écrits très simples" + }, + { + "nom": "Identifier, dans de très courts textes illustrés, quelques éléments d'information simples, relatifs à une thématique familière et quotidienne." + }, + { + "nom": "Trouver des informations sur les lieux, les dates et les prix, sur des panneaux d'affichage, des prospectus, des affiches comportant des visuels." + }, + { + "nom": "Isoler des informations simples dans un texte court et d'un niveau adapté." + }, + { + "nom": "Se faire une idée du sens d'un texte informatif court et assez simple, accompagné d'un document visuel." + }, + { + "nom": "Identifier des informations dans un texte court pour suivre des indications brèves et simples." + }, + { + "nom": "Identifier, dans de courts textes, des informations simples relatives à une thématique clairement identifiée." + } + ] + }, + { + "nom": "Expression orale en continu (parler en continu)", + "competences": [ + { + "nom": "Repères phonologiques" + }, + { + "nom": "Prononcer un répertoire de quelques mots et expressions mémorisés, de manière compréhensible." + }, + { + "nom": "Prononcer un répertoire d'expressions et de mots mémorisés, de manière compréhensible." + }, + { + "nom": "Reproduire un modèle oral" + }, + { + "nom": "Reproduire un modèle oral court." + }, + { + "nom": "Décrire très brièvement des personnes, des objets ou des animaux familiers en utilisant des modèles mémorisés." + }, + { + "nom": "Lire et reproduire un modèle oral court (répéter, réciter, etc.)." + }, + { + "nom": "Décrire brièvement des personnes, des objets ou des animaux familiers après y avoir été entraîné." + }, + { + "nom": "Raconter" + }, + { + "nom": "Produire une phrase courte pour raconter une partie d'une histoire travaillée en classe, en s'appuyant sur des images et des expressions toutes faites." + }, + { + "nom": "Raconter une histoire très brève, en juxtaposant des expressions ou des phrases simples et en s'aidant d'images." + }, + { + "nom": "Se décrire en utilisant des expressions courtes ou proches des modèles rencontrés lors des apprentissages" + }, + { + "nom": "Se présenter de manière succincte avec des mots et des expressions toutes faites, en étant soutenu et en ayant préparé son propos." + }, + { + "nom": "Exprimer ses goûts de façon très succincte." + }, + { + "nom": "Se présenter et se décrire avec des mots et des expressions simples, à condition d'avoir pu préparer son discours." + }, + { + "nom": "Exprimer ses activités préférées à l'aide d'expressions toutes faites." + } + ] + }, + { + "nom": "Expression orale en interaction (réagir et dialoguer)", + "competences": [ + { + "nom": "Échanger des informations simples" + }, + { + "nom": "Saluer un interlocuteur et se présenter à lui." + }, + { + "nom": "Comprendre et utiliser des expressions courantes." + }, + { + "nom": "Comprendre et utiliser des nombres simples dans les conversations courantes." + }, + { + "nom": "Poser des questions très simples pour obtenir des informations, et comprendre un ou deux mots de la réponse." + }, + { + "nom": "Suivre des directives ou consignes simples, brèves ou routinières." + }, + { + "nom": "Demander de ses nouvelles à quelqu'un et réagir en utilisant des formules de politesse." + }, + { + "nom": "Répondre à des questions très simples et en poser de manière ritualisée sur des sujets familiers et sur les habitudes quotidiennes." + }, + { + "nom": "Réagir à des affirmations très simples et en émettre, en utilisant des formules courtes ou toutes faites, en s'aidant éventuellement de gestes pour renforcer la communication." + }, + { + "nom": "Exprimer des émotions et formuler des souhaits élémentaires" + }, + { + "nom": "Formuler des souhaits élémentaires." + }, + { + "nom": "Exprimer ses émotions en utilisant le langage corporel (gestes, expressions du visage, etc.) et quelques adjectifs simples." + }, + { + "nom": "Exprimer simplement des sentiments et émotions, à l'aide d'expressions idiomatiques ou toutes faites pour:- demander et dire comment on se porte ;- réagir à la réponse donnée." + }, + { + "nom": "Exprimer de manière simple (y compris par onomatopées) des émotions ou réactions telles que la joie, la tristesse, la déception, la peur ou la surprise." + }, + { + "nom": "Clarifier ou faire clarifier un point" + }, + { + "nom": "Signifier son incompréhension, notamment par des gestes." + }, + { + "nom": "Clarifier ou faire clarifier des points très simples, notamment en situation de communication de classe, comme :- épeler des mots familiers ;- demander de répéter et répéter soi-même un mot ou une phrase très brève ;- demander le sens ou la traduction d'un mot ;- dire qu'on ne comprend pas." + }, + { + "nom": "Répéter une consigne simple et connue afin de s'assurer de sa bonne compréhension." + }, + { + "nom": "Utiliser des procédés très simples pour commencer, poursuivre et terminer une conversation brève" + }, + { + "nom": "Reconnaître de simples salutations et quelques formules de politesse." + }, + { + "nom": "Saluer ou remercier." + }, + { + "nom": "Etablir un contact social en utilisant quelques formules de politesse : salutation et prise de congé, présentations et expressions du type « merci », « s'il vous plaît », « pardon »." + }, + { + "nom": "Réagir à des propositions dans des situations de la vie courante (remercier, féliciter, présenter des excuses, accepter, refuser)." + }, + { + "nom": "Demander des informations complémentaires très simples (« et ? », « qui est-ce ? », « qu'est-ce que c'est ? », etc.) si le fil des échanges porte sur des faits familiers et simples, avec un interlocuteur qui adopte un débit lent et accepte de répéter." + }, + { + "nom": "Clore une conversation à l'aide de mots très simples comme « merci », « au revoir », « d'accord »." + } + ] + }, + { + "nom": "Expression écrite (écrire et réagir à l'écrit)", + "competences": [ + { + "nom": "Épeler, copier ou écrire des éléments connus" + }, + { + "nom": "Réciter l'alphabet et épeler quelques mots familiers pour les dicter dans la langue cible." + }, + { + "nom": "Copier des mots isolés, connus." + }, + { + "nom": "Copier quelques mots connus pour évoquer très simplement certains objets familiers." + }, + { + "nom": "Recopier une très courte phrase extraite d'une histoire ou de la description d'un événement." + }, + { + "nom": "Répondre par un mot simple à quelques questions orales simples sur des situations familières." + }, + { + "nom": "Copier et écrire sous la dictée quelques mots isolés déjà connus." + }, + { + "nom": "Ecrire des mots et des expressions pour évoquer ou décrire très simplement certains objets familiers." + }, + { + "nom": "Copier quelques phrases et expressions, simples et déjà connues, pour se décrire et décrire un personnage imaginaire." + }, + { + "nom": "Renseigner un questionnaire très simple le concernant (y compris sur un support numérique)." + }, + { + "nom": "Réagir très simplement par écrit à des propos et à des commentaires, en réinvestissant à l'écrit des expressions simples qui ont été travaillées à l'oral." + } + ] + }, + { + "nom": "Médiation", + "competences": [ + { + "nom": "Identifier les repères culturels" + }, + { + "nom": "Identifier une similitude ou une différence culturelle." + }, + { + "nom": "Expliciter un message, une situation, un document pour autrui" + }, + { + "nom": "Indiquer avec des gestes simples les besoins élémentaires d'une tierce personne dans une situation précise." + }, + { + "nom": "Pointer des informations simples et prévisibles d'un intérêt immédiat, données dans des supports visuels explicites (documents iconographiques, pictogrammes, panneaux et affiches)." + }, + { + "nom": "Indiquer avec des mots et des gestes simples les besoins élémentaires d'une tierce personne dans une situation précise." + }, + { + "nom": "Attirer l'attention sur des informations simples et prévisibles d'un intérêt immédiat, données dans des supports courts et simples tels que des panneaux, des annonces, affiches, programmes, dépliants, etc." + }, + { + "nom": "Animer un travail collectif, coopérer et contribuer à des échanges interculturels" + }, + { + "nom": "Demander aux participants des contributions à des tâches élémentaires, notamment par des mots simples soutenus par une gestuelle explicitant la tâche à accomplir." + }, + { + "nom": "Signifier, notamment par des gestes, qu'il a compris ou qu'il est d'accord." + }, + { + "nom": "Demander aux autres, notamment par des gestes, s'ils ont compris ou s'ils sont d'accord." + }, + { + "nom": "Faciliter un échange interculturel en se montrant accueillant et en manifestant son intérêt avec des mots simples, des expressions non verbales ou des gestes." + }, + { + "nom": "Utiliser des mots simples, des expressions non verbales ou des gestes pour montrer son intérêt pour une idée." + }, + { + "nom": "Demander aux participants leur avis ou des contributions à des tâches élémentaires, à l'aide d'expressions mémorisées." + }, + { + "nom": "Dire très simplement qu'il a compris, qu'il est d'accord, que tout va bien (dans la tâche à accomplir ou dans la communication)." + }, + { + "nom": "Demander aux autres s'ils ont compris, s'ils sont d'accord, s'ils ont un problème." + } + ] + }, + { + "nom": "L'enfant", + "competences": [ + { + "nom": "Soi, le corps, les vêtements." + }, + { + "nom": "La famille." + }, + { + "nom": "L'organisation de la journée." + }, + { + "nom": "Les habitudes de l'enfant." + }, + { + "nom": "Les trajets quotidiens de l'enfant." + }, + { + "nom": "Les usages dans les relations à l'école." + }, + { + "nom": "Le temps, les grandes périodes de l'année, de la vie." + }, + { + "nom": "Sensations, gouts et sentiments." + }, + { + "nom": "Éléments de description physique et morale." + } + ] + }, + { + "nom": "La classe", + "competences": [ + { + "nom": "L'alphabet." + }, + { + "nom": "Les nombres." + }, + { + "nom": "Les repères temporels." + }, + { + "nom": "Climat et météo." + }, + { + "nom": "Les rituels." + }, + { + "nom": "Les règles et règlements dans la classe." + }, + { + "nom": "Les activités scolaires." + }, + { + "nom": "Le sport." + }, + { + "nom": "Les loisirs artistiques." + }, + { + "nom": "L'amitié." + } + ] + }, + { + "nom": "L'univers enfantin", + "competences": [ + { + "nom": "La maison, l'environnement immédiat et concret." + }, + { + "nom": "La vie quotidienne, les commerces, les lieux publics." + }, + { + "nom": "L'environnement géographique ou culturel proche." + }, + { + "nom": "Les animaux." + }, + { + "nom": "Les contes et légendes." + }, + { + "nom": "Les monstres, fées et autres références culturelles de la littérature enfantine." + }, + { + "nom": "Les comptines, les chansons." + }, + { + "nom": "La littérature enfantine." + }, + { + "nom": "Quelques villes, campagnes et paysages typiques." + }, + { + "nom": "Les drapeaux et monnaies." + }, + { + "nom": "Les grandes fêtes et coutumes." + }, + { + "nom": "Les recettes." + } + ] + } + ] + }, + { + "nom": "Enseignements artistiques", + "categories": [ + { + "nom": "Arts plastiques", + "competences": [ + { + "nom": "Réaliser et donner à voir, individuellement ou collectivement, des productions plastiques de natures diverses.", + "fin_cycle": true + }, + { + "nom": "Proposer des réponses inventives dans un projet individuel ou collectif.", + "fin_cycle": true + }, + { + "nom": "Coopérer dans un projet artistique.", + "fin_cycle": true + }, + { + "nom": "S'exprimer sur sa production, celle de ses pairs, sur l'art.", + "fin_cycle": true + }, + { + "nom": "Comparer quelques œuvres d'art.", + "fin_cycle": true + }, + { + "nom": "S'approprier par les sens les éléments du langage plastique : matière, support, couleur..." + }, + { + "nom": "Observer les effets produits par ses gestes, par les outils utilisés." + }, + { + "nom": "Tirer parti de trouvailles fortuites, saisir les effets du hasard." + }, + { + "nom": "Représenter le monde environnant ou donner forme à son imaginaire en explorant la diversité des domaines (dessin, collage, modelage, sculpture, photographie ...)." + }, + { + "nom": "Respecter l'espace, les outils et les matériaux partagés." + }, + { + "nom": "Mener à terme une production individuelle dans le cadre d'un projet accompagné par le professeur." + }, + { + "nom": "Montrer sans réticence ses productions et regarder celles des autres." + }, + { + "nom": "Prendre la parole devant un groupe pour partager ses trouvailles, s'intéresser à celles découvertes dans des œuvres d'art." + }, + { + "nom": "Formuler ses émotions, entendre et respecter celles des autres." + }, + { + "nom": "Repérer les éléments du langage plastique dans une production : couleurs, formes, matières, support..." + }, + { + "nom": "Effectuer des choix parmi les images rencontrées, établir un premier lien entre son univers visuel et la culture artistique." + }, + { + "nom": "Exprimer ses émotions lors de la rencontre avec des œuvres d'art, manifester son intérêt pour la rencontre directe avec des œuvres." + }, + { + "nom": "S'approprier quelques œuvres de domaines et d'époques variés appartenant au patrimoine national et mondial." + }, + { + "nom": "S'ouvrir à la diversité des pratiques et des cultures artistiques." + }, + { + "nom": "Utiliser le dessin dans toute sa diversité comme moyen d'expression." + }, + { + "nom": "Employer divers outils, dont ceux numériques, pour représenter." + }, + { + "nom": "Prendre en compte l'influence des outils, supports, matériaux, gestes sur la représentation en deux et en trois dimensions." + }, + { + "nom": "Connaitre diverses formes artistiques de représentation du monde : œuvres contemporaines et du passé, occidentales et extra occidentales." + }, + { + "nom": "Exprimer sa sensibilité et son imagination en s'emparant des éléments du langage plastique." + }, + { + "nom": "Expérimenter les effets des couleurs, des matériaux, des supports... en explorant l'organisation et la composition plastiques." + }, + { + "nom": "Exprimer ses émotions et sa sensibilité en confrontant sa perception à celle d'autres élèves." + }, + { + "nom": "Réaliser des productions plastiques pour raconter, témoigner." + }, + { + "nom": "Transformer ou restructurer des images ou des objets." + }, + { + "nom": "Articuler le texte et l'image à des fins d'illustration, de création." + } + ] + }, + { + "nom": "Éducation musicale", + "competences": [ + { + "nom": "Expérimenter sa voix parlée et chantée, explorer ses paramètres, la mobiliser au bénéfice d'une reproduction expressive.", + "fin_cycle": true + }, + { + "nom": "Connaitre et mettre en œuvre les conditions d'une écoute attentive et précise.", + "fin_cycle": true + }, + { + "nom": "Imaginer des organisations simples ; créer des sons et maitriser leur succession.", + "fin_cycle": true + }, + { + "nom": "Exprimer sa sensibilité et exercer son esprit critique tout en respectant les gouts et points de vue de chacun.", + "fin_cycle": true + }, + { + "nom": "Chanter une mélodie simple avec une intonation juste, chanter une comptine ou un chant par imitation." + }, + { + "nom": "Interpréter un chant avec expressivité." + }, + { + "nom": "Décrire et comparer des éléments sonores." + }, + { + "nom": "Comparer des musiques et identifier des ressemblances et des différences." + }, + { + "nom": "Imaginer des représentations graphiques ou corporelles de la musique." + }, + { + "nom": "Inventer une organisation simple à partir de différents éléments sonores." + }, + { + "nom": "Exprimer ses émotions, ses sentiments et ses préférences." + }, + { + "nom": "Écouter et respecter l'avis des autres et l'expression de leur sensibilité." + }, + { + "nom": "Reproduire un modèle mélodique, rythmique." + }, + { + "nom": "Chanter une mélodie simple avec une intonation juste." + }, + { + "nom": "Chanter une comptine, un chant par imitation." + }, + { + "nom": "Interpréter un chant avec expressivité (phrasé, articulation du texte) en respectant ses phrases musicales." + }, + { + "nom": "Mobiliser son corps pour interpréter." + }, + { + "nom": "Connaître les principaux registres vocaux : voix parlée/chantée, aigu, grave." + }, + { + "nom": "Maitriser le éléments constitutifs d'une production vocale : respiration, articulation, posture du corps." + }, + { + "nom": "Connaître un répertoire varié de chansons et de comptines." + }, + { + "nom": "Maitriser les éléments de vocabulaire concernant l'usage musical de la voix : fort, doux, aigu, grave, faux, juste, etc." + }, + { + "nom": "Décrire et comparer des éléments sonores, identifier des éléments communs et contrastés." + }, + { + "nom": "Repérer une organisation simple : récurrence d'une mélodie, d'un motif rythmique, d'un thème, etc." + }, + { + "nom": "Comparer des musiques et identifier des ressemblances et des différences." + }, + { + "nom": "Maitriser le lexique élémentaire pour décrire la musique : timbre, hauteur, formes simples, intensité, tempo." + }, + { + "nom": "Connaître quelques grandes œuvres du patrimoine." + }, + { + "nom": "Avoir quelques repères simples dans l'espace et le temps." + }, + { + "nom": "Expérimenter les paramètres du son : intensité, hauteur, timbre, durée." + }, + { + "nom": "Imaginer des représentations graphiques ou corporelles de la musique." + }, + { + "nom": "Inventer une organisation simple à partir d'éléments sonores travaillés." + }, + { + "nom": "Connaître les éléments de vocabulaire liés aux paramètres du son (intensité, durée, hauteur, timbre)." + }, + { + "nom": "Maitriser les postures du musicien : écouter, respecter l'autre, jouer ensemble." + }, + { + "nom": "Comprendre la diversité des matériaux sonores." + }, + { + "nom": "Exprimer ses émotions, ses sentiments et ses préférences artistiques." + }, + { + "nom": "Écouter et respecter l'avis des autres et l'expression de leur sensibilité." + }, + { + "nom": "Respecter les règles et les exigences d'une production musicale collective." + }, + { + "nom": "Utiliser un vocabulaire adapté à l'expression de son avis." + }, + { + "nom": "Mettre en œuvre les conditions d'un travail collectif : concentration, écoute, respect..." + }, + { + "nom": "Connaître le règles et contraintes du travail collectif." + } + ] + } + ] + }, + { + "nom": "Éducation physique et sportive", + "categories": [ + { + "nom": "Produire une performance optimale, mesurable à une échéance donnée.", + "competences": [ + { + "nom": "Courir, sauter, lancer à des intensités et des durées variables dans des contextes adaptés.", + "fin_cycle": true + }, + { + "nom": "Savoir différencier : courir vite et courir longtemps / lancer loin et lancer précis / sauter haut et sauter loin.", + "fin_cycle": true + }, + { + "nom": "Accepter de viser une performance mesurée et de se confronter aux autres.", + "fin_cycle": true + }, + { + "nom": "Remplir quelques rôles spécifiques.", + "fin_cycle": true + }, + { + "nom": "Transformer sa motricité spontanée pour maitriser les actions motrices ; courir, sauter, lancer." + }, + { + "nom": "Utiliser sa main d'adresse et son pied d'appel et construire une adresse gestuelle et corporelle bilatérale." + }, + { + "nom": "Mobiliser de façon optimale ses ressources pour produire des efforts à des intensités variables." + }, + { + "nom": "Pendant l'action, prendre des repères extérieurs à son corps pour percevoir : espace, temps, durée et effort." + }, + { + "nom": "Respecter les règles de sécurité édictées par le professeur." + } + ] + }, + { + "nom": "Adapter ses déplacements à des environnements variés.", + "competences": [ + { + "nom": "Se déplacer dans l'eau sur une quinzaine de mètres sans appui et après un temps d'immersion.", + "fin_cycle": true + }, + { + "nom": "Réaliser un parcours en adaptant ses déplacements à un environnement inhabituel. L'espace est aménagé et sécurisé.", + "fin_cycle": true + }, + { + "nom": "Respecter les règles de sécurité qui s'appliquent.", + "fin_cycle": true + }, + { + "nom": "Transformer sa motricité spontanée pour maitriser les actions motrices." + }, + { + "nom": "S'engager sans appréhension pour se déplacer dans différents environnements." + }, + { + "nom": "Lire le milieu et adapter ses déplacements à ses contraintes." + }, + { + "nom": "Respecter les règles essentielles de sécurité." + }, + { + "nom": "Reconnaitre une situation à risque." + } + ] + }, + { + "nom": "S'exprimer devant les autres par une prestation artistique et/ou acrobatique", + "competences": [ + { + "nom": "Mobiliser le pouvoir expressif du corps, en reproduisant une séquence simple d'actions apprise ou en présentant une action inventée.", + "fin_cycle": true + }, + { + "nom": "S'adapter au rythme, mémoriser des pas, des figures, des éléments et des enchainements pour réaliser des actions individuelles et collectives.", + "fin_cycle": true + }, + { + "nom": "S'exposer aux autres : s'engager avec facilité dans des situations d'expression personnelle sans crainte de se montrer." + }, + { + "nom": "Exploiter le pouvoir expressif du corps en transformant sa motricité et en construisant un répertoire d'actions nouvelles à visée esthétique." + }, + { + "nom": "S'engager en sécurité dans des situations acrobatiques en construisant de nouveaux pouvoirs moteurs." + }, + { + "nom": "Synchroniser ses actions avec celles de partenaires." + } + ] + }, + { + "nom": "Conduire et maitriser un affrontement collectif ou interindividuel", + "competences": [ + { + "nom": "Dans des situations aménagées et très variées, s'engager dans un affrontement individuel ou collectif en respectant les règles du jeu.", + "fin_cycle": true + }, + { + "nom": "Dans des situations aménagées et très variées, contrôler son engagement moteur et affectif pour réussir des actions simples.", + "fin_cycle": true + }, + { + "nom": "Dans des situations aménagées et très variées, connaitre le but du jeu.", + "fin_cycle": true + }, + { + "nom": "Dans des situations aménagées et très variées, reconnaitre ses partenaires et ses adversaires.", + "fin_cycle": true + }, + { + "nom": "Rechercher le gain du jeu, de la rencontre." + }, + { + "nom": "Comprendre le but du jeu et orienter ses actions vers la cible." + }, + { + "nom": "Accepter l'opposition et la coopération." + }, + { + "nom": "S'adapter aux actions d'un adversaire." + }, + { + "nom": "Coordonner des actions motrices simples." + }, + { + "nom": "S'informer, prendre des repères pour agir seul ou avec les autres." + }, + { + "nom": "Respecter les règles essentielles de jeu et de sécurité." + } + ] + } + ] + }, + { + "nom": "Questionner le monde", + "categories": [ + { + "nom": "Qu'est-ce que la matière ?", + "competences": [ + { + "nom": "Identifier les trois états de la matière et observer des changements d'états.", + "fin_cycle": true + }, + { + "nom": "Identifier un changement d'état de l'eau dans un phénomène de la vie quotidienne.", + "fin_cycle": true + }, + { + "nom": "Comparer et mesurer la température, le volume, la masse de l'eau à l'état liquide et à l'état solide." + }, + { + "nom": "Reconnaitre les états de l'eau et leur manifestation dans divers phénomènes naturels." + }, + { + "nom": "Mettre en œuvre des expériences simples impliquant l'eau et/ou l'air." + }, + { + "nom": "Connaître quelques propriétés des solides, des liquides et des gaz." + }, + { + "nom": "Comprendre les notions de changements d'états de la matière, notamment la solidification, la condensation et la fusion." + }, + { + "nom": "Connaître les états de l'eau (liquide, glace, vapeur d'eau)." + }, + { + "nom": "Connaître l'existence, l'effet et quelques propriétés de l'air (matérialité et compressibilité de l'air)." + } + ] + }, + { + "nom": "Comment reconnaitre le monde vivant ?", + "competences": [ + { + "nom": "Connaitre des caractéristiques du monde vivant, ses interactions, sa diversité.", + "fin_cycle": true + }, + { + "nom": "Reconnaitre des comportements favorables à sa santé.", + "fin_cycle": true + }, + { + "nom": "Identifier ce qui est animal, végétal, minéral ou élaboré par des êtres vivants." + }, + { + "nom": "Développement d'animaux et de végétaux." + }, + { + "nom": "Comprendre le cycle de vie des êtres vivants." + }, + { + "nom": "Connaître les régimes alimentaires de quelques animaux." + }, + { + "nom": "Connaître quelques besoins vitaux des végétaux." + }, + { + "nom": "Identifier les interactions des êtres vivants entre eux et avec leur milieu." + }, + { + "nom": "Prendre conscience de la diversité des organismes vivants présents dans un milieu et de leur interdépendance." + }, + { + "nom": "Comprendre la notion de relations alimentaires entre les organismes vivants." + }, + { + "nom": "Aborder la notion de chaines de prédation." + }, + { + "nom": "Identifier quelques interactions dans l'école." + }, + { + "nom": "Repérer les éléments permettant la réalisation d'un mouvement corporel." + }, + { + "nom": "Mesurer et observer la croissance de son corps." + }, + { + "nom": "Comprendre la notion de croissance (taille, masse, pointure)." + }, + { + "nom": "Observer les modifications de la dentition." + }, + { + "nom": "Mettre en œuvre et apprécier quelques règles d'hygiène de vie : variété alimentaire, activité physique, capacité à se relaxer et mise en relation de son âge et de ses besoins en sommeil, habitudes quotidiennes de propreté (dents, mains, corps)." + }, + { + "nom": "Connaître les catégories d'aliments, leur origine." + }, + { + "nom": "Comprendre les apports spécifiques des aliments (apport d'énergie : manger pour bouger)." + }, + { + "nom": "Aborder la notion d'équilibre alimentaire (sur un repas, sur une journée, sur la semaine)." + }, + { + "nom": "Comprendre les effets positifs d'une pratique physique régulière sur l'organisme." + }, + { + "nom": "Connaître les changements des rythmes d'activité quotidiens (sommeil, activité, repos...)." + } + ] + }, + { + "nom": "Les objets techniques. Qu'est-ce que c'est ? À quels besoins répondent-ils ? Comment fonctionnent-ils ?", + "competences": [ + { + "nom": "Comprendre la fonction et le fonctionnement d'objets fabriqués.", + "fin_cycle": true + }, + { + "nom": "Réaliser quelques objets et circuits électriques simples, en respectant des règles élémentaires de sécurité.", + "fin_cycle": true + }, + { + "nom": "Commencer à s'approprier un environnement numérique.", + "fin_cycle": true + }, + { + "nom": "Observer et utiliser des objets techniques et identifier leur fonction." + }, + { + "nom": "Identifier des activités de la vie quotidienne ou professionnelle faisant appel à des outils et objets techniques." + }, + { + "nom": "Réaliser des objets techniques par association d'éléments existants en suivant un schéma de montage." + }, + { + "nom": "Identifier les propriétés de la matière vis-à-vis du courant électrique." + }, + { + "nom": "Différencier des objets selon qu'ils sont alimentés avec des piles ou avec le courant du secteur." + }, + { + "nom": "Connaître les constituants et fonctionnement d'un circuit électrique simple." + }, + { + "nom": "Connaître des exemples de bon conducteurs et d'isolants." + }, + { + "nom": "Comprendre le rôle de l'interrupteur." + }, + { + "nom": "Mettre en œuvre les règles élémentaires de sécurité." + }, + { + "nom": "Décrire l'architecture simple d'un dispositif informatique." + }, + { + "nom": "Avoir acquis une familiarisation suffisante avec le traitement de texte et en faire un usage rationnel." + } + ] + }, + { + "nom": "Se situer dans l'espace.", + "competences": [ + { + "nom": "Se repérer dans l'espace et le représenter.", + "fin_cycle": true + }, + { + "nom": "Situer un lieu sur une carte, sur un globe ou sur un écran informatique.", + "fin_cycle": true + }, + { + "nom": "Se repérer dans son environnement proche." + }, + { + "nom": "Situer des objets ou des personnes les uns par rapport aux autres ou par rapport à d'autres repères." + }, + { + "nom": "Maitriser le vocabulaire permettant de définir des positions (gauche, droite, au-dessus, en dessous, sur, sous, devant, derrière, près, loin, premier plan, second plan, nord, sud, est, ouest...)." + }, + { + "nom": "Maitriser le vocabulaire permettant de définir des déplacements (avancer, reculer, tourner à droite/à gauche, monter, descendre...)." + }, + { + "nom": "Produire des représentations des espaces familiers (les espaces scolaires extérieurs proches, le village, le quartier) et moins familiers (vécus lors de sorties)." + }, + { + "nom": "Aborder quelques modes de représentation de l'espace." + }, + { + "nom": "Lire des plans, se repérer sur des cartes." + }, + { + "nom": "Connaître les éléments constitutifs d'une carte : titre, échelle, orientation, légende." + }, + { + "nom": "Identifier des représentations globales de la Terre et du monde." + }, + { + "nom": "Situer les espaces étudiés sur une carte ou un globe." + }, + { + "nom": "Repérer la position de sa région, de la France, de l'Europe et des autres continents." + }, + { + "nom": "Savoir que la Terre fait partie d'un univers très vaste composé de différents types d'astres." + }, + { + "nom": "De l'espace connu à l'espace lointain : Connaître les pays, les continents, les océans." + }, + { + "nom": "De l'espace connu à l'espace lointain : Connaître la Terre et les astres (la Lune, le Soleil...)." + } + ] + }, + { + "nom": "Se situer dans le temps.", + "competences": [ + { + "nom": "Se repérer dans le temps et mesurer des durées.", + "fin_cycle": true + }, + { + "nom": "Repérer et situer quelques évènements dans un temps long.", + "fin_cycle": true + }, + { + "nom": "Identifier les rythmes cycliques du temps." + }, + { + "nom": "Lire l'heure et les dates." + }, + { + "nom": "Maitriser l'alternance jour/nuit." + }, + { + "nom": "Comprendre le caractère cyclique des jours, des semaines, des mois, des saisons." + }, + { + "nom": "Savoir que la journée est divisée en heures." + }, + { + "nom": "Savoir que la semaine est divisée en jours." + }, + { + "nom": "Comparer, estimer, mesurer des durées." + }, + { + "nom": "Connaître les unités de mesure usuelles de durées : jour, semaine, heure, minute, seconde, mois, année, siècle, millénaire." + }, + { + "nom": "Maitriser les relations entre ces unités." + }, + { + "nom": "Situer des évènements les uns par rapport aux autres." + }, + { + "nom": "Connaître les évènements quotidiens, hebdomadaires, récurrents, et leur positionnement les uns par rapport aux autres." + }, + { + "nom": "Comprendre les notions de continuité et succession, d'antériorité et postériorité, de simultanéité." + }, + { + "nom": "Prendre conscience que le temps qui passe est irréversible." + }, + { + "nom": "Aborder le temps des parents." + }, + { + "nom": "Connaître les générations vivantes et la mémoire familiale." + }, + { + "nom": "Comprendre l'évolution des sociétés à travers des modes de vie (alimentation, habitat, vêtements, outils, guerre, déplacements...) et des techniques à diverses époques." + }, + { + "nom": "Repérer des périodes de l'histoire du monde occidental et de la France en particulier, quelques grandes dates et personnages clés." + }, + { + "nom": "Connaître quelques personnages et dates." + } + ] + }, + { + "nom": "Explorer les organisations du monde.", + "competences": [ + { + "nom": "Comparer quelques modes de vie des hommes et des femmes, et quelques représentations du monde.", + "fin_cycle": true + }, + { + "nom": "Identifier quelques interactions élémentaires entre mode de vie et environnement.", + "fin_cycle": true + }, + { + "nom": "Comprendre qu'un espace est organisé.", + "fin_cycle": true + }, + { + "nom": "Identifier des paysages.", + "fin_cycle": true + }, + { + "nom": "Comparer des modes de vie (alimentation, habitat, vêtements, outils, guerre, déplacements...) à différentes époques ou de différentes cultures." + }, + { + "nom": "Connaître quelques éléments permettant de comparer des modes de vie : alimentation, habitat, vêtements, outils, guerre, déplacements..." + }, + { + "nom": "Connaître quelques modes de vie des hommes et des femmes et quelques représentations du monde à travers le temps historique." + }, + { + "nom": "Connaître les modes de vie caractéristiques dans quelques espaces très emblématiques." + }, + { + "nom": "Identifier et comprendre des interactions simples entre modes de vie et environnement à partir d'un exemple (l'alimentation, l'habitat, le vêtement ou les déplacements)." + }, + { + "nom": "Découvrir le quartier, le village, la ville: ses principaux espaces et ses principales fonctions." + }, + { + "nom": "Découvrir des espaces très proches (école, parc, parcours régulier...) puis proches et plus complexes (quartier, village, centre-ville, centre commercial...), en construisant progressivement des légendes." + }, + { + "nom": "Découvrir des organisations spatiales, à partir de photographies paysagères de terrain et aériennes; à partir de documents cartographiques." + }, + { + "nom": "Aborder une carte thématique simple des villes en France." + }, + { + "nom": "Connaître le rôle joué par certains acteurs urbains ou du village (la municipalité, les habitants, les commerçants, etc.) dans l'environnement, à partir d'un exemple lié au traitement des déchets, à la place de la nature en ville, aux déplacements ou à la qualité de l'air." + }, + { + "nom": "Reconnaitre différents paysages: les littoraux, les massifs montagneux, les campagnes, les villes, les déserts..." + }, + { + "nom": "Connaître les principaux paysages français en s'appuyant sur des lieux de vie." + }, + { + "nom": "Connaître quelques paysages de la planète et leurs caractéristiques." + }, + { + "nom": "Comparer des paysages d'aujourd'hui et du passé pour mettre en évidence quelques transformations." + } + ] + } + ] + }, + { + "nom": "Mathématiques", + "categories": [ + { + "nom": "Nombres et calculs", + "competences": [ + { + "nom": "Comprendre et utiliser des nombres entiers pour dénombrer, ordonner, repérer, comparer.", + "fin_cycle": true + }, + { + "nom": "Nommer, lire, écrire, représenter des nombres entiers.", + "fin_cycle": true + }, + { + "nom": "Résoudre des problèmes en utilisant des nombres entiers et le calcul.", + "fin_cycle": true + }, + { + "nom": "Calculer avec des nombres entiers.", + "fin_cycle": true + }, + { + "nom": "Dénombrer, constituer et comparer des collections en les organisant, notamment par des groupements par dizaines, centaines et milliers.- Désignation du nombre d'éléments de diverses façons : écritures additives ou multiplicatives, écritures en unités de numération, écriture usuelle ;- Utilisation de ces diverses désignations pour comparer des collections." + }, + { + "nom": "Dénombrer des collections en les organisant.", + "niveau": "CP" + }, + { + "nom": "Dénombrer des collections en les organisant.", + "niveau": "CE1" + }, + { + "nom": "Dénombrer des collections en les organisant.", + "niveau": "CE2" + }, + { + "nom": "Comprendre la notion de centaine.", + "niveau": "CE1" + }, + { + "nom": "Différencier le chiffre des centaines, le chiffre des dizaines et le chiffre des unités.", + "niveau": "CE1" + }, + { + "nom": "Différencier le chiffre des milliers, le chiffre des centaines, le chiffre des dizaines et le chiffre des unités.", + "niveau": "CE2" + }, + { + "nom": "Comprendre la notion de millier.", + "niveau": "CE2" + }, + { + "nom": "Repérer un rang ou une position dans une file ou sur une piste." + }, + { + "nom": "Repérer un rang ou une position dans une file ou dans une liste d'objets ou de personnes, le nombre d'objets ou de personnes étant inférieur à 30.", + "niveau": "CP" + }, + { + "nom": "Placer des nombres sur un axe ou nomme le nombre identifié sur un axe.", + "niveau": "CE1" + }, + { + "nom": "Repérer un rang ou une position dans une file ou dans une liste d'objets ou de personnes, le nombre d'objets ou de personnes étant inférieur à 1 000.", + "niveau": "CE1" + }, + { + "nom": "Placer des nombres sur un axe ou nommer le nombre identifié sur un axe.", + "niveau": "CE2" + }, + { + "nom": "Repérer un rang ou une position dans une file ou dans une liste d'objets ou de personnes, le nombre d'objets ou de personnes étant inférieur à 10 000.", + "niveau": "CE2" + }, + { + "nom": "Faire le lien entre le rang dans une liste et le nombre d'éléments qui le précèdent :- relation entre ordinaux et cardinaux." + }, + { + "nom": "Faire le lien entre le rang dans une liste et le nombre d'éléments qui le précèdent pour des nombres inférieurs à 20.", + "niveau": "CP" + }, + { + "nom": "Faire le lien entre le rang dans une liste et le nombre d'éléments qui le précèdent pour des nombres inférieurs à 1 000.", + "niveau": "CE1" + }, + { + "nom": "Faire le lien entre le rang dans une liste et le nombre d'éléments qui le précèdent pour des nombres inférieurs à 10 000.", + "niveau": "CE2" + }, + { + "nom": "Comparer, ranger, encadrer, intercaler des nombres entiers, en utilisant les symboles =, ≠, <, > :- égalité traduisant l'équivalence de deux désignations du même nombre ;- ordre ;- sens des symboles =, ≠, <, >." + }, + { + "nom": "Comparer, encadrer, intercaler des nombres entiers en utilisant les symboles =, < et >.", + "niveau": "CP" + }, + { + "nom": "Ordonner des nombres dans l'ordre croissant ou décroissant.", + "niveau": "CP" + }, + { + "nom": "Ordonner des nombres dans l'ordre croissant ou décroissant.", + "niveau": "CE1" + }, + { + "nom": "Ordonner des nombres dans l'ordre croissant ou décroissant.", + "niveau": "CE2" + }, + { + "nom": "Comprendre et savoir utiliser à bon escient les expressions : égal à, autant que, plus que, plus grand que, moins que, plus petit que...", + "niveau": "CP" + }, + { + "nom": "Comparer, encadrer, intercaler des nombres entiers en utilisant les symboles (=, <, >).", + "niveau": "CE1" + }, + { + "nom": "Comprendre et savoir utiliser les expressions égal à , supérieur à , inférieur à.", + "niveau": "CE1" + }, + { + "nom": "Comparer, encadrer, intercaler des nombres entiers en utilisant les symboles (=, <, >).", + "niveau": "CE2" + }, + { + "nom": "Comprendre et savoir utiliser à bon escient les expressions égal à, supérieur à, inférieur à.", + "niveau": "CE2" + }, + { + "nom": "Utiliser diverses représentations des nombres (écritures en chiffres et en lettres, noms à l'oral, graduations sur une demi-droite, constellations sur des dés, doigts de la main, etc.)." + }, + { + "nom": "Lire un nombre écrit en chiffres.", + "niveau": "CP" + }, + { + "nom": "Lire un nombre écrit en chiffres.", + "niveau": "CE1" + }, + { + "nom": "Lire un nombre écrit en chiffres.", + "niveau": "CE2" + }, + { + "nom": "Ecrire en chiffres et en lettres des nombres dictés.", + "niveau": "CP" + }, + { + "nom": "Ecrire en chiffres et en lettres des nombres dictés.", + "niveau": "CE1" + }, + { + "nom": "Ecrire en chiffres et en lettres des nombres dictés.", + "niveau": "CE2" + }, + { + "nom": "Lire un nombre en lettres.", + "niveau": "CE1" + }, + { + "nom": "Lire un nombre en lettres.", + "niveau": "CE2" + }, + { + "nom": "Passer d'une représentation à une autre, en particulier associer les noms des nombres à leurs écritures chiffrées." + }, + { + "nom": "Connaitre et utiliser diverses représentations d'un nombre et passer de l'une à l'autre.", + "niveau": "CP" + }, + { + "nom": "Connaitre et utiliser les diverses représentations d'un nombre (écriture en chiffres, en lettres, noms à l'oral, décompositions additives c/d/u, produit, somme de termes égaux...) et passer de l'une à l'autre.", + "niveau": "CE1" + }, + { + "nom": "Connaitre et utiliser les diverses représentations d'un nombre (écriture en chiffres, en lettres, noms à l'oral, décompositions additives m/c/d/u, produit, somme de termes égaux...) et passer de l'une à l'autre.", + "niveau": "CE2" + }, + { + "nom": "Interpréter les noms des nombres à l'aide des unités de numération et des écritures arithmétiques." + }, + { + "nom": "Utiliser des écritures en unités de numération (5d 6u, mais aussi 4d 16u ou 6u 5d pour 56) :- unités de numération (unités simples, dizaines, centaines, milliers) et leurs relations (principe décimal de la numération en chiffres) ;- valeur des chiffres en fonction de leur rang dans l'écriture d'un nombre (principe de position) ;- noms des nombres." + }, + { + "nom": "Connaitre la valeur des chiffres en fonction de leur position (unités, dizaines).", + "niveau": "CP" + }, + { + "nom": "Connaitre et utiliser la relation entre dizaine et unité.", + "niveau": "CP" + }, + { + "nom": "Connaitre la valeur des chiffres en fonction de leur position (unités, dizaines, centaines).", + "niveau": "CE1" + }, + { + "nom": "Connaitre et utiliser la relation entre unités et dizaines, entre unités et centaines, entre dizaines et centaines.", + "niveau": "CE1" + }, + { + "nom": "Identifier la parité d'un nombre (pair/impair).", + "niveau": "CE1" + }, + { + "nom": "Connaitre la valeur des chiffres en fonction de leur position (unités, dizaines, centaines, milliers).", + "niveau": "CE2" + }, + { + "nom": "Connaitre et utiliser la relation entre unités et dizaines, entre unités et centaines, entre dizaines et centaines, entre centaines et milliers, entre unité et milliers, entre dizaines et milliers.", + "niveau": "CE2" + }, + { + "nom": "Identifier la parité d'un nombre (pair/impair).", + "niveau": "CE2" + }, + { + "nom": "Itérer une suite de 1 en 1, de 10 en 10, de 100 en 100." + }, + { + "nom": "Dire, à l'oral ou à l'écrit, la suite des nombres à partir d'un nombre donné.", + "niveau": "CE1" + }, + { + "nom": "Dire, à l'oral ou à l'écrit, la suite des nombres à partir de 0 ou d'un nombre donné.", + "niveau": "CE2" + }, + { + "nom": "Associer un nombre entier à une position sur une demi-droite graduée, ainsi qu'à la distance de ce point à l'origine." + }, + { + "nom": "Graduer une demi-droite munie d'un point origine à l'aide d'une unité de longueur." + }, + { + "nom": "Associer un nombre ou un encadrement à une grandeur en mesurant celle-ci à l'aide d'une unité." + }, + { + "nom": "Faire le lien entre unités de numération et unités du système métrique étudiées au cycle 2." + }, + { + "nom": "Résoudre des problèmes issus de situations de la vie quotidienne ou adaptés de jeux portant sur des grandeurs et leur mesure, des déplacements sur une demi-droite graduée, etc., conduisant à utiliser les quatre opérations :- sens des opérations ;- problèmes relevant des structures additives (addition/soustraction) ;- problèmes relevant des structures multiplicatives, de partages ou de groupements (multiplication/division)." + }, + { + "nom": "Résoudre des problèmes du champ additif (addition et soustraction) en une ou deux étapes.", + "niveau": "CP" + }, + { + "nom": "Résoudre, en mobilisant ses connaissances du champ additif sur des petits nombres ou en s'aidant de manipulations, des problèmes du champ multiplicatif en une étape (recherche d'un produit ou recherche de la valeur d'une part ou du nombre de parts dans une sitaution d'un partage équitable). Les écritures mathématiques avec les symboles : et x ne sont pas attendues.", + "niveau": "CP" + }, + { + "nom": "Résoudre des problèmes du champ additif (addition et soustraction) en une ou deux étapes.", + "niveau": "CE1" + }, + { + "nom": "Résoudre des problèmes du champ multiplicatif (itération d'addition).", + "niveau": "CE1" + }, + { + "nom": "Résoudre des problèmes multiplicatifs qui mettent en jeu un produit.", + "niveau": "CE1" + }, + { + "nom": "Résoudre des problèmes à deux étapes mixant additions, soustractions et/ou multiplications.", + "niveau": "CE1" + }, + { + "nom": "Résoudre des problèmes de partage (ceux où l'on cherche combien de fois une grandeur contient une autre grandeur, ceux où l'on partage une grandeur en un nombre donné de grandeurs).", + "niveau": "CE1" + }, + { + "nom": "Résoudre des problèmes du champ additif et/ou multiplicatif en une, deux ou trois étapes.", + "niveau": "CE2" + }, + { + "nom": "Résoudre des problèmes de partage et de groupement (ceux où l'on cherche combien de fois une grandeur contient une autre grandeur, ceux où l'on partage une grandeur en un nombre donné de grandeurs).", + "niveau": "CE2" + }, + { + "nom": "Modéliser ces problèmes à l'aide d'écritures mathématiques :- sens des symboles +, −, ×, :" + }, + { + "nom": "Modéliser ces problèmes à l'aide de schémas ou d'écritures mathématiques.", + "niveau": "CP" + }, + { + "nom": "Modéliser ces problèmes à l'aide de schémas ou d'écritures mathématiques.", + "niveau": "CE1" + }, + { + "nom": "Modéliser ces problèmes à l'aide de schémas ou d'écritures mathématiques.", + "niveau": "CE2" + }, + { + "nom": "Connaitre le sens des signes - et +.", + "niveau": "CP" + }, + { + "nom": "Connaitre le sens des signes - et +.", + "niveau": "CE1" + }, + { + "nom": "Connaitre le sens du signe ×", + "niveau": "CE1" + }, + { + "nom": "Connaitre le sens des signes –, +, x et :.", + "niveau": "CE2" + }, + { + "nom": "Exploiter des données numériques, par exemple des relevés de température." + }, + { + "nom": "Résoudre des problèmes nécessitant l'exploration d'un tableau ou d'un graphique.", + "niveau": "CE2" + }, + { + "nom": "Présenter et organiser des mesures sous forme de tableaux ou de graphiques :- modes de représentation de données numériques : tableaux, graphiques simples, etc." + }, + { + "nom": "Mémoriser des faits numériques et des procédures :- tables de l'addition et de la multiplication ;- décompositions additives et multiplicatives de 10 et de 100, compléments à la dizaine supérieure, à la centaine supérieure, multiplication par 10 et par 100, doubles et moitiés de nombres d'usage courant, etc." + }, + { + "nom": "Connaitre les compléments à 10.", + "niveau": "CP" + }, + { + "nom": "Connaitre la décomposition additive des nombres inférieurs ou égaux à 10.", + "niveau": "CP" + }, + { + "nom": "Connaitre le double des nombres inférieurs à 10.", + "niveau": "CP" + }, + { + "nom": "Connaitre ou savoir retrouver rapidement les doubles des dizaines entières (jusqu'à 50).", + "niveau": "CP" + }, + { + "nom": "Connaitre ou savoir retrouver rapidement la moitié des nombres pairs inférieurs à 20.", + "niveau": "CP" + }, + { + "nom": "Connaitre ou savoir retrouver rapidement la somme de deux nombres inférieurs ou égaux à 10.", + "niveau": "CP" + }, + { + "nom": "Connaitre les compléments à la dizaine supérieure.", + "niveau": "CE1" + }, + { + "nom": "Connaitre les compléments à 100 des dizaines entières.", + "niveau": "CE1" + }, + { + "nom": "Savoir retrouver rapidement les compléments à la centaine supérieure.", + "niveau": "CE1" + }, + { + "nom": "Savoir multiplier par 10 un nombre inférieur à 100.", + "niveau": "CE1" + }, + { + "nom": "Connaitre les doubles de nombres d'usage courant (nombres de 1 à 15, 25, 30, 40, 50 et 100).", + "niveau": "CE1" + }, + { + "nom": "Connaitre les moitiés de nombres pairs d'usage courant (nombres pairs de 1 à 30, 40, 50 et 100).", + "niveau": "CE1" + }, + { + "nom": "Connaitre les tables d'addition.", + "niveau": "CE2" + }, + { + "nom": "Connaitre les tables de multiplication par 2, 3, 4 et 5.", + "niveau": "CE1" + }, + { + "nom": "Connaitre et savoir utiliser la propriété de commutativité de l'addition et de la multiplication.", + "niveau": "CE1" + }, + { + "nom": "Connaitre les doubles de nombres d'usage courant (nombres de 1 à 20, 25, 30, 40, 50, 60 et 100).", + "niveau": "CE2" + }, + { + "nom": "Connaitre les moitiés de nombres pairs d'usage courant (nombres pairs de 1 à 40, 50, 60 et 100).", + "niveau": "CE2" + }, + { + "nom": "Connaitre les tables de multiplication de 2 à 9 . Connaitre et utilise la propriété de la commutativité de l'addition et de la multiplication.", + "niveau": "CE2" + }, + { + "nom": "Mobiliser en situation ses connaissances de faits numériques et ses connaissances sur la numération pour par exemple :- répondre à des questions comme : 7 × 4 = ? ; 28 = 7 × ? ; 28 = 4 × ?, etc. ;- retrouver que 24 × 10, c'est 24 dizaines, c'est 240." + }, + { + "nom": "Traiter à l'oral et à l'écrit des calculs relevant des quatre opérations." + }, + { + "nom": "Elaborer ou choisir des stratégies, expliciter les procédures utilisées et comparer leur efficacité :- addition, soustraction, multiplication, division ;- propriétés implicites des opérations :2 + 9, c'est pareil que 9 + 2 ;3 x 5, c'est pareil que 5 x 3 ;3 × 5 × 2, c'est pareil que 3 × 10.- propriétés de la numération :« 50 + 80, c'est 5 dizaines + 8 dizaines, c'est 13 dizaines, c'est 130 » ;« 4 × 60, c'est 4 × 6 dizaines, c'est 24 dizaines, c'est 240 » ;- propriétés du type : 5 × 12 = 5 × 10 + 5 × 2" + }, + { + "nom": "Calculer sans le support de l'écrit, pour obtenir un résultat exact, pour estimer un ordre de grandeur ou pour vérifier la vraisemblance d'un résultat." + }, + { + "nom": "Calculer mentalement des sommes et des différences.", + "niveau": "CP" + }, + { + "nom": "Commencer à savoir utiliser des procédures et des propriétés : mettre le plus grand nombre en premier, changer l'ordre des termes d'une somme, décomposer additivement un des termes pour calculer plus facilement, associer différemment les termes d'une somme.", + "niveau": "CP" + }, + { + "nom": "Savoir retrouver rapidement les compléments à la dizaine supérieure.", + "niveau": "CE1" + }, + { + "nom": "Savoir trouver rapidement les compléments à la centaine supérieure.", + "niveau": "CE1" + }, + { + "nom": "Calculer mentalement des sommes, des différences et des produits.", + "niveau": "CE1" + }, + { + "nom": "Calculer mentalement des sommes, des différences et des produits.", + "niveau": "CE2" + }, + { + "nom": "Utiliser des procédures et des propriétés : mettre le plus grand nombre en premier, changer l'ordre des termes d'une somme et d'une multiplication, décomposer additivement un des termes pour calculer plus facilement, associer différemment les termes d'une somme et d'une multiplication.", + "niveau": "CE1" + }, + { + "nom": "Savoir multiplier par 10 un nombre inférieur à 100.", + "niveau": "CE1" + }, + { + "nom": "Estimer un ordre de grandeur pour vérifier la vraisemblance d'un résultat.", + "niveau": "CE1" + }, + { + "nom": "Estimer un ordre de grandeur pour vérifier la vraisemblance d'un résultat.", + "niveau": "CE2" + }, + { + "nom": "Savoir trouver rapidement les compléments à 100 et à 1 000.", + "niveau": "CE2" + }, + { + "nom": "Savoir trouver rapidement les compléments à la dizaine supérieure, à la centaine supérieure et au millier supérieur.", + "niveau": "CE2" + }, + { + "nom": "Utiliser des procédures et des propriétés : changer l'ordre des termes d'une somme et d'une multiplication, décomposer additivement un des termes pour calculer plus facilement, associer différemment les termes d'une somme ou d'une multiplication.", + "niveau": "CE2" + }, + { + "nom": "Savoir multiplier un nombre par 10 ou par 100.", + "niveau": "CE2" + }, + { + "nom": "Savoir obtenir le quotient et le reste d'une division euclidienne par un nombre à 1 chiffre et par des nombres comme 10, 25, 50, 100.", + "niveau": "CE2" + }, + { + "nom": "Résoudre mentalement des problèmes arithmétiques, à données numériques simples. En particulier :- calcul sur les nombres 1, 2, 5, 10, 20, 50, 100 en lien avec la monnaie ;- calcul sur les nombres 15, 30, 45, 60, 90 en lien avec les durées." + }, + { + "nom": "Calculer avec le support de l'écrit, en utilisant des écritures en ligne additives, soustractives, multiplicatives, mixtes." + }, + { + "nom": "Calculer à l'écrit des sommes et des différences.", + "niveau": "CP" + }, + { + "nom": "Commencer à savoir utiliser des procédures écrites et des propriétés : mettre le plus grand nombre en premier, changer l'ordre des termes d'une somme, décomposer additivement un des termes pour calculer plus facilement, associer différemment les termes d'une somme.", + "niveau": "CP" + }, + { + "nom": "À l'écrit, savoir retrouver rapidement les compléments à la dizaine supérieure.", + "niveau": "CE1" + }, + { + "nom": "À l'écrit, savoir trouver rapidement les compléments à la centaine supérieure.", + "niveau": "CE1" + }, + { + "nom": "À l'écrit, calculer des sommes, des différences et des produits.", + "niveau": "CE1" + }, + { + "nom": "À l'écrit, calculer des sommes, des différences et des produits.", + "niveau": "CE2" + }, + { + "nom": "À l'écrit, utiliser des procédures et des propriétés : mettre le plus grand nombre en premier, changer l'ordre des termes d'une somme et d'une multiplication, décomposer additivement un des termes pour calculer plus facilement, associer différemment les termes d'une somme et d'une multiplication.", + "niveau": "CE1" + }, + { + "nom": "À l'écrit, savoir multiplier par 10 un nombre.", + "niveau": "CE1" + }, + { + "nom": "À l'écrit, estimer un ordre de grandeur pour vérifier la vraisemblance d'un résultat.", + "niveau": "CE1" + }, + { + "nom": "À l'écrit, estimer un ordre de grandeur pour vérifier la vraisemblance d'un résultat.", + "niveau": "CE2" + }, + { + "nom": "À l'écrit, savoir trouver rapidement les compléments à 100 et à 1 000.", + "niveau": "CE2" + }, + { + "nom": "À l'écrit, savoir trouver rapidement les compléments à la dizaine supérieure, à la centaine supérieure et au millier supérieur.", + "niveau": "CE2" + }, + { + "nom": "À l'écrit, utiliser des procédures et des propriétés : changer l'ordre des termes d'une somme et d'une multiplication, décomposer additivement un des termes pour calculer plus facilement, associer différemment les termes d'une somme ou d'une multiplication.", + "niveau": "CE2" + }, + { + "nom": "À l'écrit, savoir multiplier un nombre par 10 ou par 100.", + "niveau": "CE2" + }, + { + "nom": "À l'écrit, savoir obtenir le quotient et le reste d'une division euclidienne par un nombre à 1 chiffre et par des nombres comme 10, 25, 50, 100.", + "niveau": "CE2" + }, + { + "nom": "Mettre en œuvre un algorithme de calcul posé pour l'addition, la soustraction, la multiplication." + }, + { + "nom": "Poser et calculer des additions en colonnes avec ou sans retenue.", + "niveau": "CP" + }, + { + "nom": "Poser et calculer des additions en colonnes.", + "niveau": "CE1" + }, + { + "nom": "Poser et calculer des additions en colonnes.", + "niveau": "CE2" + }, + { + "nom": "Poser et calculer des soustractions en colonnes.", + "niveau": "CE1" + }, + { + "nom": "Poser et calculer des soustractions en colonnes.", + "niveau": "CE2" + }, + { + "nom": "Poser et calculer des multiplications d'un nombre à deux ou trois chiffres par un nombre à un ou deux chiffres.", + "niveau": "CE2" + } + ] + }, + { + "nom": "Grandeurs et mesures", + "competences": [ + { + "nom": "Comparer, estimer, mesurer des longueurs, des masses, des contenances, des durées.", + "fin_cycle": true + }, + { + "nom": "Utiliser le lexique, les unités, les instruments de mesures spécifiques de ces grandeurs.", + "fin_cycle": true + }, + { + "nom": "Résoudre des problèmes impliquant des longueurs, des masses, des contenances, des durées, des prix.", + "fin_cycle": true + }, + { + "nom": "Comparer des objets selon plusieurs grandeurs et identifier quand il s'agit d'une longueur, d'une masse, d'une contenance ou d'une durée :- lexique spécifique associé aux longueurs, aux masses, aux contenances, aux durées : lourd, léger, grand, petit, haut, bas, court, long." + }, + { + "nom": "Comparer des objets selon leur longueur.", + "niveau": "CP" + }, + { + "nom": "Utiliser le lexique spécifique associé aux longueurs : plus long, plus court, plus près, plus loin, double, moitié.", + "niveau": "CP" + }, + { + "nom": "Comparer des objets selon leur masse, en les soupesant (si les masses sont suffisamment distinctes) ou en utilisant une balance de type Roberval.", + "niveau": "CE1" + }, + { + "nom": "Utiliser le lexique spécifique associé aux masses : plus lourd, moins lourd, plus léger.", + "niveau": "CP" + }, + { + "nom": "Utiliser le lexique spécifique associé aux longueurs :- plus long, plus court, plus près, plus loin, double, moitié- règle graduée- cm, dm, m, km.", + "niveau": "CE1" + }, + { + "nom": "Utiliser le lexique spécifique associé aux masses :- plus lourd, moins lourd, plus léger- balance- g et kg.", + "niveau": "CE2" + }, + { + "nom": "Comparer des objets selon leur contenance, en transvasant.", + "niveau": "CE2" + }, + { + "nom": "Utiliser le lexique spécifique associé aux longueurs :- plus long, plus court, plus près, plus loin, double, moitié- règle graduée- mm, cm, dm, m, km.", + "niveau": "CE2" + }, + { + "nom": "Comparer des longueurs, des masses et des contenances, directement, en introduisant la comparaison à un objet intermédiaire ou par mesurage :- principe de comparaison des longueurs, des masses, des contenances." + }, + { + "nom": "Comparer des segments selon leur longueur.", + "niveau": "CE2" + }, + { + "nom": "Estimer à vue des rapports très simples de longueur." + }, + { + "nom": "Estimer les ordres de grandeurs de quelques longueurs, masses et contenances en relation avec les unités métriques." + }, + { + "nom": "Commencer à s'approprier quelques longueurs de référence :- 1 cm (unité utilisée en classe),- 20 cm (double-décimètre),- 1 m (règle du professeur).", + "niveau": "CP" + }, + { + "nom": "S'approprier quelques longueurs de référence (1 cm, 10 cm, 20 cm, 1 m, 1 dm, 2 dm, 1 km... distance école/maison, école/lieu de vacances...).", + "niveau": "CE1" + }, + { + "nom": "Choisir l'unité de longueur (cm, dm, m ou km) correspondant le mieux pour exprimer une longueur.", + "niveau": "CE1" + }, + { + "nom": "Estimer un ordre de grandeur des objets du quotidien entre le cm, le m et le km.", + "niveau": "CE1" + }, + { + "nom": "Choisit l'unité de masse (g ou kg) correspondant le mieux pour exprimer une masse.", + "niveau": "CE1" + }, + { + "nom": "Estimer un ordre de grandeur des objets du quotidien en utilisant le g ou le kg (un trombone pour le g, un paquet de sucre pour le kg par exemple).", + "niveau": "CE2" + }, + { + "nom": "S'approprier quelques longueurs de référence (1 mm, 5 mm, 1 cm, 10 cm, 20 cm, 1 m, 1 dm, 2 dm, 1 km... distance école/maison, école/vacances, distance entre deux lignes d'un cahier...).", + "niveau": "CE2" + }, + { + "nom": "Choisir l'unité de longueur (mm, cm, dm, m ou km) correspondant le mieux pour exprimer une longueur.", + "niveau": "CE2" + }, + { + "nom": "Estimer un ordre de grandeur des objets du quotidien entre le mm, cm, le m et le km.", + "niveau": "CE2" + }, + { + "nom": "Choisir l'unité de masse (g ou kg ou t) correspondant le mieux pour exprimer une masse.", + "niveau": "CE2" + }, + { + "nom": "Vérifier avec un instrument dans les cas simples :- ordres de grandeur des unités usuelles en les associant à quelques objets familiers ;- rapports très simples de longueurs (double et moitié)." + }, + { + "nom": "Dans des cas simples, mesurer des longueurs, des masses et des contenances en reportant une unité (bande de papier ou ficelle, poids, récipient) :- notion d'unité : grandeur arbitraire prise comme référence pour mesurer les grandeurs de la même espèce." + }, + { + "nom": "Dans des cas simples, mesurer des longueurs, des masses et des contenances en utilisant un instrument adapté (règle graduée, bande de 1 dm de long graduée ou non, mètre gradué ou non, balance à plateaux, balance à lecture directe, verre mesureur) :- unités de mesures usuelles :longueur : m, dm, cm, mm, km et relations entre m, dm, cm et mm ainsi qu'entre km et m ;masse : g, kg, tonne et relations entre kg et g ainsi qu'entre tonne et kg ;contenance : L, dL, cL et leurs relations." + }, + { + "nom": "Savoir que le m et le cm mesurent des longueurs.", + "niveau": "CP" + }, + { + "nom": "Mesurer des segments en utilisant une règle graduée, en cm entiers ou dans une autre unité (définie par les carreaux d'une feuille par exemple).", + "niveau": "CP" + }, + { + "nom": "Mesurer des segments en utilisant une règle graduée, en dm et/ou cm entiers.", + "niveau": "CE1" + }, + { + "nom": "Mesurer des longueurs avec des instruments de mesures (le mètre ruban).", + "niveau": "CE1" + }, + { + "nom": "Savoir que le cm, le dm, le m et le km mesurent des longueurs.", + "niveau": "CE1" + }, + { + "nom": "Connaitre les relations entre cm, dm et m.", + "niveau": "CE1" + }, + { + "nom": "Savoir que le g et le kg mesurent des masses.", + "niveau": "CE1" + }, + { + "nom": "Peser des objets en g ou kg (balance type Roberval, balance digitale...)", + "niveau": "CE1" + }, + { + "nom": "Connaitre les relations entre kg et g.", + "niveau": "CE1" + }, + { + "nom": "Utiliser le litre pour mesurer des contenances.", + "niveau": "CE1" + }, + { + "nom": "Savoir que le L mesure des contenances.", + "niveau": "CE1" + }, + { + "nom": "Savoir que le mm, le cm, le dm, le m et le km mesurent des longueurs.", + "niveau": "CE2" + }, + { + "nom": "Mesurer des segments en utilisant une règle graduée, dm, en cm et/ou en mm entiers.", + "niveau": "CE2" + }, + { + "nom": "Mesurer des longueurs avec des instruments de mesures (le mètre ruban).", + "niveau": "CE2" + }, + { + "nom": "Connaitre les relations entre mm, cm, dm, m et entre m, km.", + "niveau": "CE2" + }, + { + "nom": "Peser des objets en g ou kg (balance type Roberval, balance digitale...).", + "niveau": "CE2" + }, + { + "nom": "Savoir que le g, le kg et la t mesurent des masses.", + "niveau": "CE2" + }, + { + "nom": "Connaitre les relations entre t, kg et g.", + "niveau": "CE2" + }, + { + "nom": "Savoir que le L, le dL et le cL mesurent des contenances.", + "niveau": "CE2" + }, + { + "nom": "Utiliser le litre (L), le décilitre (dL) et le centilitre (cL) pour mesurer des contenances.", + "niveau": "CE2" + }, + { + "nom": "Connaitre les relations entre L, dL et cL.", + "niveau": "CE2" + }, + { + "nom": "Encadrer une grandeur par deux nombres entiers d'unités (par exemple : le couloir mesure entre 6m et 7m de long)." + }, + { + "nom": "Lire l'heure sur une horloge ou une montre à aiguilles." + }, + { + "nom": "Lire des horaires sur une horloge à aiguilles en heures entières.", + "niveau": "CP" + }, + { + "nom": "Positionner les aiguilles d'une horloge, l'horaire étant donné, en heures entières.", + "niveau": "CP" + }, + { + "nom": "Les associer à un moment de la journée.", + "niveau": "CP" + }, + { + "nom": "Lire des horaires sur une horloge à aiguilles en heures entières et en heures et demi-heure.", + "niveau": "CE1" + }, + { + "nom": "Positionner les aiguilles d'une horloge, l'horaire lui étant donné, en heures entières et en heures et demi-heure.", + "niveau": "CE1" + }, + { + "nom": "Lire des horaires sur une horloge à aiguilles en heures entières et en heures, demi-heure et quart d'heure.", + "niveau": "CE2" + }, + { + "nom": "Positionner les aiguilles d'une horloge, l'horaire lui étant donné, en heures entières et en heures, demi-heure et quart d'heure.", + "niveau": "CE2" + }, + { + "nom": "Comparer, estimer, mesurer des durées :- unités de mesure usuelles de durées : j, semaine, h, min, s, mois, année, siècle, millénaire ;- relations entre ces unités." + }, + { + "nom": "Utiliser le lexique associé aux dates et durées :- plus long, plus court, avant, après, plus tôt, plus tard ;- jour, semaine.", + "niveau": "CP" + }, + { + "nom": "Savoir qu'il y a sept jours dans la semaine.", + "niveau": "CP" + }, + { + "nom": "Utiliser le lexique spécifique associé aux dates et durées :- plus long, plus court, avant, après, plus tôt, plus tard- horloge, montre, aiguille- jour, semaine, mois, année, heure, minute.", + "niveau": "CE1" + }, + { + "nom": "Connaitre les unités de mesures de durées et certaines de leurs relations : jour/semaine, jour/mois, mois/année, jour/heure, heure/minute", + "niveau": "CE1" + }, + { + "nom": "Utiliser des repères temporels pour situer des événements dans le temps : d'abord, ensuite, puis , enfin…", + "niveau": "CE2" + }, + { + "nom": "Utiliser le lexique spécifique associé aux dates et durées :- plus long, plus court, avant, après, plus tôt, plus tard- horloge, montre, aiguille- millénaire, siècle, année, jour, semaine, mois, année, heure, minute, seconde.", + "niveau": "CE2" + }, + { + "nom": "Connaitre les unités de mesures de durées et certaines de leurs relations : jour/semaine, jour/mois, mois/année/siècle/millénaire, jour/heure, heure/minute, minute/seconde.", + "niveau": "CE2" + }, + { + "nom": "Dans des cas simples, représenter une grandeur par une longueur, notamment sur une demi-droite graduée :- des objets de grandeurs égales sont représentés par des segments de longueurs égales ;- une grandeur double est représentée par une longueur double ;- la règle graduée en cm comme cas particulier d'une demi-droite graduée." + }, + { + "nom": "Tracer des segments de longueur donnée, en cm entiers en utilisant une règle graduée, ou dans une autre unité (définie par les carreaux d'une feuille par exemple).", + "niveau": "CP" + }, + { + "nom": "Reproduire des segments en les mesurant en cm entiers ou en utilisant une bande de papier.", + "niveau": "CP" + }, + { + "nom": "Reproduire des segments en les mesurant en dm et/ou cm entiers.", + "niveau": "CE1" + }, + { + "nom": "Tracer des segments de longueur donnée, en dm et/ou cm entiers en utilisant une règle graduée.", + "niveau": "CE1" + }, + { + "nom": "Reproduire des segments en les mesurant en dm, en cm et/ou en mm entiers.", + "niveau": "CE2" + }, + { + "nom": "Tracer des segments de longueur donnée, dm, en cm et/ou en mm entiers en utilisant une règle graduée.", + "niveau": "CE2" + }, + { + "nom": "Lire les graduations représentant des grandeurs : cadran d'une balance, frise chronologique, axes d'un graphique gradués en unités." + }, + { + "nom": "Résoudre des problèmes, notamment de mesurage et de comparaison, en utilisant les quatre opérations sur les grandeurs ou leurs mesures :- addition, soustraction, multiplication par un entier ; division : recherche du nombre de parts et de la taille d'une part ;- principes d'utilisation de la monnaie (en euros et centimes d'euros) ;- lexique lié aux pratiques économiques ;- mesurer des segments pour calculer la longueur d'une ligne brisée ou le périmètre d'un polygone." + }, + { + "nom": "Résoudre des problèmes en une ou deux étapes impliquant des longueurs, des durées ou des prix.", + "niveau": "CP" + }, + { + "nom": "Utiliser le lexique spécifique associé aux prix :- plus cher, moins cher ;- rendre la monnaie ;- billet, pièce, somme, reste ;- euros.", + "niveau": "CP" + }, + { + "nom": "Résoudre des problèmes en une ou deux étapes impliquant des longueurs, des masses, des contenances, des durées ou des prix :- problèmes impliquant des manipulations de monnaie- problèmes du champ additif- problèmes multiplicatifs (addition réitérée)- problèmes de durées- problèmes de partage.", + "niveau": "CE1" + }, + { + "nom": "Mobiliser le lexique suivant : le double, la moitié.", + "niveau": "CE2" + }, + { + "nom": "Utiliser le lexique spécifique associé aux prix :- plus cher, moins cher- rendre la monnaie- billet, pièce, somme- euros, centimes d'euro.", + "niveau": "CE1" + }, + { + "nom": "Connaitre la relation entre centime d'euro et euro.", + "niveau": "CE1" + }, + { + "nom": "Résoudre des problèmes impliquant des conversions simples d'une unité usuelle à une autre :- relations entre les unités usuelles ;- lien entre les unités de mesure décimales et les unités de numération." + } + ] + }, + { + "nom": "Espace et géométrie", + "competences": [ + { + "nom": "(Se) repérer et (se) déplacer en utilisant des repères et des représentations.", + "fin_cycle": true + }, + { + "nom": "Reconnaitre, nommer, décrire, reproduire quelques solides.", + "fin_cycle": true + }, + { + "nom": "Reconnaitre, nommer, décrire, reproduire, construire quelques figures géométriques.", + "fin_cycle": true + }, + { + "nom": "Reconnaitre et utiliser les notions d'alignement, d'angle droit, d'égalité de longueurs, de milieu, de symétrie.", + "fin_cycle": true + }, + { + "nom": "Se repérer dans son environnement proche." + }, + { + "nom": "Situer des objets ou des personnes les uns par rapport aux autres ou par rapport à d'autres repères :- vocabulaire permettant de définir des positions (gauche, droite, au-dessus, en dessous, sur, sous, devant, derrière, près, loin, premier plan, second plan, nord, sud, est, ouest,etc.) ;- vocabulaire permettant de définir des déplacements (avancer, reculer, tourner à droite/à gauche, monter, descendre, etc.)." + }, + { + "nom": "Situer les uns par rapport aux autres des objets ou des personnes qui se trouvent dans la classe ou dans l'école en utilisant un vocabulaire spatial précis : à gauche, à droite, sur, sous, entre, devant, derrière, au-dessus, en-dessous.", + "niveau": "CP" + }, + { + "nom": "Utiliser ou produire une suite d'instructions qui codent un déplacement sur un tapis quadrillé, dans la classe ou dans l'école en utilisant un vocabulaire spatial précis : avancer, reculer, tourner à droite, tourner à gauche, monter, descendre.", + "niveau": "CE2" + }, + { + "nom": "Situer, les uns par rapport aux autres, des objets ou des personnes qui se trouvent dans la classe ou dans l'école en utilisant un vocabulaire spatial précis : à gauche, à droite, sur, sous, entre, devant, derrière, au-dessus, en-dessous, près, loin, premier plan, second plan, nord, sud, est, ouest.", + "niveau": "CE1" + }, + { + "nom": "Produire des représentations des espaces familiers (l'école, les espaces proches de l'école, le village, le quartier) et moins familiers (vécus lors de sorties) :- quelques modes de représentation de l'espace (maquettes, plans, photos)." + }, + { + "nom": "Produire des représentations des espaces familiers (école, espaces proches de l'école, quartier, village) et moins familiers (vécus lors de sorties).", + "niveau": "CE1" + }, + { + "nom": "S'orienter et se déplacer en utilisant des repères." + }, + { + "nom": "Réaliser des déplacements dans l'espace et les coder pour qu'un autre élève puisse les reproduire." + }, + { + "nom": "Produire des représentations d'un espace restreint et s'en servir pour communiquer des positions." + }, + { + "nom": "Programmer les déplacements d'un robot ou ceux d'un personnage sur un écran :- repères spatiaux ;- relations entre l'espace dans lequel on se déplace et ses représentations." + }, + { + "nom": "Reconnaître et trier les solides usuels parmi des solides variés." + }, + { + "nom": "Reconnaitre les solides usuels suivants : cube, boule, cône, pyramide, cylindre, pavé droit.", + "niveau": "CP" + }, + { + "nom": "Reconnaitre les solides usuels suivants : cube, boule, cône, pyramide, pavé droit.", + "niveau": "CE1" + }, + { + "nom": "Reconnaître des solides simples dans son environnement proche." + }, + { + "nom": "Repérer des solides simples dans son environnement proche.", + "niveau": "CP" + }, + { + "nom": "Décrire et comparer des solides en utilisant le vocabulaire approprié." + }, + { + "nom": "Nommer le cube, la boule et le pavé droit.", + "niveau": "CP" + }, + { + "nom": "Décrire le cube et le pavé droit en utilisant les termes face et sommet.", + "niveau": "CP" + }, + { + "nom": "Nommer : cube, boule, cône, pyramide, pavé droit.", + "niveau": "CE1" + }, + { + "nom": "Décrire : cube, pyramide, pavé droit en utilisant les termes face, sommet et arête.", + "niveau": "CE2" + }, + { + "nom": "Nommer et décrire les solides usuels suivants : cube, boule, cône, pyramide, cylindre, pavé droit.", + "niveau": "CE2" + }, + { + "nom": "Nommer : cube, boule, cône, pyramide, cylindre, pavé droit.", + "niveau": "CE2" + }, + { + "nom": "Réaliser et reproduire des assemblages de cubes et de pavés droits et associer de tels assemblages à divers types de représentations (photos, vues, etc.)." + }, + { + "nom": "Fabriquer un cube à partir d'un patron fourni :- vocabulaire approprié pour : nommer des solides (cube, pavé droit, boule, cylindre, cône, pyramide) ; décrire des polyèdres (face, sommet, arête).- les faces d'un cube sont des carrés ;- les faces d'un pavé droit sont des rectangles (qui peuvent être des carrés)." + }, + { + "nom": "Savoir que les faces d'un cube sont des carrés et que les faces d'un pavé droit sont des carrés ou des rectangles.", + "niveau": "CP" + }, + { + "nom": "Savoir que les faces d'un cube sont des carrés.", + "niveau": "CE2" + }, + { + "nom": "Savoir que les faces d'un pavé droit sont des carrés ou des rectangles.", + "niveau": "CE1" + }, + { + "nom": "Fabriquer un cube à partir de carrés, de tiges que l'on peut assembler, d'un patron.", + "niveau": "CE1" + }, + { + "nom": "Fabriquer un cube à partir de carrés, de tiges que l'on peut assembler.", + "niveau": "CE2" + }, + { + "nom": "Approcher la notion de patron d'un cube.", + "niveau": "CE2" + }, + { + "nom": "Décrire, reproduire sur papier quadrillé ou uni des figures ou des assemblages de figures planes (éventuellement à partir d'éléments déjà fournis de la figure à reproduire qu'il s'agit alors de compléter)." + }, + { + "nom": "Reproduire un carré, un rectangle et un triangle ou des assemblages de ces figures sur du papier quadrillé ou pointé, sans règle ou avec une règle.", + "niveau": "CP" + }, + { + "nom": "Utiliser la règle, le compas ou l'équerre comme instruments de tracé." + }, + { + "nom": "Utiliser la règle comme instrument de tracé.", + "niveau": "CP" + }, + { + "nom": "Utiliser la règle, l'équerre et le compas comme instruments de tracé.", + "niveau": "CE2" + }, + { + "nom": "Reconnaître, nommer les figures usuelles : carré, rectangle, triangle, triangle rectangle, polygone, cercle, disque." + }, + { + "nom": "Reconnaitre les figures usuelles suivantes : cercle, carré, rectangle et triangle.", + "niveau": "CP" + }, + { + "nom": "Repérer des figures simples dans un assemblage, dans son environnement proche ou sur des photos.", + "niveau": "CE2" + }, + { + "nom": "Nommer le cercle, le carré, le rectangle et le triangle.", + "niveau": "CP" + }, + { + "nom": "Reconnaitre les figures usuelles suivantes : carré, rectangle, triangle et cercle.", + "niveau": "CE1" + }, + { + "nom": "Nommer le cercle, le carré, le rectangle, le triangle, le triangle rectangle et le cercle.", + "niveau": "CE2" + }, + { + "nom": "Décrire à partir des côtés et des angles droits, un carré, un rectangle, un triangle rectangle. Les construire sur un support uni connaissant la longueur des côtés." + }, + { + "nom": "Donner une première description du carré, du rectangle, du triangle en utilisant les termes sommet et côté.", + "niveau": "CP" + }, + { + "nom": "Décrire le carré, le rectangle, le triangle et le triangle rectangle en utilisant un vocabulaire approprié.", + "niveau": "CE1" + }, + { + "nom": "Connaitre les propriétés des angles et des égalités de longueur pour les carrés et les rectangles.", + "niveau": "CE2" + }, + { + "nom": "Reproduire un carré, un rectangle, un triangle, un triangle rectangle et un cercle ou des assemblages de ces figures sur du papier quadrillé ou pointé ou uni, avec une règle graduée, une équerre, et un compas.", + "niveau": "CE1" + }, + { + "nom": "Reproduire un carré, un rectangle, un triangle, un triangle rectangle et un cercle ou des assemblages de ces figures sur tout support (papier quadrillé ou pointé ou uni ou autre), avec une règle graduée, une équerre, et un compas.", + "niveau": "CE2" + }, + { + "nom": "Construire un cercle connaissant son centre et un point, ou son centre et son rayon :- vocabulaire approprié pour décrire les figures planes usuelles : carré, rectangle, triangle, triangle rectangle, polygone, côté, sommet, angle droit ; cercle, disque, rayon, centre ; segment, milieu d'un segment, droite.- propriété des angles et égalités de longueur des côtés pour les carrés et les rectangles ;- lien entre propriétés géométriques et instruments de tracé : droite, alignement et règle non graduée ; angle droit et équerre ; cercle et compas." + }, + { + "nom": "Utiliser le vocabulaire approprié :- polygone, côté, sommet, angle droit- cercle, centre- segment, milieu d'un segment, droite.", + "niveau": "CE2" + }, + { + "nom": "Faire le lien entre propriétés géométriques et instruments de tracés : angle droit/équerre, cercle/compas.", + "niveau": "CE1" + }, + { + "nom": "Utiliser la règle (non graduée) pour repérer et produire des alignements." + }, + { + "nom": "Repérer visuellement des alignements.", + "niveau": "CP" + }, + { + "nom": "Utiliser la règle pour repérer ou vérifier des alignements.", + "niveau": "CP" + }, + { + "nom": "Repérer et produire des angles droits à l'aide d'un gabarit, d'une équerre." + }, + { + "nom": "Repérer et reproduire des angles droits.", + "niveau": "CE2" + }, + { + "nom": "Reporter une longueur sur une droite déjà tracée, en utilisant une bande de papier avec un bord droit ou la règle graduée ou le compas (en fin de cycle)." + }, + { + "nom": "Reporter une longueur sur une droite déjà tracée en utilisant la règle graduée.", + "niveau": "CE1" + }, + { + "nom": "Reporter une longueur sur une droite déjà tracée en utilisant la règle graduée ou le compas.", + "niveau": "CE2" + }, + { + "nom": "Repérer ou trouver le milieu d'un segment, en utilisant une bande de papier avec un bord droit ou la règle graduée :- alignement de points et de segments ;- angle droit ;- égalité de longueurs ;- milieu d'un segment." + }, + { + "nom": "Trouver le milieu d'un segment en utilisant la règle graduée.", + "niveau": "CE1" + }, + { + "nom": "Reconnaître si une figure présente un axe de symétrie (à trouver), visuellement et/ou en utilisant du papier calque, des découpages, des pliages." + }, + { + "nom": "Reconnaitre si une figure présente un axe de symétrie en utilisant du papier calque, des découpages et des pliages.", + "niveau": "CE2" + }, + { + "nom": "Reconnaître dans son environnement des situations modélisables par la symétrie (papillons, bâtiments, etc.)." + }, + { + "nom": "Reconnaitre dans son environnement des situations modélisables par la symétrie (papillons, bâtiments).", + "niveau": "CE1" + }, + { + "nom": "Compléter une figure pour qu'elle soit symétrique par rapport à un axe donné :- symétrie axiale ;- une figure décalquée puis retournée qui coïncide avec la figure initiale est symétrique : elle a un axe de symétrie (à trouver) ;- une figure symétrique pliée sur son axe de symétrie, se partage en deux parties qui coïncident exactement." + }, + { + "nom": "Compléter, sur une feuille quadrillée ou pointée, une figure simple pour qu'elle soit symétrique par rapport à un axe donné.", + "niveau": "CE1" + }, + { + "nom": "Compléter, sur une feuille quadrillée ou pointée, une figure pour qu'elle soit symétrique par rapport à un axe donné.", + "niveau": "CE2" + } + ] + } + ] + }, + { + "nom": "Enseignement moral et civique 2020", + "categories": [] + }, + { + "nom": "Enseignement moral et civique 2024", + "categories": [ + { + "nom": "Connaissance et maîtrise de soi", + "competences": [ + { + "nom": "Comprendre ses émotions et ses sentiments : leur origine et leurs manifestations.", + "niveau": "CP" + }, + { + "nom": "Trouver les réponses appropriées aux besoins exprimés.", + "niveau": "CP" + }, + { + "nom": "Consolider sa confiance en soi.", + "niveau": "CP" + }, + { + "nom": "Acquérir une estime de soi.", + "niveau": "CP" + } + ] + }, + { + "nom": "Les règles collectives et l'autonomie", + "competences": [ + { + "nom": "S'approprier les règles de l'école (droits et devoirs), pour soi-même (son propre bien-être et sa propre sécurité).", + "niveau": "CP" + }, + { + "nom": "Respecter les différents adultes de l'école en identifiant leur rôle.", + "niveau": "CP" + }, + { + "nom": "Développer son autonomie.", + "niveau": "CP" + }, + { + "nom": "Prendre des initiatives personnelles et faire des choix sans craindre de se tromper.", + "niveau": "CP" + }, + { + "nom": "Identifier les risques et les dangers de son environnement immédiat et adopter un comportement adapté.", + "niveau": "CP" + }, + { + "nom": "Respecter les équipements de la collectivité, condition du partage de biens communs.", + "niveau": "CP" + }, + { + "nom": "Savoir que les enfants ont des droits (Convention internationale des droits de l'enfant, 1989).", + "niveau": "CP" + } + ] + }, + { + "nom": "Règles d'hygiène et exigence d'intimité", + "competences": [ + { + "nom": "Avoir conscience de son intégrité.", + "niveau": "CP" + }, + { + "nom": "Connaître et appliquer les règles élémentaires d'hygiène personnelle.", + "niveau": "CP" + }, + { + "nom": "Connaître et respecter les règles élémentaires de l'intimité personnelle.", + "niveau": "CP" + } + ] + }, + { + "nom": "Être élève à l'école de la République", + "competences": [ + { + "nom": "Identifier le drapeau français.", + "niveau": "CP" + }, + { + "nom": "Reconnaitre La Marseillaise.", + "niveau": "CP" + } + ] + }, + { + "nom": "Altérité et sociabilité", + "competences": [ + { + "nom": "Reconnaitre et prendre en compte les émotions et les sentiments d'autrui.", + "niveau": "CE1" + }, + { + "nom": "Développer sa capacité d'empathie.", + "niveau": "CE1" + }, + { + "nom": "S'entraider et partager avec les autres.", + "niveau": "CE1" + }, + { + "nom": "Reconnaitre la diversité comme richesse et ne pas faire des différences (sociales, physiques, culturelles, de genre) un motif de violence.", + "niveau": "CE1" + }, + { + "nom": "Faire comprendre que la solidarité et l'entraide, en lien avec la notion de fraternité, permettent un renforcement de la notion d'égalité entre les personnes.", + "niveau": "CE1" + } + ] + }, + { + "nom": "Règles collectives et prise d'initiative", + "competences": [ + { + "nom": "Connaître et appliquer les règles élémentaires de vie, de communication et d'échange en collectivité : l'idée de civilité.", + "niveau": "CE1" + }, + { + "nom": "Identifier les dangers au sein des situations dans lesquelles on se trouve.", + "niveau": "CE1" + }, + { + "nom": "Prendre des initiatives (faire des choix, les justifier).", + "niveau": "CE1" + } + ] + }, + { + "nom": "Principes et symboles de la République", + "competences": [ + { + "nom": "Identifier les symboles républicains.", + "niveau": "CE1" + }, + { + "nom": "Apprendre à chanter le 1er couplet et le refrain de La Marseillaise.", + "niveau": "CE1" + }, + { + "nom": "Comprendre la devise « Liberté, Égalité, Fraternité ».", + "niveau": "CE1" + }, + { + "nom": "Aborder le principe de la liberté de conscience.", + "niveau": "CE1" + }, + { + "nom": "Savoir que le français est la langue de la République.", + "niveau": "CE1" + } + ] + }, + { + "nom": "L'engagement pour le bien commun", + "competences": [ + { + "nom": "Sensibiliser à la notion de bien commun et amener les élèves à prendre conscience que les actions individuelles doivent tenir compte de l'intérêt collectif.", + "niveau": "CE2" + }, + { + "nom": "Savoir qu'il existe des institutions et des associations au service du bien commun.", + "niveau": "CE2" + }, + { + "nom": "Aborder des enjeux d'intérêt collectif : l'éducation pour tous, l'environnement, la sécurité, l'information.", + "niveau": "CE2" + } + ] + }, + { + "nom": "La République et son fonctionnement", + "competences": [ + { + "nom": "Savoir qu'en France le chef de l'État est le président de la République et qu'il est élu.", + "niveau": "CE2" + }, + { + "nom": "Savoir que le maire est un élu local, et le représentant de l'État dans la commune ; connaître son rôle à la tête de la collectivité (état civil, école, environnement).", + "niveau": "CE2" + }, + { + "nom": "Approfondir la compréhension de la devise « Liberté, Égalité, Fraternité ».", + "niveau": "CE2" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/Back-End/competences/Cycle3.json b/Back-End/competences/Cycle3.json new file mode 100644 index 0000000..d1fde32 --- /dev/null +++ b/Back-End/competences/Cycle3.json @@ -0,0 +1,2537 @@ +{ + "domaines": [ + { + "nom": "Français", + "categories": [ + { + "nom": "Langage oral", + "competences": [ + { + "nom": "Écouter un récit et manifester sa compréhension en répondant à des questions sans se reporter au texte.", + "fin_cycle": true + }, + { + "nom": "Dire de mémoire un texte à haute voix.", + "fin_cycle": true + }, + { + "nom": "Réaliser une courte présentation orale en prenant appui sur des notes ou sur diaporama ou autre outil (numérique par exemple).", + "fin_cycle": true + }, + { + "nom": "Participer de façon constructive aux échanges avec d'autres élèves dans un groupe pour confronter des réactions ou des points de vue.", + "fin_cycle": true + }, + { + "nom": "Porter attention aux éléments vocaux et gestuels lors de l'audition d'un texte ou d'un message (segmentation, accentuation, intonation, discrimination entre des sonorités proches, ...) et repérer leurs effets." + }, + { + "nom": "Remarquer les éléments vocaux et gestuels d'un discours.", + "niveau": "CM1" + }, + { + "nom": "Identifier les effets des éléments vocaux et gestuels dans un discours.", + "niveau": "CM2" + }, + { + "nom": "Mobiliser son attention en fonction d'un but." + }, + { + "nom": "Soutenir son attention, sur une durée de 10 minutes, en vue d'une restitution orale.", + "niveau": "CM1" + }, + { + "nom": "Soutenir une attention longue (15 minutes environ) en vue d'une restitution orale de l'essentiel d'un message ou d'un texte entendu.", + "niveau": "CM2" + }, + { + "nom": "Identifier et mémoriser des informations importantes, leurs enchaînements, mettre en relation ces informations, avec les informations implicites." + }, + { + "nom": "Ecouter des propos oraux et des textes lus de natures et de genres variés pour prélever et mémoriser des informations.", + "niveau": "CM1" + }, + { + "nom": "En fonction des différents genres de discours entendus (récit, compte rendu, exposé...), adapter son écoute de façon à prélever les informations importantes, repérer leurs enchaînements et les mettre en relation avec les informations implicites.", + "niveau": "CM2" + }, + { + "nom": "Repérer et prendre en compte les caractéristiques des différents genres de discours (récit, compte rendu, reformulation, exposé, argumentation, … ), le lexique et les références culturelles liés au domaine du message ou du texte entendu." + }, + { + "nom": "Repérer d'éventuelles difficultés de compréhension, savoir les verbaliser et trouver des moyens d'y répondre." + }, + { + "nom": "Après avoir écouté un discours, situer précisément ce que l'on n'a pas compris.", + "niveau": "CM1" + }, + { + "nom": "Dans le cadre d'une seconde écoute guidée par le professeur, lever les difficultés de compréhension rencontrées.", + "niveau": "CM2" + }, + { + "nom": "Exercer une vigilance critique par rapport au langage écouté." + }, + { + "nom": "Mobiliser les ressources de la voix et du corps pour être entendu et compris." + }, + { + "nom": "Prendre la parole de manière à se faire entendre de son auditoire.", + "niveau": "CM1" + }, + { + "nom": "Utiliser des techniques liées à la voix et au corps pour être compris et susciter l'attention de son auditoire.", + "niveau": "CM2" + }, + { + "nom": "Organiser et structurer le propos selon le genre de discours ; mobilisation des formes, des tournures et du lexique appropriés (conte ou récit, compte rendu, présentation d'un ouvrage, présentation des résultats d'une recherche documentaire ; description, explication, justification, présentation d'un point de vue argumenté, etc.)." + }, + { + "nom": "Prendre la parole en s'aidant du texte qu'on a préalablement rédigé.", + "niveau": "CM1" + }, + { + "nom": "Prendre la parole en s'appuyant sur ses notes.", + "niveau": "CM2" + }, + { + "nom": "Utiliser les techniques de mise en voix des textes littéraires (poésie, théâtre en particulier)." + }, + { + "nom": "Mettre en voix, avec l'aide de son professeur, de courts textes, en tenant compte de leurs caractéristiques.", + "niveau": "CM1" + }, + { + "nom": "Mettre en voix, seul ou avec des camarades, des textes narratifs plus complexes.", + "niveau": "CM2" + }, + { + "nom": "Utiliser les techniques de mémorisation des textes présentés ou interprétés." + }, + { + "nom": "Restituer des textes ou un travail auquel on a participé.", + "niveau": "CM1" + }, + { + "nom": "Prendre en compte la parole des différents interlocuteurs dans un débat et identifier les points de vue exprimés." + }, + { + "nom": "Dans un échange, prendre la parole en respectant son tour, sans couper la parole, pour apporter des compléments en lien avec le sujet abordé.", + "niveau": "CM1" + }, + { + "nom": "Présenter une idée, un point de vue en tenant compte des autres points de vue exprimés (approbation, réfutation, apport de compléments, reformulation, ...)." + }, + { + "nom": "Dans le cadre d'échanges, réagir aux propos de ses camarades pour les approuver ou donner un point de vue différent en relation avec le sujet abordé.", + "niveau": "CM2" + }, + { + "nom": "Respecter les règles de la conversation (quantité, qualité, clarté et concision, relation avec le propos)." + }, + { + "nom": "Mobiliser des expressions et des formules qui engagent celui qui parle (savoir exprimer un refus, exprimer une demande, présenter ses excuses, remercier)." + }, + { + "nom": "Mobiliser des stratégies argumentatives : recours à des exemples, réfutation, récapitulation, ..." + }, + { + "nom": "Développer le lexique en lien avec le domaine visé." + }, + { + "nom": "Réinvestir le lexique appris en classe ou utilisé par ses camarades.", + "niveau": "CM1" + }, + { + "nom": "Savoir construire son discours (organisation du propos, enchaînement des phrases)." + }, + { + "nom": "Savoir mobiliser des moyens d'expression (lexique, formules, types de phrase, ...)." + }, + { + "nom": "Savoir mettre à distance son expérience et mobiliser des connaissances (formulation et reformulation, explicitation des démarches, des contenus, des procédures, etc.)." + }, + { + "nom": "Appuyer sa prise de parole sur le matériau linguistique travaillé en classe, notamment les expressions et formulations relatives à l'affirmation d'un point de vue.", + "niveau": "CM2" + }, + { + "nom": "Élaborer les règles organisant les échanges ; repérer le respect ou non de ces règles dans les propos d'un pair, aider à la reformulation." + }, + { + "nom": "Participer aux échanges dans le respect des règles élaborées collectivement.", + "niveau": "CM1" + }, + { + "nom": "Participer à des échanges et intervenir pour faire respecter les règles élaborées collectivement.", + "niveau": "CM2" + }, + { + "nom": "Prendre en compte les critères d'évaluation explicites élaborés collectivement pour les présentations orales." + }, + { + "nom": "Être capable d'autocorrection après écoute (reformulations)." + }, + { + "nom": "À l'écoute de sa prise de parole enregistrée, repérer les moments qui sont à améliorer.", + "niveau": "CM1" + }, + { + "nom": "Après écoute, améliorer sa prise de parole en tenant compte des conseils donnés par le groupe.", + "niveau": "CM2" + }, + { + "nom": "Comparer le fonctionnement de la syntaxe de la langue orale (prosodie, juxtaposition, répétitions et ajustements, importance des verbes) avec celle de la langue écrite." + }, + { + "nom": "Dégager des différences syntaxiques entre un message oral et sa transposition à l'écrit.", + "niveau": "CM2" + } + ] + }, + { + "nom": "Lecture et compréhension de l'écrit", + "competences": [ + { + "nom": "Lire, comprendre et interpréter un texte littéraire adapté à son âge et réagir à sa lecture.", + "fin_cycle": true + }, + { + "nom": "Lire et comprendre des textes et des documents (textes, tableaux, graphiques, schémas, diagrammes, images) pour apprendre dans les différentes disciplines.", + "fin_cycle": true + }, + { + "nom": "Lire et comprendre des œuvres de plus en plus longues et de plus en plus complexes : - CM1 : 5 ouvrages de littérature de jeunesse et 2 œuvres du patrimoine ; - CM2 : 4 ouvrages de littérature de jeunesse et 3 œuvres du patrimoine ; - 6e : 3 ouvrages de littérature de jeunesse et 3 œuvres du patrimoine.", + "fin_cycle": true + }, + { + "nom": "Mémoriser la lecture de mots fréquents et irréguliers." + }, + { + "nom": "Mémoriser de plus en plus de mots fréquents et irréguliers.", + "niveau": "CM1" + }, + { + "nom": "Automatiser le décodage." + }, + { + "nom": "Lire à voix haute un texte court, après préparation, sans confondre les graphèmes, mêmes complexes.", + "niveau": "CM1" + }, + { + "nom": "Lire sans effort un texte d'une page silencieusement ou à haute voix.", + "niveau": "CM1" + }, + { + "nom": "Lire correctement en moyenne 110 mots par minute.", + "niveau": "CM1" + }, + { + "nom": "Lire à voix haute, après préparation, un texte long.", + "niveau": "CM2" + }, + { + "nom": "Lire correctement en moyenne 120 mots par minute.", + "niveau": "CM2" + }, + { + "nom": "Prendre en compte les groupes syntaxiques (groupes de mots avec unité de sens), les marques de ponctuation, dans la lecture." + }, + { + "nom": "Dans sa lecture à haute voix, prendre en compte les marques de ponctuation.", + "niveau": "CM1" + }, + { + "nom": "Par sa lecture à voix haute, rendre compte de la ponctuation et respecter le rythme des groupes syntaxiques.", + "niveau": "CM2" + }, + { + "nom": "Être capable de s'engager dans une démarche progressive pour accéder au sens." + }, + { + "nom": "Dans un texte, repérer les informations explicites et pointer les informations qui ne sont pas données.", + "niveau": "CM1" + }, + { + "nom": "Restituer l'essentiel d'un texte qui contient des informations explicites et des informations implicites.", + "niveau": "CM2" + }, + { + "nom": "Être capable de mettre en relation le texte lu avec les lectures antérieures, l'expérience vécue et les connaissances culturelles." + }, + { + "nom": "Mettre en relation le texte lu avec un autre texte étudié en classe.", + "niveau": "CM1" + }, + { + "nom": "Mettre en relation le texte lu avec un autre texte ou une autre référence culturelle.", + "niveau": "CM2" + }, + { + "nom": "Être capable de mobiliser des connaissances grammaticales et lexicales." + }, + { + "nom": "Être initié à la notion d'aspect verbal (valeurs des temps), abordée à travers l'emploi des verbes dans les textes lus (le récit au passé simple, le discours au présent ou au passé composé, etc.)." + }, + { + "nom": "Être capable de repérer ses difficultés et de chercher comment les résoudre." + }, + { + "nom": "Être capable de recourir, de manière autonome, aux différentes démarches de lecture apprises en classe." + }, + { + "nom": "Lire des livres qu'on a choisis.", + "niveau": "CM2" + }, + { + "nom": "Être capable d'identifier les principaux genres littéraires (conte, roman, poésie, fable, nouvelle, théâtre) et de repérer leurs caractéristiques majeures." + }, + { + "nom": "Distinguer, par la mise en page, un extrait de théâtre, un poème et un texte narratif.", + "niveau": "CM1" + }, + { + "nom": "Reconnaître et nommer les principaux genres littéraires à l'aide de critères explicites donnés par le professeur.", + "niveau": "CM2" + }, + { + "nom": "Être capable de s'engager dans une démarche progressive pour accéder au sens." + }, + { + "nom": "Trouver dans des documents simples les réponses à des questions.", + "niveau": "CM1" + }, + { + "nom": "Être capable de mettre en relation différentes informations." + }, + { + "nom": "Découvrir des documents composites et y repérer des informations grâce à un questionnement.", + "niveau": "CM1" + }, + { + "nom": "À partir de questions posées, prélever des informations (en faisant des inférences si nécessaire) et les combiner pour donner un sens global au document composite.", + "niveau": "CM2" + }, + { + "nom": "Être capable d'identifier les différents genres représentés et de repérer leurs caractéristiques majeures." + }, + { + "nom": "Donner la nature et la source d'un document.", + "niveau": "CM1" + }, + { + "nom": "Identifier les différents genres représentés et repérer leurs caractéristiques majeures.", + "niveau": "CM1" + }, + { + "nom": "Reconnaître et nommer les caractéristiques des différents éléments d'un document composite.", + "niveau": "CM2" + } + ] + }, + { + "nom": "Écriture", + "competences": [ + { + "nom": "Écrire un texte d'une à deux pages adapté à son destinataire.", + "fin_cycle": true + }, + { + "nom": "Après révision, obtenir un texte organisé et cohérent, à la graphie lisible et respectant les régularités orthographiques étudiées au cours du cycle.", + "fin_cycle": true + }, + { + "nom": "Automatiser les gestes de l'écriture cursive par un entraînement régulier." + }, + { + "nom": "Copier sans erreur un texte d'une dizaine de lignes selon la mise en forme demandée en recherchant la rapidité et l'efficacité.", + "niveau": "CM1" + }, + { + "nom": "Développer la rapidité et l'efficacité de la copie en respectant la mise en page d'écrits variés." + }, + { + "nom": "Ecrire un texte de 5 à 10 lignes en respectant les normes de l'écriture et en reproduisant la forme induite par le modèle.", + "niveau": "CM1" + }, + { + "nom": "Ecrire un texte de façon soignée et lisible d'une quinzaine de lignes en reproduisant la forme induite par le modèle.", + "niveau": "CM2" + }, + { + "nom": "Utiliser méthodiquement le clavier et le traitement de texte." + }, + { + "nom": "Utiliser les fonctionnalités du traitement de texte pour réviser ses écrits.", + "niveau": "CM2" + }, + { + "nom": "Maîtriser les bases de l'écriture au clavier." + }, + { + "nom": "Utiliser le clavier pour copier et mettre en page, avec rapidité et efficacité, un texte court (5 lignes).", + "niveau": "CM1" + }, + { + "nom": "Copier et mettre en page sur l'ordinateur des textes courts de 5 à 10 lignes.", + "niveau": "CM2" + }, + { + "nom": "Formuler des impressions de lecture." + }, + { + "nom": "Dans son cahier de brouillon ou carnet de lecteur, formuler ses impressions de lecture, recopier les passages qui lui plaisent...", + "niveau": "CM1" + }, + { + "nom": "Émettre des hypothèses." + }, + { + "nom": "Lister, articuler, hiérarchiser des idées." + }, + { + "nom": "Utiliser le cahier de brouillon pour lister ses idées avant d'écrire.", + "niveau": "CM1" + }, + { + "nom": "Utiliser un cahier de brouillon pour noter ce que l'on retient à l'écoute d'un exposé, à l'occasion d'une sortie, d'une rencontre.", + "niveau": "CM2" + }, + { + "nom": "Reformuler." + }, + { + "nom": "Reformuler par écrit l'essentiel d'un texte, d'une leçon écrite.", + "niveau": "CM2" + }, + { + "nom": "Élaborer des conclusions provisoires." + }, + { + "nom": "Rédiger des résumés." + }, + { + "nom": "Utiliser ses écrits de travail pour reformuler, produire des conclusions provisoires, des résumés avec l'aide du professeur.", + "niveau": "CM1" + }, + { + "nom": "Résumer par un titre les paragraphes d'un message oral ou écrit.", + "niveau": "CM2" + }, + { + "nom": "Expliquer une démarche." + }, + { + "nom": "Justifier une réponse." + }, + { + "nom": "Introduire ses réponses à des questions de compréhension en utilisant les mots de la question et justifier son choix.", + "niveau": "CM2" + }, + { + "nom": "Argumenter un propos." + }, + { + "nom": "Connaître les caractéristiques principales des différents genres d'écrits à rédiger." + }, + { + "nom": "En s'appuyant sur des modèles, rédiger de courts textes de genres différents (poèmes, récits...).", + "niveau": "CM1" + }, + { + "nom": "En respectant les principales caractéristiques des genres littéraires, préalablement déterminées, écrire régulièrement des textes variés : récits, textes poétiques, saynètes.", + "niveau": "CM2" + }, + { + "nom": "Mettre en œuvre (de manière guidée, puis autonome) une démarche de rédaction de textes : convoquer un univers de référence, un matériau linguistique (lexique et syntaxe déjà connus ou préparés pour l'écrit demandé), trouver et organiser des idées, élaborer des phrases, les enchaîner avec cohérence, élaborer des paragraphes ou d'autres formes d'organisation textuelles." + }, + { + "nom": "Dans différentes situations de travail, noter des informations oralisées durant une leçon. Rédiger une phrase de synthèse à partir de ces écrits intermédiaires.", + "niveau": "CM1" + }, + { + "nom": "Rédiger un texte sous forme de paragraphes en organisant ses idées.", + "niveau": "CM1" + }, + { + "nom": "Suivre un protocole donné par le professeur pour écrire un texte, en utilisant les outils mis à sa disposition par le professeur.", + "niveau": "CM1" + }, + { + "nom": "Organiser l'écriture de son texte en planifiant et respectant des étapes nécessaires : premier jet, relecture, révision...", + "niveau": "CM2" + }, + { + "nom": "Mobiliser des outils liés à l'étude de la langue à disposition dans la classe (matériau linguistique, outils orthographiques, guides de relecture, dictionnaires en ligne, traitements de texte, correcteurs orthographiques)." + }, + { + "nom": "Réécrire un texte en tenant compte des suggestions de révision élaborées en classe (marques grammaticales, substituts, connecteurs temporels).", + "niveau": "CM1" + }, + { + "nom": "Pour écrire un texte, mobiliser ce qui a précédemment appris sur la langue (syntaxe, lexique, conjugaison...).", + "niveau": "CM2" + }, + { + "nom": "Mobiliser ses connaissances sur la langue (mémoire orthographique des mots, règles d'accord, ponctuation, organisateurs du discours...)." + }, + { + "nom": "Être initié à la notion d'aspect verbal (valeurs des temps), abordée à travers l'emploi des verbes en rédaction (le récit au passé simple, le discours au présent ou au passé composé, etc.)." + }, + { + "nom": "Concevoir l'écriture comme un processus inscrit dans la durée." + }, + { + "nom": "Faire évoluer son texte au fur et à mesure des différentes relectures guidées. Son écriture relève d'un processus.", + "niveau": "CM2" + }, + { + "nom": "Mettre à distance son texte pour l'évaluer." + }, + { + "nom": "Réviser son texte à l'aide de grilles de critères et y apporter des améliorations ou des corrections.", + "niveau": "CM2" + }, + { + "nom": "Enrichir par la recherche des formulations plus adéquates." + }, + { + "nom": "Reprendre la première version de son texte, après lecture de son professeur, pour l'améliorer.", + "niveau": "CM1" + }, + { + "nom": "Utiliser les connecteurs logiques, temporels, les reprises anaphoriques, les temps verbaux pour éviter des dysfonctionnements." + }, + { + "nom": "Prendre en compte la notion de paragraphe et les formes d'organisation du texte propres aux différents genres et types d'écrits." + }, + { + "nom": "Mobiliser des connaissances portant sur la ponctuation (utilité, usage, participation au sens du texte) et sur la syntaxe (la phrase comme unité de sens)." + }, + { + "nom": "Connaître les signes de ponctuation et les utiliser à bon escient, au service de la cohérence du texte écrit.", + "niveau": "CM1" + }, + { + "nom": "Travailler l'organisation du texte sur l'ensemble de l'écrit, y compris la présentation de la copie.", + "niveau": "CM1" + }, + { + "nom": "S'appuyer sur ses connaissances de la ponctuation, de la syntaxe pour écrire.", + "niveau": "CM2" + }, + { + "nom": "Structurer ses textes en paragraphes.", + "niveau": "CM2" + }, + { + "nom": "En lien avec l'étude de la langue, mobiliser des connaissances portant sur l'orthographe grammaticale : accord du verbe avec le sujet ; morphologie verbale en fonction des temps ; accord du déterminant et de l'adjectif avec le nom ; accord de l'attribut et du sujet." + }, + { + "nom": "Réinvestir les notions abordées en étude de la langue (complémentarité des notions abordées et de certains énoncés proposés en production d'écrits).", + "niveau": "CM2" + }, + { + "nom": "Mobiliser des connaissances portant sur l'orthographe lexicale et être capable de vérifier l'orthographe des mots dont on doute." + }, + { + "nom": "Apprendre à identifier les zones d'erreurs possibles dans un premier temps avec le guidage du professeur, puis de manière plus autonome." + }, + { + "nom": "Identifier les dysfonctionnements de son texte, guidé par le professeur qui pointe des critères de réussite selon les notions abordées en étude de la langue.", + "niveau": "CM1" + } + ] + }, + { + "nom": "Étude de la langue (grammaire, orthographe, lexique)", + "competences": [ + { + "nom": "En rédaction de textes dans des contextes variés, maîtriser les accords dans le groupe nominal (déterminant, nom, adjectif), entre le verbe et son sujet dans des cas simples (sujet placé avant le verbe et proche de lui, sujet composé d'un groupe nominal comportant au plus un adjectif ou un complément du nom ou sujet composé de deux noms, sujet inversé suivant le verbe) ainsi que l'accord de l'attribut avec le sujet.", + "fin_cycle": true + }, + { + "nom": "Raisonner pour analyser le sens des mots en contexte et en prenant appui sur la morphologie.", + "fin_cycle": true + }, + { + "nom": "Être capable de repérer les principaux constituants d'une phrase simple et complexe.", + "fin_cycle": true + }, + { + "nom": "Maîtriser l'ensemble des phonèmes du français et des graphèmes associés." + }, + { + "nom": "Maîtriser l'ensemble des phonèmes du français et des graphèmes associés.", + "niveau": "CM1" + }, + { + "nom": "Avoir conscience de quelques homophonies lexicales et grammaticales, et orthographie correctement les mots concernés.", + "niveau": "CM2" + }, + { + "nom": "Maîtriser la variation et les marques morphologiques du genre et du nombre, à l'oral et à l'écrit (noms, déterminants, adjectifs, pronoms, verbes)." + }, + { + "nom": "À l'écrit et à l'oral, repérer les classes de mots qui subissent des variations et les marques morphologiques du genre et du nombre.", + "niveau": "CM1" + }, + { + "nom": "Maîtriser la variation et les marques morphologiques du genre et du nombre, à l'oral et à l'écrit (noms, déterminants, adjectifs, pronoms, verbes).", + "niveau": "CM2" + }, + { + "nom": "Comprendre et maîtriser les notions de nature (ou classe grammaticale) et fonction." + }, + { + "nom": "Connaître les notions de nature et fonction et ne pas les confondre.", + "niveau": "CM1" + }, + { + "nom": "Identifier les constituants d'une phrase simple et les hiérarchiser :- approfondir la connaissance du sujet (sujet composé de plusieurs noms ou groupes nominaux, sujet inversé) ;- différencier les compléments : COD, COI, compléments circonstanciels de temps, lieu et cause ;- identifier l'attribut du sujet." + }, + { + "nom": "Dans une phrase simple, identifier le sujet, y compris lorsqu'il est composé de plusieurs noms.", + "niveau": "CM1" + }, + { + "nom": "Identifier les constituants d'une phrase simple : le sujet, le verbe, les compléments d'objet, sans les distinguer, et les compléments circonstanciels, sans les distinguer.", + "niveau": "CM1" + }, + { + "nom": "Dans des situations simples, distinguer les COD et COI.", + "niveau": "CM2" + }, + { + "nom": "Repérer la préposition qui introduit le COI ; distinguer un COI d'un CC introduit également par une préposition.", + "niveau": "CM2" + }, + { + "nom": "Identifier les CC de temps, lieu et cause.", + "niveau": "CM2" + }, + { + "nom": "Identifier le sujet, même quand il est inversé.", + "niveau": "CM2" + }, + { + "nom": "Identifier l'attribut du sujet.", + "niveau": "CM2" + }, + { + "nom": "Analyser le groupe nominal : notions d'épithète et de complément du nom." + }, + { + "nom": "Dans un groupe nominal, distinguer le nom noyau et repérer le complément du nom.", + "niveau": "CM1" + }, + { + "nom": "Au sein du groupe nominal, identifier le complément du nom et l'épithète.", + "niveau": "CM2" + }, + { + "nom": "Différencier les classes de mots :- le déterminant : déterminants possessif et démonstratif ;- le pronom personnel objet ;- l'adverbe ;- la préposition (construire la notion de groupe nominal prépositionnel) ;- les conjonctions de coordination et les conjonctions de subordination les plus usuelles (quand, comme, si, que, lorsque, parce que, puisque etc.)." + }, + { + "nom": "En plus des classes grammaticales déjà connues, identifier les conjonctions de coordination, les adverbes, les déterminants possessifs et démonstratifs.", + "niveau": "CM1" + }, + { + "nom": "Parmi les mots invariables, identifier les prépositions.", + "niveau": "CM2" + }, + { + "nom": "Approfondir la connaissance des trois types de phrases (déclaratives, interrogatives et impératives) et des formes négative et exclamative." + }, + { + "nom": "Identifier et connaître les emplois des trois types de phrases (déclaratives, interrogatives et impératives) et des formes négative et exclamative.", + "niveau": "CM1" + }, + { + "nom": "Différencier phrase simple et phrase complexe à partir de la notion de proposition." + }, + { + "nom": "Distinguer phrase simple et phrase complexe à partir du repérage des verbes conjugués.", + "niveau": "CM2" + }, + { + "nom": "Repérer les différents modes d'articulation des propositions au sein de la phrase complexe : notions de juxtaposition, coordination, subordination." + }, + { + "nom": "Comprendre les différences entre l'usage de la conjonction de coordination et l'usage de la conjonction de subordination." + }, + { + "nom": "Identifier les classes de mots subissant des variations : le nom et le verbe ; le déterminant ; l'adjectif ; le pronom." + }, + { + "nom": "Identifier les classes de mots subissant des variations : le nom et le verbe ; le déterminant.", + "niveau": "CM1" + }, + { + "nom": "Distinguer les classes de mots, selon qu'ils subissent ou non des variations. Repérer les variations qui affectent l'adjectif et le pronom.", + "niveau": "CM2" + }, + { + "nom": "Connaître la notion de groupe nominal et d'accord au sein du groupe nominal." + }, + { + "nom": "Dans un groupe nominal, repérer le noyau et fait les accords au sein de celui-ci dans des situations simples : déterminant + nom + adjectif(s).", + "niveau": "CM1" + }, + { + "nom": "Maîtriser l'accord du verbe avec son sujet y compris inversé, de l'attribut avec le sujet, du participe passé avec être (cas les plus usuels)." + }, + { + "nom": "Maîtriser l'accord du verbe avec son sujet.", + "niveau": "CM1" + }, + { + "nom": "Maîtriser l'accord du verbe avec le sujet, même quand celui-ci est inversé.", + "niveau": "CM2" + }, + { + "nom": "Élaborer des règles de fonctionnement construites sur les régularités." + }, + { + "nom": "Reconnaître le verbe (utilisation de plusieurs procédures)." + }, + { + "nom": "Reconnaître le verbe conjugué dans une phrase.", + "niveau": "CM1" + }, + { + "nom": "Connaître les trois groupes de verbes." + }, + { + "nom": "Connaître les régularités des marques de temps et de personne." + }, + { + "nom": "Connaître les trois groupes de verbes et les régularités de marques de temps et de personne aux temps simples.", + "niveau": "CM1" + }, + { + "nom": "Sur le plan morphologique, repérer le radical, les marques de temps et les marques de personne.", + "niveau": "CM2" + }, + { + "nom": "Mémoriser: le présent, l'imparfait, le futur, le passé simple, le passé composé, le plus-que-parfait de l'indicatif, le conditionnel présent et l'impératif présent pour :- être et avoir ;- les verbes du 1er et du 2e groupe ;- les verbes irréguliers du 3e groupe : faire, aller, dire, venir, pouvoir, voir, vouloir, prendre.- distinguer temps simples et temps composés ;- comprendre la notion de participe passé." + }, + { + "nom": "Maîtriser la conjugaison du présent, de l'imparfait, du futur, et du passé composé pour : être et avoir ; les verbes du 1er groupe ; les verbes du 2e groupe ; les verbes irréguliers du 3e groupe : faire, aller, dire, venir, pouvoir, voir, vouloir, prendre.", + "niveau": "CM1" + }, + { + "nom": "Connaître les marques de temps de l'imparfait et du futur de l'indicatif.", + "niveau": "CM1" + }, + { + "nom": "En plus des temps déjà appris, mémoriser le passé simple et le plus-que-parfait pour : être et avoir ; les verbes du 1er et du 2e groupe ; les verbes irréguliers du 3e groupe : faire, aller, dire, venir, pouvoir, voir, vouloir, prendre.", + "niveau": "CM2" + }, + { + "nom": "Identifier les marques de temps du passé simple.", + "niveau": "CM2" + }, + { + "nom": "Distinguer temps simples et temps composés." + }, + { + "nom": "En s'appuyant sur sa connaissance du passé composé, faire la différence entre temps simples et temps composés.", + "niveau": "CM1" + }, + { + "nom": "Connaître le passé composé et comprendre la formation du plus-que-parfait de l'indicatif.", + "niveau": "CM2" + }, + { + "nom": "Comprendre la notion de participe passé." + }, + { + "nom": "Comprendre la notion de participe passé et travailler sur son accord quand il est employé avec le verbe être.", + "niveau": "CM2" + }, + { + "nom": "Enrichir son lexique par la lecture, en lien avec le programme de culture littéraire et artistique." + }, + { + "nom": "Se servir du contexte pour comprendre les mots inconnus rencontrés au cours de ses lectures.", + "niveau": "CM2" + }, + { + "nom": "Enrichir son lexique par l'usage du dictionnaire ou autres outils en version papier ou numérique." + }, + { + "nom": "Utiliser des dictionnaires, au format papier ou numérique pour enrichir son lexique en trouvant synonymes ou antonymes.", + "niveau": "CM1" + }, + { + "nom": "Recourir à un dictionnaire pour lever les questions sémantiques en cas d'homonymie.", + "niveau": "CM1" + }, + { + "nom": "Utiliser des dictionnaires dont le fonctionnement est maitrisé. Prendre connaissance de l'intégralité d'un article et y distinguer les différentes informations qui y figurent.", + "niveau": "CM2" + }, + { + "nom": "Savoir réutiliser à bon escient le lexique appris à l'écrit et à l'oral." + }, + { + "nom": "Réutiliser le lexique appris dans des situations de communication écrites ou orales.", + "niveau": "CM1" + }, + { + "nom": "Réutiliser à bon escient le lexique appris à l'écrit et à l'oral.", + "niveau": "CM2" + }, + { + "nom": "Comprendre la formation des mots : mots simples, mots complexes." + }, + { + "nom": "Comprendre la formation des mots complexes : par dérivation et par composition." + }, + { + "nom": "Découvrir la notion de dérivation.", + "niveau": "CM2" + }, + { + "nom": "Découvrir en contexte la formation des mots par composition.", + "niveau": "CM2" + }, + { + "nom": "Connaître le sens des principaux préfixes : découvrir des bases latines et grecques." + }, + { + "nom": "Repérer dans des corpus de mots complexes les principaux préfixes et suffixes et en connaît le sens.", + "niveau": "CM1" + }, + { + "nom": "Consolider sa connaissance du sens des principaux préfixes et découvrir les racines latines et grecques.", + "niveau": "CM2" + }, + { + "nom": "Approfondir sa connaissance des préfixes et suffixes les plus fréquents, notamment en proposant un classement sémantique.", + "niveau": "CM2" + }, + { + "nom": "Identifier les niveaux de langue familier, courant, soutenu." + }, + { + "nom": "Mettre en réseau des mots (groupements par familles de mots, par champ lexical)." + }, + { + "nom": "Mettre en réseau des mots en identifiant les familles de mots.", + "niveau": "CM1" + }, + { + "nom": "Pour un champ lexical donné, regrouper des mots.", + "niveau": "CM2" + }, + { + "nom": "Connaître les notions de synonymie, antonymie, homonymie, polysémie." + }, + { + "nom": "Connaître la synonymie et l'antonymie et découvre la notion d'homonymie.", + "niveau": "CM1" + }, + { + "nom": "Consolider sa connaissance de l'homonymie et découvrir la notion de polysémie.", + "niveau": "CM2" + }, + { + "nom": "Mémoriser l'orthographe des mots invariables appris en grammaire." + }, + { + "nom": "Mémoriser de nouveaux mots invariables.", + "niveau": "CM1" + }, + { + "nom": "Retenir le caractère invariable et l'orthographe de certains mots en grammaire, comme les prépositions, les conjonctions de coordination et les adverbes les plus usuels.", + "niveau": "CM1" + }, + { + "nom": "Orthographier correctement les mots invariables appris en grammaire grâce à l'acquisition d'automatismes.", + "niveau": "CM2" + }, + { + "nom": "Mémoriser le lexique appris en s'appuyant sur ses régularités, sa formation." + }, + { + "nom": "Mémoriser le lexique appris en s'appuyant sur ses régularités, sa formation.", + "niveau": "CM1" + }, + { + "nom": "Acquérir des repères orthographiques en s'appuyant sur la formation des mots et leur étymologie." + } + ] + } + ] + }, + { + "nom": "Langues vivantes (étrangères ou régionales)", + "categories": [ + { + "nom": "Compréhension de l'oral (écouter et comprendre)", + "competences": [ + { + "nom": "Suivre le fil d'une histoire simple" + }, + { + "nom": "Isoler des informations simples dans un message clair." + }, + { + "nom": "Repérer des indices sonores simples dans une variété de supports simples (comptines, chansons, contes, légendes, dialogues, etc.)." + }, + { + "nom": "Associer un document oral à un visuel." + }, + { + "nom": "Isoler des informations simples dans un message clair et les relier à un titre ou un thème." + }, + { + "nom": "Suivre l'idée générale de textes oraux de genres différents sur un sujet familier (contes, légendes, courts récits de fiction, exposés), si le message est délivré clairement, dans un langage simple." + }, + { + "nom": "Comprendre un message oral court sur un sujet familier ou d'actualité" + }, + { + "nom": "Comprendre l'ensemble des consignes utilisées en classe." + }, + { + "nom": "Comprendre des mots familiers et des expressions courtes dans une conversation simple à condition que l'interlocuteur parle lentement et très distinctement." + }, + { + "nom": "Distinguer des blocs ou des segments." + }, + { + "nom": "Relever une tonalité pour comprendre le sens d'un message (plaisanterie, agressivité, etc.)." + }, + { + "nom": "Suivre l'idée générale d'une conversation sur un sujet familier ou d'un extrait de documentaire sur un sujet d'actualité, si le message est délivré lentement et distinctement." + }, + { + "nom": "Comprendre et agir" + }, + { + "nom": "Associer un document oral à un visuel." + }, + { + "nom": "Suivre des consignes courtes et simples." + }, + { + "nom": "Repérer dans un message une instruction : autorisation, obligation ou d'interdiction pour s'y conformer." + }, + { + "nom": "Suivre une série d'instructions données pour réaliser une tâche simple." + } + ] + }, + { + "nom": "Compréhension de l'écrit (lire)", + "competences": [ + { + "nom": "Comprendre des textes courts et simples" + }, + { + "nom": "Repérer des mots isolés dans un énoncé ou un texte court." + }, + { + "nom": "Comprendre des messages ou courriels simples, des annonces ou de courts récits illustrés, au sujet d'activités quotidiennes et rédigés avec des mots simples." + }, + { + "nom": "Se faire une idée du contenu d'un texte informatif assez simple, surtout s'il est accompagné d'un document visuel l'illustrant." + }, + { + "nom": "Identifier la typologie d'un support écrit et en repérer le thème." + }, + { + "nom": "Comprendre une lettre personnelle, un courriel ou une publication simples, dans lesquels il est question de sujets familiers." + }, + { + "nom": "Comprendre les points principaux de textes informatifs courts qui traitent de sujets quotidiens." + }, + { + "nom": "Suivre le récit d'un album jeunesse, ou d'une histoire simple accompagnée d'images." + }, + { + "nom": "Identifier la trame narrative d'un récit clairement structuré" + }, + { + "nom": "Repérer la structure narrative de courts récits illustrés, au sujet d'activités quotidiennes, et rédigés avec des mots simples, à condition que les images l'aident à deviner le contenu." + }, + { + "nom": "Accéder à la trame d'une narration brève et répétitive." + }, + { + "nom": "Comprendre suffisamment pour lire des histoires et des bandes dessinées courtes mettant en scène des situations concrètes et familières, rédigées dans une langue simple et usuelle." + }, + { + "nom": "Traiter les informations et agir" + }, + { + "nom": "Comprendre des consignes et suivre des indications courtes et simples dans un texte informatif, un mode d'emploi ou une recette." + }, + { + "nom": "Repérer dans un texte court une information d'autorisation, d'obligation ou d'interdiction." + }, + { + "nom": "Sélectionner dans un texte les informations pertinentes en lien avec un questionnement donné et les classer par catégories." + }, + { + "nom": "Identifier et comprendre des informations et des consignes dans un texte pour réaliser une tâche simple." + } + ] + }, + { + "nom": "Expression orale en continu (parler en continu)", + "competences": [ + { + "nom": "Repères phonologiques" + }, + { + "nom": "Prononcer un répertoire d'expressions et de mots mémorisés, de manière compréhensible." + }, + { + "nom": "Lire à haute voix et de manière expressive un texte bref (petit dialogue, poème, extrait d'album de littérature jeunesse, etc.) à condition qu'il ait été préparé auparavant." + }, + { + "nom": "Reproduire et reprendre à son compte des phrases simples avec l'aide de son interlocuteur." + }, + { + "nom": "S'exprimer brièvement, mais de manière suffisamment claire pour être compris. L'interlocuteur devra parfois faire répéter et coopérer." + }, + { + "nom": "Se présenter oralement et présenter les autres" + }, + { + "nom": "Lire et reproduire un modèle oral court (répéter, réciter, etc.)." + }, + { + "nom": "Décrire brièvement des personnes, des objets ou des animaux familiers après y avoir été entraîné." + }, + { + "nom": "Décrire brièvement des personnes, des objets ou des animaux en réponse à une sollicitation." + }, + { + "nom": "Coordonner plusieurs éléments simples dans une description ou présentation élémentaire." + }, + { + "nom": "Décrire des personnes, des objets ou des animaux en prenant appui sur une liste de points et en se reprenant au besoin." + }, + { + "nom": "Raconter" + }, + { + "nom": "Raconter une histoire courte en quelques mots, en juxtaposant des phrases simples et en s'aidant d'images." + }, + { + "nom": "Raconter des faits brefs en quelques mots ou phrases simples et en s'aidant d'images." + }, + { + "nom": "Parler de soi-même à l'aide de mots et d'expressions simples et juxtaposés." + }, + { + "nom": "Raconter une histoire courte en juxtaposant des phrases simples et en s'aidant d'images." + }, + { + "nom": "En prenant appui sur une liste de mots-clés :- relater succinctement un événement ;- raconter une histoire." + }, + { + "nom": "Décrire son environnement quotidien, des personnes ou des activités, et exprimer ses goûts" + }, + { + "nom": "Se présenter et se décrire avec des mots et des expressions simples, à condition d'avoir pu préparer son discours." + }, + { + "nom": "Exprimer ses activités préférées à l'aide d'expressions toutes faites." + }, + { + "nom": "Exprimer ses goûts et ses activités préférées de façon simple et succincte." + }, + { + "nom": "S'exprimer brièvement et de manière suffisamment claire sur un sujet relatif à sa vie quotidienne, en prenant appui sur ses actions et ses projets." + }, + { + "nom": "Expliciter brièvement en quoi une chose lui plaît ou lui déplaît." + }, + { + "nom": "Exprimer des préférences en s'appuyant sur des comparaisons, de façon simple et directe." + } + ] + }, + { + "nom": "Expression orale en interaction (réagir et dialoguer)", + "competences": [ + { + "nom": "Échanger des informations" + }, + { + "nom": "Suivre des directives ou consignes simples, brèves ou routinières." + }, + { + "nom": "Demander de ses nouvelles à quelqu'un et réagir en utilisant des formules de politesse." + }, + { + "nom": "Répondre à des questions très simples et en poser de manière ritualisée sur des sujets familiers et sur les habitudes quotidiennes." + }, + { + "nom": "Réagir à des affirmations très simples et en émettre, en utilisant des formules courtes ou toutes faites, en s'aidant éventuellement de gestes pour renforcer la communication." + }, + { + "nom": "Interagir dans des situations familières à l'aide d'expressions toutes faites." + }, + { + "nom": "Poser des questions, y répondre et échanger de manière simple des idées et des renseignements sur des sujets connus dans des situations familières et prévisibles." + }, + { + "nom": "Interagir dans des situations prévisibles et dans de brèves discussions avec l'aide de l'interlocuteur si besoin." + }, + { + "nom": "Exprimer ses émotions ou ses souhaits et réagir" + }, + { + "nom": "Exprimer simplement des sentiments et émotions, à l'aide d'expressions idiomatiques ou toutes faites pour:- demander et dire comment on se porte ;- réagir à la réponse donnée." + }, + { + "nom": "Exprimer de manière simple (y compris par onomatopées) des émotions ou réactions telles que la joie, la tristesse, la déception, la peur ou la surprise." + }, + { + "nom": "Réagir positivement ou négativement aux demandes de son interlocuteur." + }, + { + "nom": "S'appuyer sur les réactions de son interlocuteur pour faire part de ses propres sentiments." + }, + { + "nom": "Réagir à des situations familières en exprimant de manière simple des réactions, émotions ou sentiments tels que la satisfaction, la surprise, le mécontentement, l'empathie, l'impatience, le dégoût." + }, + { + "nom": "Marquer son adhésion ou son désaccord." + }, + { + "nom": "Clarifier ou faire clarifier un point" + }, + { + "nom": "Clarifier ou faire clarifier des points très simples, notamment en situation de communication de classe, comme :- épeler des mots familiers ;- demander de répéter et répéter soi-même un mot ou une phrase très brève ;- demander le sens ou la traduction d'un mot ;- dire qu'on ne comprend pas." + }, + { + "nom": "Répéter une consigne simple et connue afin de s'assurer de sa bonne compréhension." + }, + { + "nom": "Solliciter l'aide de son interlocuteur." + }, + { + "nom": "Répéter une phrase simple afin de s'assurer de sa bonne compréhension." + }, + { + "nom": "Reformuler en partie les propos de l'interlocuteur pour confirmer une compréhension mutuelle." + }, + { + "nom": "Demander à une personne d'expliciter de manière plus précise ou plus claire ce qu'elle vient de dire." + }, + { + "nom": "Clarifier sur demande des informations simples et familières en donnant quelques renseignements complémentaires comme l'heure, la date, un lieu, des détails sur une personne." + }, + { + "nom": "Établir un contact (saluer, se présenter, présenter quelqu'un, etc.)" + }, + { + "nom": "Etablir un contact social en utilisant quelques formules de politesse : salutation et prise de congé, présentations et expressions du type « merci », « s'il vous plaît », « pardon »." + }, + { + "nom": "Réagir à des propositions dans des situations de la vie courante (remercier, féliciter, présenter des excuses, accepter, refuser)." + }, + { + "nom": "Demander des informations complémentaires très simples (« et ? », « qui est-ce ? », « qu'est-ce que c'est ? », etc.) si le fil des échanges porte sur des faits familiers et simples, avec un interlocuteur qui adopte un débit lent et accepte de répéter." + }, + { + "nom": "Clore une conversation à l'aide de mots très simples comme « merci », « au revoir », « d'accord »." + }, + { + "nom": "Engager et clore une conversation de manière adaptée en fonction de son interlocuteur." + }, + { + "nom": "Utiliser les formules quotidiennes de politesse et d'adresse." + }, + { + "nom": "Accueillir quelqu'un, demander de ses nouvelles et réagir à sa réponse." + }, + { + "nom": "Relancer une conversation à l'aide de quelques questions complémentaires dans le cadre d'une situation simple et prévisible." + }, + { + "nom": "Clore une conversation simplement, en tenant compte du contexte et des particularités de ses interlocuteurs." + } + ] + }, + { + "nom": "Expression écrite (écrire et réagir à l'écrit)", + "competences": [ + { + "nom": "Épeler, copier ou écrire sous la dictée des éléments connus" + }, + { + "nom": "Copier et écrire sous la dictée quelques mots isolés, déjà connus, et des textes courts." + }, + { + "nom": "Noter et reprendre à son compte des phrases simples sous la dictée ou extraites d'un document." + }, + { + "nom": "Ecrire un texte court en rapport avec des besoins." + }, + { + "nom": "Raconter" + }, + { + "nom": "Raconter succinctement des expériences vécues ou imaginées en mobilisant des structures grammaticales et des expressions simples appartenant à un répertoire mémorisé." + }, + { + "nom": "Ecrire des phrases et des expressions simples sur lui-même et des personnages imaginaires, dire où ils vivent et ce qu'ils font." + }, + { + "nom": "Relater brièvement un événement réel ou fictif, des activités passées ou des expériences personnelles, en s'appuyant sur des énoncés déjà mémorisés." + }, + { + "nom": "Mobiliser des structures simples pour écrire des phrases en s'appuyant sur une trame connue" + }, + { + "nom": "Décrire des objets ou des lieux familiers." + }, + { + "nom": "Produire de manière autonome quelques phrases sur lui-même, les autres, des personnages réels ou imaginaires, en s'appuyant sur des modèles connus." + }, + { + "nom": "Rédiger un courrier bref et simple, en référence à des modèles (message électronique court, carte postale, lettre)." + }, + { + "nom": "Décrire des objets, des lieux familiers ou des personnes en articulant les phrases avec des connecteurs très simples (« et », « ou », « mais »)." + }, + { + "nom": "Ecrire quelques lignes, à l'aide d'expressions et de phrases courtes reliées par des connecteurs simples, en suivant un modèle ou une trame connus (description, courriel, dialogue simple, etc.)." + }, + { + "nom": "Ecrire quelques lignes, à l'aide d'expressions et de phrases courtes reliées par des connecteurs simples, en suivant un modèle ou une trame connus (description, courrier, entrée de journal, dialogue, etc.)." + }, + { + "nom": "Ecrire le début d'une histoire ou en continuer une (y compris sous forme d'écrit collaboratif)." + }, + { + "nom": "Ecrire une courte biographie (imaginaire ou non) ou un court poème sur un sujet familier." + }, + { + "nom": "Écrire pour décrire, informer ou exprimer un point de vue" + }, + { + "nom": "Ecrire des mots et des expressions isolées pour évoquer ou décrire très simplement certains objets familiers." + }, + { + "nom": "Compléter une fiche de renseignements très simple." + }, + { + "nom": "Rédiger un courrier court et simple, écrire un message ou y répondre (y compris sur support numérique), en s'appuyant sur un modèle et en réinvestissant les apprentissages de la classe." + }, + { + "nom": "Réagir très simplement, positivement ou négativement, à des propos et à des commentaires, en réinvestissant des expressions apprises." + }, + { + "nom": "Paraphraser des phrases simples de type annonce, consigne, mode d'emploi, etc." + }, + { + "nom": "Compléter une fiche de renseignements simple." + }, + { + "nom": "Notifier un avis, une consigne simple par écrit." + }, + { + "nom": "Ecrire des messages et des publications personnelles très simples en ligne, à l'aide d'un outil de traduction." + }, + { + "nom": "Réagir simplement, positivement ou négativement, à des propos et à des commentaires." + }, + { + "nom": "Ecrire des textes courts sur des sujets familiers, en liant les phrases avec des connecteurs." + }, + { + "nom": "Rédiger des informations simples sur lui-même et commencer à les organiser." + }, + { + "nom": "Donner son opinion et ses impressions dans des écrits portant sur des sujets d'intérêt personnel, en utilisant un vocabulaire et des expressions courantes." + }, + { + "nom": "Gérer des échanges simples en ligne, poser des questions, répondre, commenter brièvement, échanger des idées à condition de n'avoir qu'un seul interlocuteur à la fois, à partir d'énoncés proches de ceux rencontrés en classe." + } + ] + }, + { + "nom": "Médiation", + "competences": [ + { + "nom": "Prendre des notes, paraphraser" + }, + { + "nom": "Lister et transmettre, à l'écrit ou à l'oral, des informations d'un intérêt immédiat : noms, nombres, prix, détails factuels, à condition qu'elles soient prévisibles et formulées dans une langue simple." + }, + { + "nom": "Passer d'une langue à l'autre pour reformuler des consignes de travail simples en vue de s'assurer de la compréhension de tous." + }, + { + "nom": "Transmettre un message ou une idée simple qui lie plusieurs informations d'intérêt immédiat." + }, + { + "nom": "Redonner en langue étrangère (ou en français) des consignes de travail simples données en français (ou en langue étrangère), en vue de s'assurer de la compréhension de tous, en prenant, le cas échéant, l'initiative en fonction des besoins ou habitudes de ses interlocuteurs." + }, + { + "nom": "Prendre en notes et transmettre sous forme de mots ou d'informations très brèves les points principaux de propos ou de textes courts sur des sujets concrets et familiers, à condition qu'ils soient exprimés clairement et dans une langue simple." + }, + { + "nom": "Compléter son répertoire langagier disponible par des gestes ou mots empruntés à d'autres langues." + }, + { + "nom": "Passer d'une langue à l'autre pour reformuler des consignes de travail simples en vue de s'assurer de la compréhension de tous." + }, + { + "nom": "Identifier les repères culturels" + }, + { + "nom": "Identifier une similitude ou une différence culturelle." + }, + { + "nom": "Identifier une difficulté de compréhension d'ordre culturel et la signaler." + }, + { + "nom": "Reconnaître les difficultés de compréhension d'un fait culturel relatif à la vie quotidienne et tenter de le rendre accessible à autrui en utilisant un langage simple." + }, + { + "nom": "Expliciter un message, une situation, un document pour autrui" + }, + { + "nom": "Indiquer avec des mots et des gestes simples les besoins élémentaires d'une tierce personne dans une situation précise." + }, + { + "nom": "Attirer l'attention sur des informations simples et prévisibles d'un intérêt immédiat, données dans des supports courts et simples tels que des panneaux, des annonces, affiches, programmes, dépliants, etc." + }, + { + "nom": "Isoler dans un message (surligner, répéter) les éléments qui relèvent de l'expression du besoin, par exemple, afin d'aider autrui à le comprendre." + }, + { + "nom": "Etablir une liste d'informations convergentes sur un sujet d'intérêt immédiat." + }, + { + "nom": "Reformuler en partie le point principal d'un message simple sur un sujet quotidien afin d'aider autrui à le comprendre." + }, + { + "nom": "Expliciter en une liste de points des informations concrètes sur des sujets familiers, à condition de pouvoir s'y préparer." + }, + { + "nom": "Expliciter des informations pertinentes présentes dans des textes informatifs structurés, courts et simples, à condition qu'elles portent sur des sujets concrets, familiers et qu'elles soient formulées dans une langue usuelle et simple." + }, + { + "nom": "Participer à un travail collectif, coopérer et contribuer à des échanges interculturels" + }, + { + "nom": "Faciliter un échange interculturel en se montrant accueillant et en manifestant son intérêt avec des mots simples, des expressions non verbales ou des gestes." + }, + { + "nom": "Utiliser des mots simples, des expressions non verbales ou des gestes pour montrer son intérêt pour une idée." + }, + { + "nom": "Demander aux participants leur avis ou des contributions à des tâches élémentaires, à l'aide d'expressions mémorisées." + }, + { + "nom": "Dire très simplement qu'il a compris, qu'il est d'accord, que tout va bien (dans la tâche à accomplir ou dans la communication)." + }, + { + "nom": "Demander aux autres s'ils ont compris, s'ils sont d'accord, s'ils ont un problème." + }, + { + "nom": "Prendre en charge l'accueil d'autrui par des mots simples et des expressions non verbales pour faciliter un échange interculturel." + }, + { + "nom": "Demander aux participants des contributions à des tâches élémentaires, en s'exprimant par des phrases simples et courtes." + }, + { + "nom": "Dire qu'il a compris et demander aux autres s'ils ont compris." + }, + { + "nom": "Demander de l'aide ou signaler le besoin d'aide d'autrui." + }, + { + "nom": "Contribuer à un échange interculturel en demandant, avec des mots simples, à un interlocuteur d'expliquer ou de clarifier son propos." + }, + { + "nom": "Exprimer son accord ou son désaccord avec son interlocuteur, le remercier, l'encourager, l'inviter à compléter son propos, etc." + }, + { + "nom": "Animer un travail collectif en donnant des consignes simples et en apportant éventuellement son aide pour les formulations." + }, + { + "nom": "Demander l'avis de quelqu'un sur une idée donnée." + } + ] + } + ] + }, + { + "nom": "Arts plastiques", + "categories": [] + }, + { + "nom": "Éducation musicale", + "categories": [] + }, + { + "nom": "Histoire des arts", + "categories": [] + }, + { + "nom": "Éducation physique et sportive", + "categories": [] + }, + { + "nom": "Histoire et géographie", + "categories": [ + { + "nom": "Histoire", + "competences": [ + { + "nom": "Quelles traces d'une occupation ancienne du territoire français ?", + "niveau": "CM1" + }, + { + "nom": "Celtes, Gaulois, Grecs et Romains: quels héritages des mondes anciens ?", + "niveau": "CM1" + }, + { + "nom": "Les grands mouvements et déplacements de populations (IV-Xe siècles).", + "niveau": "CM1" + }, + { + "nom": "Clovis et Charlemagne, Mérovingiens et Carolingiens dans la continuité de l'empire romain.", + "niveau": "CM1" + }, + { + "nom": "Louis IX, le « roi chrétien » au XIIIe siècle.", + "niveau": "CM1" + }, + { + "nom": "François Ier, un protecteur des Arts et des Lettres à la Renaissance.", + "niveau": "CM1" + }, + { + "nom": "Henri IV et l'édit de Nantes.", + "niveau": "CM1" + }, + { + "nom": "Louis XIV, le roi Soleil à Versailles.", + "niveau": "CM1" + }, + { + "nom": "De l'année 1789 à l'exécution du roi : Louis XVI, la Révolution, la Nation.", + "niveau": "CM1" + }, + { + "nom": "Napoléon Bonaparte, du général à l'Empereur, de la Révolution à l'Empire", + "niveau": "CM1" + }, + { + "nom": "1892 : la République fête ses cent ans.", + "niveau": "CM2" + }, + { + "nom": "L'école primaire au temps de Jules Ferry.", + "niveau": "CM2" + }, + { + "nom": "Des républiques, une démocratie: des libertés, des droits et des devoirs.", + "niveau": "CM2" + }, + { + "nom": "Les énergies majeures de l'âge industriel (charbon puis pétrole) et les machines.", + "niveau": "CM2" + }, + { + "nom": "Le travail à la mine, à l'usine, à l'atelier, au grand magasin.", + "niveau": "CM2" + }, + { + "nom": "La ville industrielle.", + "niveau": "CM2" + }, + { + "nom": "Le monde rural.", + "niveau": "CM2" + }, + { + "nom": "Deux guerres mondiales au vingtième siècle.", + "niveau": "CM2" + }, + { + "nom": "La construction européenne.", + "niveau": "CM2" + }, + { + "nom": "Les débuts de l'humanité.", + "niveau": "6ème" + }, + { + "nom": "La « révolution » néolithique.", + "niveau": "6ème" + }, + { + "nom": "Premiers États, premières écritures.", + "niveau": "6ème" + }, + { + "nom": "Le monde des cités grecques.", + "niveau": "6ème" + }, + { + "nom": "Rome du mythe à l'histoire.", + "niveau": "6ème" + }, + { + "nom": "La naissance du monothéisme juif dans un monde polythéiste.", + "niveau": "6ème" + }, + { + "nom": "Conquêtes, paix romaine et romanisation.", + "niveau": "6ème" + }, + { + "nom": "Des chrétiens dans l'empire.", + "niveau": "6ème" + }, + { + "nom": "Les relations de l'empire romain avec les autres mondes anciens : l'ancienne route de la soie et la Chine des Han.", + "niveau": "6ème" + } + ] + }, + { + "nom": "Géographie", + "competences": [ + { + "nom": "Identifier les caractéristiques de mon(mes) lieu(x) de vie.", + "niveau": "CM1" + }, + { + "nom": "Localiser mon (mes) lieu(x) de vie et le(s) situer à différentes échelles.", + "niveau": "CM1" + }, + { + "nom": "Dans des espaces urbains.", + "niveau": "CM1" + }, + { + "nom": "Dans un espace touristique.", + "niveau": "CM1" + }, + { + "nom": "Satisfaire les besoins en énergie, en eau.", + "niveau": "CM1" + }, + { + "nom": "Satisfaire les besoins alimentaires.", + "niveau": "CM1" + }, + { + "nom": "Se déplacer au quotidien en France.", + "niveau": "CM2" + }, + { + "nom": "Se déplacer au quotidien dans un autre lieu du monde.", + "niveau": "CM2" + }, + { + "nom": "Se déplacer de ville en ville, en France, en Europe et dans le monde.", + "niveau": "CM2" + }, + { + "nom": "Déplacement et développement durable.", + "niveau": "CM2" + }, + { + "nom": "Un monde de réseaux.", + "niveau": "CM2" + }, + { + "nom": "Un habitant connecté au monde.", + "niveau": "CM2" + }, + { + "nom": "Des habitants inégalement connectés dans le monde.", + "niveau": "CM2" + }, + { + "nom": "Favoriser la place de la «nature» en ville.", + "niveau": "CM2" + }, + { + "nom": "Recycler.", + "niveau": "CM2" + }, + { + "nom": "Habiter un écoquartier.", + "niveau": "CM2" + }, + { + "nom": "Les métropoles et leurs habitants.", + "niveau": "6ème" + }, + { + "nom": "La ville de demain.", + "niveau": "6ème" + }, + { + "nom": "Habiter un espace à forte(s) contrainte(s) naturelle(s) ou/et de grande biodiversité.", + "niveau": "6ème" + }, + { + "nom": "Habiter un espace de faible densité à vocation agricole.", + "niveau": "6ème" + }, + { + "nom": "Littoral industrialo-portuaire, littoral touristique.", + "niveau": "6ème" + }, + { + "nom": "La répartition de la population mondiale et ses dynamiques.", + "niveau": "6ème" + }, + { + "nom": "La variété des formes d'occupation spatiale dans le monde.", + "niveau": "6ème" + } + ] + } + ] + }, + { + "nom": "Sciences et technologie", + "categories": [] + }, + { + "nom": "Mathématiques", + "categories": [ + { + "nom": "Nombres et calculs", + "competences": [ + { + "nom": "Utiliser et représenter les grands nombres entiers, des fractions simples, les nombres décimaux.", + "fin_cycle": true + }, + { + "nom": "Calculer avec des nombres entiers et des nombres décimaux.", + "fin_cycle": true + }, + { + "nom": "Résoudre des problèmes en utilisant des fractions simples, les nombres décimaux et le calcul.", + "fin_cycle": true + }, + { + "nom": "Connaître les unités de la numération décimale pour les nombres entiers (unités simples, dizaines, centaines, milliers, millions, milliards) et les relations qui les lient." + }, + { + "nom": "Connaitre les unités de la numération décimale pour les nombres entiers (unités simples, dizaines, centaines, milliers, millions, milliards) et les relations qui les lient.", + "niveau": "CM2" + }, + { + "nom": "Composer, décomposer les grands nombres entiers, en utilisant des regroupements par milliers." + }, + { + "nom": "Composer, décomposer les grands nombres entiers, en utilisant des regroupements par milliers .", + "niveau": "CM2" + }, + { + "nom": "Comprendre et appliquer les règles de la numération décimale de position aux grands nombres entiers (jusqu'à 12 chiffres)." + }, + { + "nom": "Comprendre et appliquer les règles de la numération décimale de position aux grands nombres entiers (jusqu'à 12 chiffres).", + "niveau": "CM1" + }, + { + "nom": "Comparer, ranger, encadrer des grands nombres entiers, les repérer et les placer sur une demi-droite graduée adaptée." + }, + { + "nom": "Comparer, ranger, encadrer des grands nombres entiers, les repérer et les placer sur une demi-droite graduée adaptée.", + "niveau": "CM2" + }, + { + "nom": "Connaître diverses désignations des fractions : orales, écrites et décompositions additives et multiplicatives (ex : quatre tiers ; 4/3 ; 1/3+1/3+1/3+1/3 ; 1+1/3 ; 4x1/3)." + }, + { + "nom": "Donner progressivement aux fractions le statut de nombre.", + "niveau": "CM1" + }, + { + "nom": "Connaitre diverses désignations des fractions : orales, écrites et des décompositions additives et multiplicatives (ex : quatre tiers4/31/3 + 1/3 + 1/3 + 1/31 + 1/34 x 1/3).", + "niveau": "CM2" + }, + { + "nom": "Connaître et utiliser quelques fractions simples comme opérateur de partage en faisant le lien entre les formulations en langage courant et leur écriture mathématique (ex : faire le lien entre « la moitié de » et multiplier par 1/2)." + }, + { + "nom": "Utiliser les fractions simples (comme 2/3, 1/4, 5/2) dans le cadre de partage de grandeurs ou de mesures de grandeurs, et des fractions décimales (1/10, 1/100), il fait le lien entre les formulations en langage courant et leur écriture mathématique (par exemple faire le lien entre « la moitié de » et 1/2 dans l'expression « une demi-heure »).", + "niveau": "CM2" + }, + { + "nom": "Manipuler des fractions jusqu'à 1/1000.", + "niveau": "CM1" + }, + { + "nom": "Utiliser les fractions simples (comme 2/3, 1/4, 5/2) dans le cadre de partage de grandeurs ou de mesures de grandeurs, et des fractions décimales (1/10, 1/100), faire le lien entre les formulations en langage courant et leur écriture mathématique (par exemple faire le lien entre « la moitié de » et multiplier par 1/2).", + "niveau": "CM2" + }, + { + "nom": "Utiliser des fractions pour rendre compte de partages de grandeurs ou de mesures de grandeurs." + }, + { + "nom": "Repérer et placer des fractions sur une demi-droite graduée adaptée." + }, + { + "nom": "Les positionner sur une droite graduée.", + "niveau": "CM2" + }, + { + "nom": "Encadrer une fraction par deux nombres entiers consécutifs." + }, + { + "nom": "Les encadrer entre deux entiers consécutifs.", + "niveau": "CM2" + }, + { + "nom": "Comparer deux fractions de même dénominateur." + }, + { + "nom": "Comparer deux fractions de même dénominateur.", + "niveau": "CM1" + }, + { + "nom": "Écrire une fraction sous forme de somme d'un entier et d'une fraction inférieure à 1." + }, + { + "nom": "Ecrire une fraction décimale sous forme de somme d'un entier et d'une fraction inférieure à 1.", + "niveau": "CM1" + }, + { + "nom": "Connaître des égalités entre des fractions usuelles (exemples : 5/10=1/2 ; 10/100 = 1/10 ; 2/4=1/2). Utiliser des fractions pour exprimer un quotient." + }, + { + "nom": "Connaître les unités de la numération décimale (unités simples, dixièmes, centièmes, millièmes) et les relations qui les lient." + }, + { + "nom": "Utiliser les nombres décimaux.", + "niveau": "CM2" + }, + { + "nom": "Connaitre les unités de la numération décimale (unités simples, dixièmes, centièmes, millièmes) et les relations qui les lient.", + "niveau": "CM2" + }, + { + "nom": "Comprendre et appliquer aux nombres décimaux les règles de la numération décimale de position (valeurs des chiffres en fonction de leur rang)." + }, + { + "nom": "Comprendre et appliquer aux nombres décimaux les règles de la numération décimale de position (valeurs des chiffres en fonction de leur rang).", + "niveau": "CM2" + }, + { + "nom": "Connaître et utiliser diverses désignations orales et écrites d'un nombre décimal (fractions décimales, écritures à virgule, décompositions additives et multiplicatives)." + }, + { + "nom": "Connaitre et utiliser diverses désignations orales et écrites d'un nombre décimal (fractions décimales, écritures à virgule, décompositions additives et multiplicatives).", + "niveau": "CM2" + }, + { + "nom": "Utiliser les nombres décimaux pour rendre compte de mesures de grandeurs." + }, + { + "nom": "Utiliser les nombres décimaux pour rendre compte de mesures de grandeurs. Connaitre le lien entre les unités de numération et les unités de mesure (par exemple : dixième → dm , dg, dL", + "niveau": "CM1" + }, + { + "nom": "Utiliser les nombres décimaux pour rendre compte de mesures de grandeurs, connaitre le lien entre les unités de numération et les unités de mesure (par exemple : dixième → dm - dg - dL, centième → cm - cg - cL - centimes d'euro.)", + "niveau": "CM2" + }, + { + "nom": "Connaître le lien entre les unités de numération et les unités de mesure (par exemple : dixième dm/dg/dL, centième cm/cg/cL/centimes d'euro)." + }, + { + "nom": "Repérer et placer un nombre décimal sur une demi-droite graduée adaptée." + }, + { + "nom": "Repérer et placer un nombre décimal sur une demi-droite graduée adaptée.", + "niveau": "CM2" + }, + { + "nom": "Comparer, ranger des nombres décimaux." + }, + { + "nom": "Comparer, ranger des nombres décimaux.", + "niveau": "CM2" + }, + { + "nom": "Encadrer un nombre décimal par deux nombres entiers, par deux nombres décimaux." + }, + { + "nom": "Encadrer un nombre décimal par deux nombres entiers.", + "niveau": "CM1" + }, + { + "nom": "Encadrer un nombre décimal par deux nombres entiers, par deux nombres décimaux, trouver des nombres décimaux à intercaler entre deux nombres donnés.", + "niveau": "CM2" + }, + { + "nom": "Trouver des nombres décimaux à intercaler entre deux nombres donnés." + }, + { + "nom": "Connaître des procédures élémentaires de calcul, notamment :- multiplier ou diviser un nombre décimal par 10, par 100, par 1000 ;- rechercher le complément à l'entier supérieur ;- multiplier par 5, par 25, par 50, par 0,1, par 0,5." + }, + { + "nom": "Mémoriser les premiers multiples de 25 et de 50.", + "niveau": "CM1" + }, + { + "nom": "Multiplier et diviser par 10 des nombres décimaux.", + "niveau": "CM1" + }, + { + "nom": "Rechercher le complément au nombre entier supérieur. Stabiliser sa connaissance des propriétés des opérations (ex : 12 + 199 = 199 + 1245 × 21 = 45 × 20 + 456 × 18 = 6 × 20 - 6 × 2)", + "niveau": "CM1" + }, + { + "nom": "Connaitre les premiers multiples de 25 et de 50.", + "niveau": "CM2" + }, + { + "nom": "Multiplier par 5, 10, 50 et 100 des nombres décimaux.", + "niveau": "CM2" + }, + { + "nom": "Diviser par 10 et 100 des nombres décimaux.", + "niveau": "CM2" + }, + { + "nom": "Connaître des propriétés de l'addition, de la soustraction et de la multiplication, et notamment :- 12 + 199 = 199 + 12- 5 x 21 = 21 x 5- 27,9 + 1,2+ 0,8 = 27,9 + 2- 3,2 × 25 × 4 = 3,2 × 100- 45 × 21 = 45 × 20 + 45- 6 × 18 = 6 × 20 - 6 × 2- 23 × 7 + 23 × 3 = 23 × 10" + }, + { + "nom": "Rechercher le complément au nombre entier supérieur. Connaitre quelques propriétés des opérations (par exemple : 12 + 199 = 199 + 1245 × 21 = 45 × 20 + 456 × 18 = 6 × 20 - 6 × 2)", + "niveau": "CM2" + }, + { + "nom": "Connaître les critères de divisibilité par 2, 3, 5, 9 et 10." + }, + { + "nom": "Connaitre les critères de divisibilité par 2, 5 et 10.", + "niveau": "CM1" + }, + { + "nom": "Connaitre les critères de divisibilité par 2, 3, 5, 9 et 10.", + "niveau": "CM2" + }, + { + "nom": "Utiliser ces propriétés et procédures pour élaborer et mettre en œuvre des stratégies de calcul." + }, + { + "nom": "Utiliser les principales propriétés des opérations pour des calculs rendus plus complexes par la nature des nombres en jeu, leur taille ou leur nombre.", + "niveau": "CM2" + }, + { + "nom": "Vérifier la vraisemblance d'un résultat, notamment en estimant un ordre de grandeur." + }, + { + "nom": "Vérifier la vraisemblance d'un résultat, notamment en estimant un ordre de grandeur.", + "niveau": "CM2" + }, + { + "nom": "Dans un calcul en ligne, utiliser des parenthèses pour indiquer ou respecter une chronologie dans les calculs." + }, + { + "nom": "Mobiliser les faits numériques mémorisés au cycle 2, notamment les tables de multiplication jusqu'à 9." + }, + { + "nom": "Connaître les multiples de 25 et de 50, les diviseurs de 100." + }, + { + "nom": "Connaître et mettre en œuvre un algorithme de calcul posé pour effectuer :- l'addition, la soustraction et la multiplication de nombres entiers ou décimaux ;- la division euclidienne d'un entier par un entier ;- la division d'un nombre décimal (entier ou non) par un nombre entier." + }, + { + "nom": "Apprendre les algorithmes :- de l'addition, de la soustraction de deux nombres décimaux- de la division euclidienne de deux nombres entiers (ex : dans la division euclidienne de 125 par 4, le quotient est 31 et le reste est 1).", + "niveau": "CM1" + }, + { + "nom": "Apprendre les algorithmes :- de l'addition et de la soustraction de deux nombres décimaux- de la multiplication d'un nombre décimal par un nombre entier- de la division euclidienne de deux nombres entiers (quotient décimal ou non. Par exemple, 10 : 4 ou 10 : 3)- de la division d'un nombre décimal par un nombre entier.", + "niveau": "CM2" + }, + { + "nom": "Utiliser une calculatrice pour trouver ou vérifier un résultat." + }, + { + "nom": "Résoudre des problèmes mettant en jeu les quatre opérations :- sens des opérations ;- problèmes à une ou plusieurs étapes relevant des structures additive et/ou multiplicative." + }, + { + "nom": "Dès le début du cycle, résoudre des problèmes relevant des quatre opérations et faisant appel :- au sens des opérations- à des problèmes à une ou plusieurs étapes relevant des structures additives et/ou multiplicatives.", + "niveau": "CM1" + }, + { + "nom": "Résoudre des problèmes dont la progressivité combine notamment :- les nombres mis en jeu : entiers (tout au long du cycle) puis décimaux dès le CM1 sur des nombres très simples- le nombre d'étapes de raisonnement et de calcul que l'élève doit mettre en œuvre pour sa résolution- les supports proposés pour la prise d'informations : texte, tableau, représentations graphiques.", + "niveau": "CM1" + }, + { + "nom": "Communiquer la démarche sous différentes formes : langage naturel, schémas, opérations.", + "niveau": "CM1" + }, + { + "nom": "Résoudre des problèmes nécessitant l'emploi de l'addition ou de la soustraction (avec les entiers jusqu'au milliard et/ou des décimaux ayant jusqu'à trois décimales).", + "niveau": "CM2" + }, + { + "nom": "Résoudre des problèmes faisant intervenir la multiplication ou la division.", + "niveau": "CM2" + }, + { + "nom": "Résoudre des problèmes nécessitant une ou plusieurs étapes.", + "niveau": "CM2" + }, + { + "nom": "Prélever des données numériques à partir de supports variés. Produire des tableaux, diagrammes et graphiques organisant des données numériques." + }, + { + "nom": "Prélever des données numériques à partir de supports variés. Produire des tableaux, des diagrammes et des graphiques pour organiser les données numériques.", + "niveau": "CM2" + }, + { + "nom": "Exploiter et communiquer des résultats de mesures." + }, + { + "nom": "Exploiter et communiquer des résultats de mesures.", + "niveau": "CM2" + }, + { + "nom": "Lire ou construire des représentations de données :- tableaux (en deux ou plusieurs colonnes, à double entrée) ;- diagrammes en bâtons, circulaires ou semi-circulaires ;- graphiques cartésiens." + }, + { + "nom": "Lire ou construire des représentations de données sous forme de :- tableaux (en deux ou plusieurs colonnes, à double entrée)- diagrammes en bâtons, circulaires ou semi-circulaires- graphiques cartésiens.", + "niveau": "CM2" + }, + { + "nom": "Organiser des données issues d'autres enseignements (sciences et technologie, histoire et géographie, éducation physique et sportive…) en vue de les traiter." + }, + { + "nom": "Organiser des données issues d'autres enseignements (sciences et technologie, histoire et géographie, éducation physique et sportive...) en vue de les traiter.", + "niveau": "CM2" + }, + { + "nom": "Reconnaître et résoudre des problèmes relevant de la proportionnalité en utilisant une procédure adaptée : propriétés de linéarité (additive et multiplicative), passage à l'unité, coefficient de proportionnalité." + }, + { + "nom": "Résoudre des problèmes relevant de la proportionnalité dans chacun des trois domaines « nombres et calculs », « grandeurs et mesures » et « espace et géométrie ».", + "niveau": "CM1" + }, + { + "nom": "Mobiliser pour les traiter des formes de raisonnement spécifiques et des procédures adaptées, comme les propriétés de linéarité (additive et multiplicative).", + "niveau": "CM1" + }, + { + "nom": "Mobiliser pour les traiter des formes de raisonnement spécifiques et des procédures adaptées : les propriétés de linéarité (additive et multiplicative), le passage à l'unité, le coefficient de proportionnalité.", + "niveau": "CM2" + }, + { + "nom": "Appliquer un pourcentage." + } + ] + }, + { + "nom": "Grandeurs et mesures", + "competences": [ + { + "nom": "Comparer, estimer, mesurer des grandeurs géométriques avec des nombres entiers et des nombres décimaux : longueur (périmètre), aire, volume, angle.", + "fin_cycle": true + }, + { + "nom": "Utiliser le lexique, les unités, les instruments de mesures spécifiques de ces grandeurs.", + "fin_cycle": true + }, + { + "nom": "Résoudre des problèmes impliquant des grandeurs (géométriques, physiques, économiques) en utilisant des nombres entiers et des nombres décimaux.", + "fin_cycle": true + }, + { + "nom": "Comparer des périmètres avec ou sans recours à la mesure (par exemple en utilisant une ficelle, ou en reportant les longueurs des côtés d'un polygone sur un segment de droite avec un compas) :- notion de longueur : cas particulier du périmètre ;- unités relatives aux longueurs : relations entre les unités de longueur et les unités de numération." + }, + { + "nom": "Comparer des périmètres avec ou sans avoir recours à la mesure.", + "niveau": "CM1" + }, + { + "nom": "Mesurer des périmètres par report d'unités, et de fractions d'unités (par exemple en utilisant une ficelle) ou par report des longueurs des côtés sur un segment de droite avec le compas.", + "niveau": "CM2" + }, + { + "nom": "Travailler la notion de longueur avec le cas particulier du périmètre.", + "niveau": "CM2" + }, + { + "nom": "Connaitre les relations entre les unités de longueur et les unités de numération.", + "niveau": "CM1" + }, + { + "nom": "Calculer le périmètre d'un polygone en ajoutant les longueurs de ses côtés." + }, + { + "nom": "Calculer le périmètre d'un polygone en ajoutant les longueurs de ses côtés.", + "niveau": "CM2" + }, + { + "nom": "Calculer le périmètre d'un carré et d'un rectangle, la longueur d'un cercle, en utilisant une formule :- formule du périmètre d'un carré, d'un rectangle ;- formule de la longueur d'un cercle." + }, + { + "nom": "Etablir les formules du périmètre du carré et du rectangle, puis les utiliser, tout en continuant à calculer des périmètres de polygones variés en ajoutant les longueurs de leurs côtés.", + "niveau": "CM2" + }, + { + "nom": "Comparer des surfaces selon leurs aires sans avoir recours à la mesure, par superposition ou par découpage et recollement." + }, + { + "nom": "Comparer des surfaces selon leur aire, par estimation visuelle ou par superposition ou découpage et recollement.", + "niveau": "CM1" + }, + { + "nom": "Différencier périmètre et aire d'une figure." + }, + { + "nom": "Différencier aire et périmètre d'une figure.", + "niveau": "CM2" + }, + { + "nom": "Estimer la mesure d'une aire et l'exprimer dans une unité adaptée." + }, + { + "nom": "Déterminer des aires, ou les estimer, en faisant appel à une aire de référence. Les exprimer dans une unité adaptée.", + "niveau": "CM1" + }, + { + "nom": "Déterminer la mesure de l'aire d'une surface à partir d'un pavage simple ou en utilisant une formule :- unités usuelles d'aire et leurs relations : multiples et sous-multiples du m² ;- formules de l'aire d'un carré, d'un rectangle, d'un triangle, d'un disque." + }, + { + "nom": "Utiliser systématiquement une unité de référence. (Cette unité peut être une maille d'un réseau quadrillé adapté, le cm², le dm² ou le m².)", + "niveau": "CM1" + }, + { + "nom": "Utiliser les formules d'aire du carré et du rectangle.", + "niveau": "CM2" + }, + { + "nom": "Relier les unités de volume et de contenance." + }, + { + "nom": "Découvrir qu'un litre est la contenance d'un cube de 10 cm d'arête. Faire des analogies avec les autres unités de mesure à l'appui des préfixes.", + "niveau": "CM2" + }, + { + "nom": "Relier unités de volume et de contenance.", + "niveau": "CM1" + }, + { + "nom": "Estimer la mesure d'un volume ou d'une contenance par différentes procédures (transvasements, appréciation de l'ordre de grandeur) et l'exprimer dans une unité adaptée." + }, + { + "nom": "Comparer des contenances sans les mesurer, puis en les mesurant.", + "niveau": "CM2" + }, + { + "nom": "Estimer la mesure d'un volume ou d'une contenance par différentes procédures (transvasements, appréciation de l'ordre de grandeur) et l'exprimer dans une unité adaptée (multiples et sous-multiples du litre pour la contenance, cm3 , dm3 , m3 pour le volume).", + "niveau": "CM2" + }, + { + "nom": "Déterminer le volume d'un pavé droit en se rapportant à un dénombrement d'unités (cubes de taille adaptée) ou en utilisant une formule :- unités usuelles de contenance (multiples et sous multiples du litre) ;- unités usuelles de volume (cm3, dm3, m3), relations entre ces unités ;- formules du volume d'un cube, d'un pavé droit." + }, + { + "nom": "Utiliser de nouvelles unités de contenance : dL, cL et mL.", + "niveau": "CM2" + }, + { + "nom": "Identifier des angles dans une figure géométrique." + }, + { + "nom": "Identifier les angles d'une figure plane, puis comparer ces angles par superposition, avec du papier calque ou en utilisant un gabarit.", + "niveau": "CM2" + }, + { + "nom": "Comparer des angles, en ayant ou non recours à leur mesure (par superposition, avec un calque)." + }, + { + "nom": "Reproduire un angle donné en utilisant un gabarit." + }, + { + "nom": "Estimer qu'un angle est droit, aigu ou obtus." + }, + { + "nom": "Estimer, puis vérifier en utilisant l'équerre, qu'un angle est droit, aigu ou obtus.", + "niveau": "CM1" + }, + { + "nom": "Utiliser l'équerre pour vérifier qu'un angle est droit, aigu ou obtus, ou pour construire un angle droit." + }, + { + "nom": "Construire un angle droit à l'aide de l'équerre.", + "niveau": "CM2" + }, + { + "nom": "Utiliser le rapporteur pour :- déterminer la mesure en degré d'un angle ;- construire un angle de mesure donnée en degrés.- Notion d'angle.- Lexique associé aux angles : angle droit, aigu, obtus.- Mesure en degré d'un angle." + }, + { + "nom": "Résoudre des problèmes de comparaison avec et sans recours à la mesure." + }, + { + "nom": "Résoudre des problèmes de comparaison avec et sans recours à la mesure.", + "niveau": "CM1" + }, + { + "nom": "Résoudre des problèmes dont la résolution mobilise simultanément des unités différentes de mesure et/ou des conversions." + }, + { + "nom": "Mobiliser simultanément des unités différentes de mesure et/ou des conversions.", + "niveau": "CM1" + }, + { + "nom": "Calculer des périmètres, des aires ou des volumes, en mobilisant ou non, selon les cas, des formules donnant :- le périmètre d'un carré, d'un rectangle, la longueur d'un cercle ;- l'aire d'un carré, d'un rectangle, d'un triangle, d'un disque ;- le volume d'un cube, d'un pavé droit." + }, + { + "nom": "Calculer des périmètres, des aires ou des volumes, en mobilisant ou non, selon les cas, des formules donnant :- le périmètre d'un carré, d'un rectangle- l'aire d'un carré, d'un rectangle.", + "niveau": "CM1" + }, + { + "nom": "Calculer la durée écoulée entre deux instants donnés." + }, + { + "nom": "Calculer la durée écoulée entre deux instants donnés.", + "niveau": "CM1" + }, + { + "nom": "Déterminer un instant à partir de la connaissance d'un instant et d'une durée." + }, + { + "nom": "Déterminer un instant à partir de la connaissance d'un instant et d'une durée.", + "niveau": "CM2" + }, + { + "nom": "Connaître et utiliser les unités de mesure des durées et leurs relations : jour, semaine, heure, minute, seconde, dixième de seconde, mois, année, siècle, millénaire." + }, + { + "nom": "Consolider la lecture de l'heure.", + "niveau": "CM2" + }, + { + "nom": "Utiliser les unités de mesure des durées et leurs relations.", + "niveau": "CM2" + }, + { + "nom": "Réaliser des conversions :- siècle/années- semaine/jours- heure/minutes- minute/secondes.", + "niveau": "CM1" + }, + { + "nom": "Connaitre les unités de mesures usuelles : jour, semaine, heure, minute, seconde, dixième de seconde, mois, année, siècle, millénaire.", + "niveau": "CM2" + }, + { + "nom": "Réaliser des conversions nécessitant l'interprétation d'un reste : transformer des heures en jours, avec un reste en heures ou des secondes en minutes, avec un reste en secondes.", + "niveau": "CM2" + }, + { + "nom": "Résoudre des problèmes en exploitant des ressources variées (horaires de transport, horaires de marées, programmes de cinéma ou de télévision, etc.)." + }, + { + "nom": "Réinvestir les unités de mesures dans la résolution de problèmes de deux types : calcul d'une durée à partir de la donnée de l'instant initial et de l'instant final et détermination d'un instant à partir de la donnée d'un instant et d'une durée.", + "niveau": "CM2" + }, + { + "nom": "Résoudre des problèmes en exploitant des ressources variées (horaires de transport, horaires de marées, programme de cinéma ou de télévision...).", + "niveau": "CM1" + }, + { + "nom": "Identifier une situation de proportionnalité entre deux grandeurs à partir du sens de la situation." + }, + { + "nom": "Identifier une situation de proportionnalité entre deux grandeurs à partir du sens de la situation.", + "niveau": "CM1" + }, + { + "nom": "Identifier une situation de proportionnalité entre deux grandeurs à partir du sens de la situation. Des situations simples impliquant des échelles et des vitesses constantes peuvent être rencontrées.", + "niveau": "CM2" + }, + { + "nom": "Résoudre un problème de proportionnalité impliquant des grandeurs." + } + ] + }, + { + "nom": "Espace et géométrie", + "competences": [ + { + "nom": "(Se) repérer et (se) déplacer dans l'espace en utilisant ou en élaborant des représentations.", + "fin_cycle": true + }, + { + "nom": "Reconnaître, nommer, décrire, reproduire, représenter, construire des figures et solides usuels.", + "fin_cycle": true + }, + { + "nom": "Reconnaître et utiliser quelques relations géométriques (notions d'alignement, d'appartenance, de perpendicularité, de parallélisme, d'égalité de longueurs, d'égalité d'angle, de distance entre deux points, de symétrie, d'agrandissement et de réduction).", + "fin_cycle": true + }, + { + "nom": "Se repérer, décrire ou exécuter des déplacements, sur un plan ou sur une carte (école, quartier, ville, village)." + }, + { + "nom": "Se repérer, décrire ou exécuter des déplacements, sur un plan ou sur une carte (école, quartier, ville, village).", + "niveau": "CM2" + }, + { + "nom": "Accomplir, décrire, coder des déplacements dans des espaces familiers." + }, + { + "nom": "Accomplir, décrire, coder des déplacements dans des espaces familiers.", + "niveau": "CM2" + }, + { + "nom": "Programmer les déplacements d'un robot ou ceux d'un personnage sur un écran en utilisant un logiciel de programmation.- vocabulaire permettant de définir des positions et des déplacements (tourner à gauche, à droite ; faire demi-tour, effectuer un quart de tour à droite, à gauche) ;- divers modes de représentation de l'espace : maquettes, plans, schémas." + }, + { + "nom": "Programmer les déplacements d'un robot ou ceux d'un personnage sur un écran.", + "niveau": "CM1" + }, + { + "nom": "Connaitre et utiliser le vocabulaire permettant de définir des positions et des déplacements- tourner à gauche, à droite,- faire demi-tour,- effectuer un quart de tour à droite, à gauche.", + "niveau": "CM2" + }, + { + "nom": "Réaliser divers modes de représentation de l'espace : maquettes, plans, schémas.", + "niveau": "CM2" + }, + { + "nom": "Reconnaître, nommer, décrire des figures simples ou complexes (assemblages de figures simples) :- triangles, dont les triangles particuliers (triangle rectangle, triangle isocèle, triangle équilatéral) ;- quadrilatères, dont les quadrilatères particuliers (carré, rectangle, losange, première approche du parallélogramme) ;- cercle (comme ensemble des points situés à une distance donnée d'un point donné), disque." + }, + { + "nom": "Reconnaitre, nommer, décrire des figures simples ou complexes (assemblages de figures simples) :- triangles dont les triangles particuliers (triangle rectangle, triangle isocèle, triangle équilatéral)- quadrilatères dont les quadrilatères particuliers (carré, rectangle, losange, première approche du parallélogramme)- cercle (comme ensemble des points situés à une distance donnée d'un point donné), disque.", + "niveau": "CM2" + }, + { + "nom": "Reconnaître, nommer, décrire des solides simples ou des assemblages de solides simples : cube, pavé droit, prisme droit, pyramide, cylindre, cône, boule- vocabulaire associé à ces objets et à leurs propriétés : côté, sommet, angle, diagonale, polygone, centre, rayon, diamètre, milieu, hauteur solide, face, arête." + }, + { + "nom": "Reconnaitre, nommer, décrire des solides simples ou des assemblages de solides simples : cube, pavé droit, prisme droit, pyramide, cylindre, cône, boule.", + "niveau": "CM1" + }, + { + "nom": "Connaitre le vocabulaire associé aux objets et aux propriétés : côté, sommet, angle, diagonale, polygone, centre, rayon, diamètre, milieu, hauteur, solide, face, arête.", + "niveau": "CM2" + }, + { + "nom": "Reproduire, représenter, construire :- des figures simples ou complexes (assemblages de figures simples) ;- des solides simples ou des assemblages de solides simples sous forme de maquettes ou de dessins ou à partir d'un patron (donné, dans le cas d'un prisme ou d'une pyramide, ou à construire dans le cas d'un pavé droit)." + }, + { + "nom": "Reproduire, représenter, construire des figures simples ou complexes (assemblages de figures simples).", + "niveau": "CM1" + }, + { + "nom": "Tracer un cercle de rayon donné.", + "niveau": "CM1" + }, + { + "nom": "Reproduire, représenter, construire des solides simples ou des assemblages de solides simples sous forme de maquettes ou de dessins ou à partir d'un patron (donné, dans le cas d'un prisme ou d'une pyramide, ou à construire dans le cas d'un pavé droit, d'un cube).", + "niveau": "CM1" + }, + { + "nom": "Construire, pour un cube de dimension donnée, des patrons différents.", + "niveau": "CM2" + }, + { + "nom": "Reconnaitre, parmi un ensemble de patrons et de faux patrons donnés, ceux qui correspondent à un solide donné : cube, pavé droit, pyramide.", + "niveau": "CM2" + }, + { + "nom": "Réaliser, compléter et rédiger un programme de construction d'une figure plane." + }, + { + "nom": "Réaliser, compléter et rédiger un programme de construction.", + "niveau": "CM2" + }, + { + "nom": "Réaliser une figure plane simple ou une figure composée de figures simples à l'aide d'un logiciel de géométrie dynamique." + }, + { + "nom": "Réaliser une figure simple ou une figure composée de figures simples à l'aide d'un logiciel.", + "niveau": "CM2" + }, + { + "nom": "Tracer avec l'équerre la droite perpendiculaire à une droite donnée passant par un point donné." + }, + { + "nom": "Tracer avec l'équerre la droite perpendiculaire à une droite donnée passant par un point donné qui peut être extérieur à la droite.", + "niveau": "CM2" + }, + { + "nom": "Tracer avec la règle et l'équerre la droite parallèle à une droite donnée passant par un point donné." + }, + { + "nom": "Tracer avec la règle et l'équerre la droite parallèle à une droite donnée passant par un point donné.", + "niveau": "CM2" + }, + { + "nom": "Déterminer le plus court chemin entre un point et une droite.- Alignement, appartenance.- Perpendicularité, parallélisme.- Segment de droite.- Distance entre deux points, entre un point et une droite." + }, + { + "nom": "Connaitre les notions d'alignement/appartenance, de perpendicularité/parallélisme, de segment de droite, de distance entre deux points, entre un point et une droite.", + "niveau": "CM2" + }, + { + "nom": "Déterminer le plus court chemin entre deux points, entre un point et une droite.", + "niveau": "CM2" + }, + { + "nom": "Tracer un carré, un rectangle ou un triangle rectangle de dimensions données.", + "niveau": "CM1" + }, + { + "nom": "Compléter une figure par symétrie axiale." + }, + { + "nom": "Reconnaitre si une figure présente un axe de symétrie : on conjecture visuellement l'axe à trouver et on valide cette conjecture en utilisant du papier calque, des découpages, des pliages.", + "niveau": "CM1" + }, + { + "nom": "Compléter une figure par symétrie axiale.", + "niveau": "CM1" + }, + { + "nom": "Observer que deux points sont symétriques par rapport à une droite donnée lorsque le segment qui les joint coupe cette droite perpendiculairement en son milieu.", + "niveau": "CM2" + }, + { + "nom": "Construire le symétrique d'un point, d'un segment, d'une droite par rapport à un axe donné." + }, + { + "nom": "Construire le symétrique d'une droite, d'un segment, d'un point par rapport à un axe donné.", + "niveau": "CM1" + }, + { + "nom": "Construire, à l'équerre et à la règle graduée, le symétrique par rapport à une droite d'un point, d'un segment, d'une figure.", + "niveau": "CM2" + }, + { + "nom": "Construire la figure symétrique d'une figure donnée par rapport à un axe donné :- figure symétrique, axe de symétrie d'une figure, figures symétriques par rapport à un axe ;- propriétés de conservation de la symétrie axiale ;- médiatrice d'un segment : définition (droite perpendiculaire au segment en son milieu) et caractérisation (ensemble des points équidistants des extrémités du segment)." + }, + { + "nom": "Construire la figure symétrique d'une figure donnée par rapport à un axe donné que l'axe de symétrie coupe ou non la figure.", + "niveau": "CM1" + }, + { + "nom": "Reproduire une figure en respectant une échelle donnée :- agrandissement ou réduction d'une figure." + } + ] + } + ] + }, + { + "nom": "Enseignement moral et civique 2020", + "categories": [] + }, + { + "nom": "Enseignement moral et civique 2024", + "categories": [ + { + "nom": "Civisme et citoyenneté", + "competences": [ + { + "nom": "Définir le civisme comme l'action d'un individu en fonction du bien public et dans le respect des règles.", + "niveau": "CM1" + }, + { + "nom": "Aborder des exemples de comportement civique dans la classe, l'école, dans la vie quotidienne, en ligne, et en faveur de l'environnement.", + "niveau": "CM1" + }, + { + "nom": "Connaître et appliquer les règles de civilité en société ; identifier les incivilités et comprendre pourquoi elles nuisent à la vie en commun.", + "niveau": "CM1" + }, + { + "nom": "Apprendre la signification du terme « démocratie » et le fonctionnement du suffrage direct.", + "niveau": "CM1" + } + ] + }, + { + "nom": "L'égalité dans la dignité", + "competences": [ + { + "nom": "Comprendre la notion d'égalité en droit.", + "niveau": "CM1" + }, + { + "nom": "Comprendre ce qu'implique le principe de dignité de la personne humaine.", + "niveau": "CM1" + } + ] + }, + { + "nom": "Comment faire société", + "competences": [ + { + "nom": "Comprendre la notion de fraternité, valeur et principe de la République.", + "niveau": "CM1" + }, + { + "nom": "Comprendre ce qu'implique et permet l'empathie.", + "niveau": "CM1" + } + ] + }, + { + "nom": "Citoyenneté et nationalité", + "competences": [ + { + "nom": "Connaître les conditions d'acquisition de la nationalité française : montrer le lien étroit entre citoyenneté et nationalité. Un citoyen bénéficie de droits civils, puis politiques à sa majorité, et tout individu bénéficie de droits civils.", + "niveau": "CM2" + }, + { + "nom": "Connaître le rôle politique des citoyennes et des citoyens : ils ont vocation à participer à la vie politique du pays et à l'évolution des institutions (découverte des procédures générales d'élaboration de la loi).", + "niveau": "CM2" + }, + { + "nom": "Connaître les devoirs du citoyen et de toute personne résidant sur le territoire national : respecter les lois, contribuer à financer les dépenses publiques. Chaque citoyen doit respecter les droits des autres qui sont identiques aux siens (Déclaration des droits de l'homme et du citoyen, art. 3 et 13).", + "niveau": "CM2" + }, + { + "nom": "Comprendre que le terme de devoir peut aussi désigner une réalité plus morale, qui doit guider le citoyen dans son comportement dans l'espace public.", + "niveau": "CM2" + }, + { + "nom": "Connaître et comprendre les symboles républicains mentionnés par la Constitution : drapeau, hymne, devise, et d'autres coutumiers comme Marianne ; connaître la fête nationale du 14 juillet (héritière de la Fête de la Fédération de 1790); comprendre la nécessité de respecter ces symboles.", + "niveau": "CM2" + }, + { + "nom": "Savoir que la République française est membre de l'Union européenne (UE).", + "niveau": "CM2" + } + ] + }, + { + "nom": "Libertés et droits fondamentaux", + "competences": [ + { + "nom": "Faire connaître les droits et libertés fondamentaux institués par la Déclaration des droits de l'homme et du citoyen (1789), la Déclaration universelle des droits de l'homme (1948) et la Charte des droits fondamentaux de l'Union européenne (2000).", + "niveau": "CM2" + }, + { + "nom": "Mettre en avant certains droits politiques, économiques et sociaux qui en découlent : suffrage universel, droit à l'emploi, à la protection de la santé, à la gratuité de l'enseignement public, accès à la culture, droits environnementaux (Charte de l'environnement).", + "niveau": "CM2" + }, + { + "nom": "Montrer que les droits fondamentaux s'exercent dans le cadre de la loi (exemple de la liberté d'expression).", + "niveau": "CM2" + }, + { + "nom": "Approfondir avec les droits dits « de troisième génération », qui résultent du droit de chacun de « vivre dans un environnement équilibré et respectueux de la santé » (art. 1er de la Charte de l'environnement).", + "niveau": "CM2" + } + ] + }, + { + "nom": "Respecter les droits de tous", + "competences": [ + { + "nom": "Montrer que la lutte contre les discriminations suppose la déconstruction des préjugés et des stéréotypes.", + "niveau": "CM2" + }, + { + "nom": "Faire reconnaitre les atteintes aux personnes : le racisme, l'antisémitisme, le sexisme, la xénophobie, l'homophobie, le harcèlement ; savoir que l'expression des discriminations est sanctionnée par la loi.", + "niveau": "CM2" + } + ] + }, + { + "nom": "À l'école laïque", + "competences": [ + { + "nom": "Comprendre que le respect des croyances est assuré, mais que, comme ailleurs, leur expression est limitée par la loi. Celle-ci protège les élèves de toute influence religieuse et préserve leur liberté de conscience.", + "niveau": "CM2" + }, + { + "nom": "Savoir que nul ne peut être discriminé pour sa croyance ou ses convictions, mais nul n'a non plus le droit d'imposer ses croyances ou ses convictions aux autres.", + "niveau": "CM2" + } + ] + }, + { + "nom": "Représenter les autres et servir l'intérêt général", + "competences": [ + { + "nom": "Savoir que les représentantes et représentants, choisis par un vote, portent la parole des autres, participent à des délibérations collectives et à la prise de décision.", + "niveau": "6ème" + }, + { + "nom": "Savoir qu'à toutes les échelles (classe, collège, commune, département, région, pays, Union européenne), ils répondent à des besoins collectifs : éducation, santé, secours, transport, enjeux du développement durable et de la transition écologique.", + "niveau": "6ème" + }, + { + "nom": "Savoir que l'intérêt général est l'intérêt commun de tous les membres de la société et qu'il n'est pas toujours compatible avec les intérêts de chacun.", + "niveau": "6ème" + }, + { + "nom": "Dans une perspective de développement durable, comprendre que la définition de l'intérêt général prend en compte les générations futures.", + "niveau": "6ème" + }, + { + "nom": "Savoir que les représentants élus sont responsables : ils expriment la parole des électeurs et suivent les règles des assemblées et des conseils dans lesquels ils sont élus.", + "niveau": "6ème" + } + ] + }, + { + "nom": "Respecter des règles et en comprendre la finalité : l'exemple de la laïcité à l'École", + "competences": [ + { + "nom": "Comprendre que la laïcité garantit la liberté de conscience et l'égalité de toutes les citoyennes et tous les citoyens, quelles que soient leurs croyances ou opinions ; Avoir connaissance de la neutralité de l'État à l'égard des religions et du libre exercice des cultes (loi de 1905).", + "niveau": "6ème" + }, + { + "nom": "Savoir que la liberté de conscience est celle de croire, celle de ne pas croire, celle aussi de changer de croyanceoude religion.", + "niveau": "6ème" + }, + { + "nom": "Comprendre que la laïcité est un principe juridique et non une opinion, et qu'elle diffère par conséquent de l'athéisme ou de l'agnosticisme, qui constituent des options philosophiques personnelles.", + "niveau": "6ème" + }, + { + "nom": "Savoir que la laïcité à l'école protège la liberté de choix de chaque enfant : elle crée un espace neutre à l'abri des prosélytismes (loi du 15 mars 2004 ; Charte de la laïcité).", + "niveau": "6ème" + }, + { + "nom": "Comprendre que dans ce but, la laïcité impose des règles à tous les membres de la communauté scolaire, qu'elle prépare les élèves à vivre dans une communauté nationale où différentes opinions philosophiques et religieuses peuvent s'exprimer et être discutées dans le cadre de la loi.", + "niveau": "6ème" + } + ] + }, + { + "nom": "Avoir des droits en tant que personne et respecter ceux des autres : l'exemple du droit à la vie privée", + "competences": [ + { + "nom": "Savoir que l'enfant comme l'adulte a droit au respect de sa vie privée (CIDE, DDHC et Charte des droits fondamentaux de l'Union européenne).", + "niveau": "6ème" + }, + { + "nom": "Savoir que le droit au respect de la vie privée comprend le droit à l'intimité et la protection du droit à l'image.", + "niveau": "6ème" + }, + { + "nom": "Savoir que l'intimité d'une personne recouvre la vie affective et sexuelle de cette personne.", + "niveau": "6ème" + }, + { + "nom": "Comprendre que ce droit doit être également respecté dans l'univers numérique et les réseaux sociaux (majorité numérique, données personnelles, traces numériques, réputation numérique).", + "niveau": "6ème" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/Back-End/competences/Cycle4.json b/Back-End/competences/Cycle4.json new file mode 100644 index 0000000..14d86d1 --- /dev/null +++ b/Back-End/competences/Cycle4.json @@ -0,0 +1,1585 @@ +{ + "domaines": [ + { + "nom": "Français", + "categories": [ + { + "nom": "Langage oral", + "competences": [ + { + "nom": "Comprendre et interpréter des discours oraux élaborés (récit, exposé magistral, émission documentaire, journal d'information, …).", + "fin_cycle": true + }, + { + "nom": "Élaborer et prononcer une intervention orale continue de cinq à dix minutes (présentation d'une œuvre littéraire ou artistique, exposé des résultats d'une recherche, défense argumentée d'un point de vue).", + "fin_cycle": true + }, + { + "nom": "Participer à un débat de manière constructive et en respectant la parole de l'autre.", + "fin_cycle": true + }, + { + "nom": "Lire un texte à haute voix de manière claire et intelligible ; dire de mémoire un texte littéraire ; s'engager dans un jeu théâtral.", + "fin_cycle": true + }, + { + "nom": "Identifier les visées d'un discours oral." + }, + { + "nom": "Distinguer explicite et implicite." + }, + { + "nom": "Savoir présenter un compte rendu à l'oral." + }, + { + "nom": "Savoir faire partager son point de vue sur une lecture, une œuvre, une situation." + }, + { + "nom": "Savoir utiliser des supports écrits pour l'expression orale." + }, + { + "nom": "Savoir raconter une histoire." + }, + { + "nom": "Savoir utiliser les ressources de la voix, de la respiration, du regard, de la gestuelle, pour lire." + }, + { + "nom": "Savoir utiliser les ressources de la voix, de la respiration, du regard, de la gestuelle, pour dire de mémoire." + }, + { + "nom": "Savoir utiliser les ressources de la voix, de la respiration, du regard, de la gestuelle, pour interpréter une scène de théâtre, un poème, …." + }, + { + "nom": "Savoir utiliser les ressources de la voix, de la respiration, du regard, de la gestuelle, pour donner du relief à sa propre parole lors d'une prestation orale." + } + ] + }, + { + "nom": "Lecture et compréhension de l'écrit et de l'image", + "competences": [ + { + "nom": "Lire et comprendre en autonomie des textes variés, des images et des documents composites, sur différents supports (papier, numérique).", + "fin_cycle": true + }, + { + "nom": "Lire, comprendre et interpréter des textes littéraires en fondant l'interprétation sur quelques outils d'analyse simples.", + "fin_cycle": true + }, + { + "nom": "Situer les textes littéraires dans leur contexte historique et culturel.", + "fin_cycle": true + }, + { + "nom": "Lire une œuvre complète et rendre compte oralement de sa lecture.", + "fin_cycle": true + }, + { + "nom": "Lire et comprendre, pour chaque niveau du cycle, au moins trois œuvres complètes du patrimoine étudiées en classe, trois œuvres complètes, notamment de littérature de jeunesse, en lecture cursive, et trois groupements de textes (lecture analytique ou cursive).", + "fin_cycle": true + }, + { + "nom": "Vérifier sa compréhension de l'écrit de façon autonome." + }, + { + "nom": "Être capable de justifier son interprétation en s'appuyant précisément sur le texte." + }, + { + "nom": "Être capable d'adapter sa lecture à l'objectif affiché." + }, + { + "nom": "Savoir choisir un livre adapté à son niveau de lecture, ses goûts et ses besoins." + }, + { + "nom": "Connaître les caractéristiques génériques des différents documents étudiés (articles de presse d'information et scientifique, essais, textes documentaires, schémas, graphiques, tableaux, images fixes et mobiles, …)." + }, + { + "nom": "Savoir décrire et analyser l'image fixe et mobile." + }, + { + "nom": "Lire des œuvres appartenant à différents genres littéraires." + }, + { + "nom": "Lire des textes appartenant à différentes époques, en lien avec le programme d'histoire." + }, + { + "nom": "Être capable de relier œuvre littéraire et œuvre artistique." + }, + { + "nom": "Connaître les caractéristiques majeures de l'esthétique des genres." + }, + { + "nom": "Être capable de situer les œuvres lues dans leur époque, leur contexte de création." + }, + { + "nom": "Avoir des repères d'histoire littéraire et culturelle, en lien avec le programme d'histoire." + }, + { + "nom": "Percevoir les effets esthétiques et significatifs de la langue littéraire." + }, + { + "nom": "Être capable d'en analyser les sources : notions d'analyse littéraire, de procédés stylistiques." + } + ] + }, + { + "nom": "Écriture", + "competences": [ + { + "nom": "Communiquer par écrit et sur des supports variés (papier, numérique) un sentiment, un point de vue, un jugement argumenté en tenant compte du destinataire et en respectant les principales normes de la langue écrite.", + "fin_cycle": true + }, + { + "nom": "Formuler par écrit sa réception d'une œuvre littéraire ou artistique.", + "fin_cycle": true + }, + { + "nom": "Rédiger, en réponse à une consigne d'écriture, un écrit d'invention s'inscrivant dans un genre littéraire du programme, en s'assurant de sa cohérence et en respectant les principales normes de la langue écrite.", + "fin_cycle": true + }, + { + "nom": "Utiliser l'écrit pour réfléchir, se donner des outils de travail.", + "fin_cycle": true + }, + { + "nom": "Comprendre le rôle historique et social de l'écriture." + }, + { + "nom": "Utiliser l'écrit pour penser et pour apprendre." + }, + { + "nom": "Recourir régulièrement aux écrits de travail pour préparer des travaux, donner forme à une réflexion, classer, résumer etc." + }, + { + "nom": "Recourir régulièrement aux écrits réflexifs pour expliquer une démarche, justifier une réponse, un propos." + }, + { + "nom": "Connaître les techniques et les usages de la prise de notes." + }, + { + "nom": "Acquérir et mettre en œuvre une démarche d'écriture (qui doit devenir progressivement autonome)." + }, + { + "nom": "Prendre en compte le destinataire, les visées du texte, les caractéristiques de son genre et le support d'écriture dès la préparation de l'écrit et jusqu'à la relecture ultime." + }, + { + "nom": "Mettre en œuvre des stratégies permettant de trouver des idées ou des éléments du texte à écrire." + }, + { + "nom": "Organiser l'écrit en fonction des règles propres au genre du texte à rédiger et à son support : connaissance des caractéristiques des genres littéraires pour composer librement des écrits, en intégrant éventuellement différents supports." + }, + { + "nom": "Respecter les normes de l'écrit dès la première phase d'écriture (brouillon) : normes qui assurent l'unité et la cohérence du texte, mais aussi normes linguistiques." + }, + { + "nom": "Vérifier et améliorer la qualité de son texte (être capable de mettre à distance son texte pour l'évaluer et le faire évoluer), en cours d'écriture, lors de la relecture et a posteriori." + }, + { + "nom": "Être conscient de ses fragilités et apprendre à identifier des zones d'erreurs possibles de manière autonome afin de faciliter la révision." + }, + { + "nom": "Prendre en compte les normes de l'écrit pour réviser son texte : cohérence, cohésion (syntaxe, énonciation, éléments sémantiques qui assurent l'unité du texte) et normes linguistiques." + }, + { + "nom": "Savoir recourir à la modalisation." + }, + { + "nom": "Connaître les principaux genres littéraires et leurs caractéristiques." + }, + { + "nom": "Être capable de transférer dans ses propres écrits le lexique, les tournures syntaxiques découverts lors de lectures." + }, + { + "nom": "Utiliser des outils d'analyse des textes." + }, + { + "nom": "Être capable d'adapter sa lecture à l'objectif affiché." + }, + { + "nom": "Savoir choisir un livre adapté à son niveau de lecture, à ses goûts et à ses besoins." + }, + { + "nom": "Connaître les principales fonctions et caractéristiques des discours argumentatifs." + }, + { + "nom": "Repérer et identifier des procédés destinés à étayer une argumentation (organisation du propos, choix des exemples, modalisation)." + }, + { + "nom": "Être capable de structurer clairement un texte argumentatif et de l'illustrer." + } + ] + }, + { + "nom": "Étude de la langue (grammaire, orthographe, lexique)", + "competences": [ + { + "nom": "Mobiliser les connaissances orthographiques, syntaxiques et lexicales en expression écrite et orale ainsi qu'en révision de texte, dans des contextes variés.", + "fin_cycle": true + }, + { + "nom": "Être capable d'analyser les principaux constituants d'une phrase simple et complexe.", + "fin_cycle": true + }, + { + "nom": "Être capable d'orthographier les mots d'usage courant, de conjuguer correctement les verbes, de pratiquer les accords dans le groupe verbal et dans le groupe nominal.", + "fin_cycle": true + }, + { + "nom": "Comprendre que la syntaxe de l'oral est différente de celle de l'écrit." + }, + { + "nom": "Être capable de transposer des énoncés oraux à l'écrit et inversement." + }, + { + "nom": "Être capable d'insérer dans un texte des paroles rapportées : discours direct, indirect." + }, + { + "nom": "Être capable d'identifier le discours indirect libre." + }, + { + "nom": "Connaître les incidences de l'écrit sur l'oral (liaison) et de l'oral sur l'écrit (élision)." + }, + { + "nom": "Mesurer les écarts de niveau de langue entre l'oral et l'écrit." + }, + { + "nom": "Être capable de recourir, dans le cadre de l'oral scolaire, à des éléments lexicaux de niveau soutenu." + }, + { + "nom": "Distinguer les principaux constituants de la phrase et les hiérarchiser." + }, + { + "nom": "Identifier et analyser les constituants de la phrase simple." + }, + { + "nom": "Être capable de reconnaître le sujet même dans les cas complexes (sujet éloigné)." + }, + { + "nom": "Approfondir sa connaissance du COD et du COI." + }, + { + "nom": "Identifier tous les compléments circonstanciels (NB : temps, lieu et cause abordés au cycle 3)." + }, + { + "nom": "Analyser la phrase impersonnelle." + }, + { + "nom": "Élargir ses connaissances des fonctions grammaticales." + }, + { + "nom": "Identifier l'attribut du COD." + }, + { + "nom": "Identifier les expansions du nom déjà abordées au cycle 3 (épithète, complément du nom)." + }, + { + "nom": "Identifier l'apposition." + }, + { + "nom": "Identifier les classes de mots." + }, + { + "nom": "Identifier les classes de mots abordées aux cycles précédents ainsi que les classes de mots : nom, verbe, adjectif et ses degrés (comparatif et superlatif), article défini, article indéfini, déterminant possessif, déterminant démonstratif, pronom personnel sujet et objet, groupe nominal, adverbe, préposition, conjonction de coordination et de subordination, groupe nominal." + }, + { + "nom": "Différencier déterminant (article défini, indéfini, partitif, déterminant possessif, interrogatif, indéfini, exclamatif, numéral), adjectif et ses degrés (comparatif et superlatif) et pronom (personnel, possessif, démonstratif, indéfini, interrogatif, relatif, adverbial)." + }, + { + "nom": "Identifier les types (déclaratif, interrogatif, impératif) et les formes (négative, passive, exclamative, impersonnelle) de phrase." + }, + { + "nom": "Distinguer phrase simple / complexe." + }, + { + "nom": "Identifier les constituants de la phrase complexe (par analogie avec les constituants de la phrase simple)." + }, + { + "nom": "Connaître les notions de juxtaposition, coordination, subordination." + }, + { + "nom": "Analyser les positions des propositions subordonnées (conjonctive, interrogative indirecte, relative, infinitive, participiale) et leurs relations avec les autres constituants de la phrase." + }, + { + "nom": "Comprendre la fonction grammaticale des propositions subordonnées dans la phrase." + }, + { + "nom": "Comprendre le fonctionnement de la proposition subordonnée relative et identifier la fonction du pronom relatif dans la subordonnée." + }, + { + "nom": "Analyser le rôle syntaxique des signes de ponctuation et utiliser ces signes à bon escient." + }, + { + "nom": "Connaître le fonctionnement des chaînes d'accord." + }, + { + "nom": "Maîtriser l'accord dans le groupe nominal complexe (avec plusieurs noms, plusieurs adjectifs, une relative, des déterminants comme tout, chaque, leur, ...)." + }, + { + "nom": "Maîtriser l'accord du participe passé avec être (à rapprocher de l'adjectif) et avec avoir (cas du COD antéposé) - cas simples." + }, + { + "nom": "Maîtriser l'accord de l'adjectif et du participe passé mis en apposition." + }, + { + "nom": "Maîtriser l'accord du verbe dans les cas complexes (sujet éloigné du verbe, avec plusieurs noms, avec plusieurs personnes, pronom relatif, collectif ou distributif, indiquant une quantité, présence d'un pronom ou d'un autre groupe syntaxique entre le donneur d'accord et le verbe, ...)." + }, + { + "nom": "Maîtriser la morphologie verbale écrite en appui sur les régularités et la décomposition du verbe (radical, terminaison qui comporte les marques de mode / temps, marques de personne pour les modes personnels)." + }, + { + "nom": "Connaître les verbes pronominaux." + }, + { + "nom": "Identifier les principaux temps et modes (personnels et non personnels) ." + }, + { + "nom": "Former les temps simples : systématiser les règles de construction des formes verbales aux différents temps simples (temps de l'indicatif, impératif présent, subjonctif présent, conditionnel présent) à partir de la connaissance des bases verbales." + }, + { + "nom": "Construire les temps composés ; connaître les formes du participe passé des verbes (é, i, u et formes avec consonne finale)." + }, + { + "nom": "Construire le passif et analyser ses effets de sens." + }, + { + "nom": "Mémoriser le présent, l'imparfait, le futur, le passé simple, le passé composé, le plus-que-parfait, le futur antérieur de l'indicatif, le présent et le passé du conditionnel, l'impératif présent, le présent et le passé du subjonctif à toutes les personnes pour être et avoir." + }, + { + "nom": "Mémoriser le présent, l'imparfait, le futur, le passé simple, le passé composé, le plus-que-parfait, le futur antérieur de l'indicatif, le présent et le passé du conditionnel, l'impératif présent, le présent et le passé du subjonctif à toutes les personnes pour les verbes des trois groupes." + }, + { + "nom": "Mémoriser le présent, l'imparfait, le futur, le passé simple, le passé composé, le plus-que-parfait, le futur antérieur de l'indicatif, le présent et le passé du conditionnel, l'impératif présent, le présent et le passé du subjonctif à toutes les personnes pour les verbes irréguliers du 3ème groupe : faire, aller, dire, venir, pouvoir, voir, vouloir, prendre, savoir, falloir, valoir." + }, + { + "nom": "Mettre en évidence le lien entre le temps employé et le sens." + }, + { + "nom": "Être initié à la valeur des temps à partir d'observations et de comparaisons : opposition entre temps simples et temps composés (non accompli/ accompli); opposition entre temps qui embrassent ou non l'action dans sa totalité (borné/non borné : elle lut une page/elle lisait une page)." + }, + { + "nom": "Observer l'incidence de la valeur des temps sur leurs emplois (premier plan / arrière-plan)." + }, + { + "nom": "Connaître les principaux emplois des différents modes." + }, + { + "nom": "Mémoriser l'orthographe des affixes (préfixes, suffixes) et de leur effet éventuel sur le radical." + }, + { + "nom": "Utiliser sa connaissance de l'étymologie pour orthographier les mots ayant la même racine." + }, + { + "nom": "Mémoriser l'orthographe du lexique appris." + }, + { + "nom": "Observer la formation, les analogies, les régularités et construire des réflexes orthographiques." + }, + { + "nom": "Enrichir son lexique par les lectures, en lien avec les entrées du programme de culture littéraire et artistique, par l'écriture, par les diverses activités conduites dans toutes les disciplines." + }, + { + "nom": "Enrichir son lexique par l'usage du dictionnaire ou autres outils en version papier ou numérique." + }, + { + "nom": "Savoir réutiliser à bon escient, à l'écrit et à l'oral, le lexique appris." + }, + { + "nom": "Observer la formation des mots : dérivation et composition, étymologie et néologie, graphie des mots, notamment à partir d'éléments latins et grecs ou empruntés aux langues étrangères ; mettre en évidence les changements de catégorie syntaxique induits par la dérivation (déménager / déménagement ; beau / beauté, ...) et de leurs incidences orthographiques." + }, + { + "nom": "Connaître le sens des préfixes et suffixes les plus fréquents et de certaines racines latines et grecques." + }, + { + "nom": "Mettre en réseau des mots (groupements par champ lexical et par champ sémantique) et maîtriser leur classement par degré d'intensité et de généralité." + }, + { + "nom": "Analyser le sens des mots : polysémie et synonymie, antonymie et homonymie, nuances et glissements de sens, locutions, construction des verbes et variations de sens, dénotation, connotation et niveaux de langue." + }, + { + "nom": "Utiliser différents types de dictionnaires et d'outils numériques." + }, + { + "nom": "Observer les variations de la langue en fonction des enjeux de la communication." + }, + { + "nom": "Repérer ce qui détermine un niveau de langue (situation de communication, enjeu, ...), et ce qui le caractérise (organisation du propos, lexique, syntaxe) à partir de quelques exemples contrastés." + }, + { + "nom": "Observer la variation à travers le repérage de différentes manières d'exprimer une même idée ou une idée nouvelle : évolution du sens des mots selon les époques, néologie, emprunts ; variation en fonction du lieu, du contexte, du moyen de communication." + }, + { + "nom": "Prendre en compte les caractéristiques des textes lus ou à rédiger." + }, + { + "nom": "Identifier et interpréter les éléments de la situation d'énonciation : qui parle à qui ? où ? quand ? (marques de personne, de lieu et de temps) ; prendre en compte la situation d'énonciation dans l'écriture ; repérer et savoir utiliser les phénomènes d'accord en relation avec l'énonciation (je, tu)." + }, + { + "nom": "Reconnaître et utiliser les paroles rapportées, directement ou indirectement." + }, + { + "nom": "Identifier et utiliser des marques d'organisation du texte (mise en page, typographie, ponctuation, connecteurs)." + }, + { + "nom": "Reconnaître des formes à la voix active/passive et leurs valeurs sémantiques ; connaître les permutations pour marquer l'insistance ou l'emphase ; savoir recourir aux présentatifs ; valeur sémantique de la phrase impersonnelle." + } + ] + } + ] + }, + { + "nom": "Langues vivantes étrangères et régionales", + "categories": [] + }, + { + "nom": "Arts plastiques", + "categories": [] + }, + { + "nom": "Éducation musicale", + "categories": [] + }, + { + "nom": "Histoire des arts", + "categories": [] + }, + { + "nom": "Éducation physique et sportive", + "categories": [] + }, + { + "nom": "Histoire et géographie", + "categories": [ + { + "nom": "Histoire", + "competences": [ + { + "nom": "Byzance et l'Europe carolingienne." + }, + { + "nom": "De la naissance de l'islam à la prise de Bagdad par les Mongols : pouvoirs, sociétés, cultures." + }, + { + "nom": "L'ordre seigneurial : la formation et la domination des campagnes." + }, + { + "nom": "L'affirmation de l'État monarchique dans le Royaume des Capétiens et des Valois." + }, + { + "nom": "Le monde au temps de Charles Quint et Soliman le Magnifique." + }, + { + "nom": "Humanisme, réformes et conflits religieux." + }, + { + "nom": "Du Prince de la Renaissance au roi absolu. (François Ier, Henri IV, Louis XIV)" + }, + { + "nom": "Bourgeoisies marchandes, négoces internationaux, traites négrières et esclavage au XVIIIe siècle." + }, + { + "nom": "L'Europe des Lumières : circulation des idées, despotisme éclairé et contestation de l'absolutisme." + }, + { + "nom": "La Révolution française et l'Empire : nouvel ordre politique et société révolutionnée en France et en Europe." + }, + { + "nom": "L'Europe de la «révolution industrielle»." + }, + { + "nom": "Conquêtes et sociétés coloniales." + }, + { + "nom": "Une difficile conquête: voter de 1815 à 1870." + }, + { + "nom": "La Troisième République." + }, + { + "nom": "Conditions féminines dans une société en mutation." + }, + { + "nom": "Civils et militaires dans la Première Guerre mondiale." + }, + { + "nom": "Démocraties fragilisées et expériences totalitaires dans l'Europe de l'entre-deux-guerres." + }, + { + "nom": "La Deuxième Guerre mondiale, une guerre d'anéantissement." + }, + { + "nom": "La France défaite et occupée. Régime de Vichy, collaboration, Résistance." + }, + { + "nom": "Indépendances et construction de nouveaux États." + }, + { + "nom": "Un monde bipolaire au temps de la guerre froide." + }, + { + "nom": "Affirmation et mise en œuvre du projet européen." + }, + { + "nom": "Enjeux et conflits dans le monde après 1989." + }, + { + "nom": "1944-1947 : refonder la République, redéfinir la démocratie." + }, + { + "nom": "La Ve République, de la République gaullienne à l'alternance et à la cohabitation." + }, + { + "nom": "Femmes et hommes dans la société des années 1950 aux années 1980 : nouveaux enjeux sociaux et culturels, réponses politiques." + } + ] + }, + { + "nom": "Géographie", + "competences": [ + { + "nom": "La croissance démographique et ses effets." + }, + { + "nom": "Répartition de la richesse et de la pauvreté dans le monde." + }, + { + "nom": "L'énergie, l'eau: des ressources à ménager et à mieux utiliser." + }, + { + "nom": "L'alimentation : comment nourrir une humanité en croissance démographique et aux besoins alimentaires accrus ?" + }, + { + "nom": "Le changement global et ses principaux effets géographiques régionaux." + }, + { + "nom": "Prévenir les risques industriels et technologiques." + }, + { + "nom": "Espaces et paysages de l'urbanisation: géographie des centres et des périphéries." + }, + { + "nom": "Des villes inégalement connectées aux réseaux de la mondialisation." + }, + { + "nom": "Un monde de migrants." + }, + { + "nom": "Le tourisme et ses espaces." + }, + { + "nom": "Mers et Océans : un monde maritimisé." + }, + { + "nom": "L'adaptation du territoire des États-Unis aux nouvelles conditions de la mondialisation." + }, + { + "nom": "Les dynamiques d'un grand ensemble géographique africain (au choix: Afrique de l'Ouest, Afrique Orientale, Afrique australe)." + }, + { + "nom": "Les aires urbaines, une nouvelle géographie d'une France mondialisée." + }, + { + "nom": "Les espaces productifs et leurs évolutions." + }, + { + "nom": "Les espaces de faible densité (espaces ruraux, montagnes, secteurs touristiques peu urbanisés) et leurs atouts." + }, + { + "nom": "Aménager pour répondre aux inégalités croissantes entre territoires français, à toutes les échelles." + }, + { + "nom": "Les territoires ultra-marins français: une problématique spécifique." + }, + { + "nom": "L'Union européenne, un nouveau territoire de référence et d'appartenance." + }, + { + "nom": "La France et l'Europe dans le monde." + } + ] + } + ] + }, + { + "nom": "Physique-Chimie", + "categories": [ + { + "nom": "Organisation et transformations de la matière.", + "competences": [ + { + "nom": "Décrire la constitution et les états de la matière.", + "fin_cycle": true + }, + { + "nom": "Décrire et expliquer des transformations chimiques.", + "fin_cycle": true + }, + { + "nom": "Décrire l'organisation de la matière dans l'Univers.", + "fin_cycle": true + }, + { + "nom": "Caractériser les différents états de la matière (solide, liquide et gaz)." + }, + { + "nom": "Proposer et mettre en œuvre un protocole expérimental pour étudier les propriétés des changements d'état." + }, + { + "nom": "Caractériser les différents changements d'état d'un corps pur." + }, + { + "nom": "Interpréter les changements d'état au niveau microscopique." + }, + { + "nom": "Proposer et mettre en œuvre un protocole expérimental pour déterminer une masse volumique d'un liquide ou d'un solide." + }, + { + "nom": "Exploiter des mesures de masse volumique pour différencier des espèces chimiques." + }, + { + "nom": "Connaître les notions d'espèce chimique et de mélange." + }, + { + "nom": "Connaître les notion de corps pur." + }, + { + "nom": "Connaître les changements d'états de la matière." + }, + { + "nom": "Connaître les notions de conservation de la masse, de variation du volume, de température de changement d'état." + }, + { + "nom": "Connaître la notion de masse volumique : Relation m = ρ.V." + }, + { + "nom": "Concevoir et réaliser des expériences pour caractériser des mélanges." + }, + { + "nom": "Estimer expérimentalement une valeur de solubilité dans l'eau." + }, + { + "nom": "Connaître la notion de solubilité." + }, + { + "nom": "Connaître la notion de miscibilité." + }, + { + "nom": "Connaître la composition de l'air." + }, + { + "nom": "Mettre en œuvre des tests caractéristiques d'espèces chimiques à partir d'une banque fournie." + }, + { + "nom": "Identifier expérimentalement une transformation chimique." + }, + { + "nom": "Distinguer transformation chimique et mélange, transformation chimique et transformation physique." + }, + { + "nom": "Interpréter une transformation chimique comme une redistribution des atomes." + }, + { + "nom": "Utiliser une équation de réaction chimique fournie pour décrire une transformation chimique observée." + }, + { + "nom": "Connaître les notions de molécules, atomes, ions." + }, + { + "nom": "Prendre conscience de la conservation de la masse lors d'une transformation chimique." + }, + { + "nom": "Associer leurs symboles aux éléments à l'aide de la classification périodique." + }, + { + "nom": "Interpréter une formule chimique en termes atomiques." + }, + { + "nom": "Connaître l'existence des éléments suivants : dioxygène, dihydrogène, diazote, eau, dioxyde de carbone." + }, + { + "nom": "Identifier le caractère acide ou basique d'une solution par mesure de pH." + }, + { + "nom": "Associer le caractère acide ou basique à la présence d'ions H+ et OH-." + }, + { + "nom": "Connaître les éléments suivants : ions H+ et OH-." + }, + { + "nom": "Savoir mesurer du pH." + }, + { + "nom": "Connaître les réactions entre solutions acides et basiques." + }, + { + "nom": "Connaître les réactions entre solutions acides et métaux." + }, + { + "nom": "Décrire la structure de l'Univers et du système solaire." + }, + { + "nom": "Aborder les différentes unités de distance et savoir les convertir : du kilomètre à l'année-lumière." + }, + { + "nom": "Connaître les notions de galaxies, évolution de l'Univers, formation du système solaire, âges géologiques." + }, + { + "nom": "Connaître et utiliser les ordres de grandeur des distances astronomiques." + }, + { + "nom": "Connaitre et comprendre l'origine de la matière." + }, + { + "nom": "Comprendre que la matière observable est partout de même nature et obéit aux mêmes lois." + }, + { + "nom": "Connaître la matière constituant la Terre et les étoiles." + }, + { + "nom": "Connaître l'existence des éléments sur Terre et dans l'univers (hydrogène, hélium, éléments lourds : oxygène, carbone, fer, silicium...)." + }, + { + "nom": "Connaître les notions suivantes : constituants de l'atome, structure interne d'un noyau atomique (nucléons : protons, neutrons), électrons." + } + ] + }, + { + "nom": "Mouvement et interactions", + "competences": [ + { + "nom": "Caractériser un mouvement.", + "fin_cycle": true + }, + { + "nom": "Modéliser une interaction par une force caractérisée par un point d'application, une direction, un sens et une valeur.", + "fin_cycle": true + }, + { + "nom": "Caractériser le mouvement d'un objet." + }, + { + "nom": "Utiliser la relation liant vitesse, distance et durée dans le cas d'un mouvement uniforme." + }, + { + "nom": "Connaître la notion de vitesse : direction, sens et valeur." + }, + { + "nom": "Connaître les notions de mouvements rectilignes et circulaires." + }, + { + "nom": "Connaître l'existence de mouvements uniformes et mouvements dont la vitesse varie au cours du temps en direction ou en valeur." + }, + { + "nom": "Prendre conscience de la relativité du mouvement dans des cas simples." + }, + { + "nom": "Identifier les interactions mises en jeu (de contact ou à distance) et les modéliser par des forces." + }, + { + "nom": "Associer la notion d'interaction à la notion de force." + }, + { + "nom": "Exploiter l'expression littérale scalaire de la loi de gravitation universelle, la loi étant fournie." + }, + { + "nom": "Connaître les notions d'action de contact et action à distance." + }, + { + "nom": "Connaître la notion de force : point d'application, direction, sens et valeur." + }, + { + "nom": "Connaître la notion de force de pesanteur et son expression P=mg." + } + ] + }, + { + "nom": "L'énergie et ses conversions", + "competences": [ + { + "nom": "Identifier les sources, les transferts, les conversions et les formes d'énergie.", + "fin_cycle": true + }, + { + "nom": "Utiliser la conservation de l'énergie.", + "fin_cycle": true + }, + { + "nom": "Réaliser des circuits électriques simples et exploiter les lois de l'électricité.", + "fin_cycle": true + }, + { + "nom": "Identifier les différentes formes d'énergie." + }, + { + "nom": "Connaître les notions d'énergie cinétique (relation Ec = ½ mv2), potentielle (dépendant de la position), thermique, électrique, chimique, nucléaire, lumineuse." + }, + { + "nom": "Identifier les sources, les transferts et les conversions d'énergie." + }, + { + "nom": "Établir un bilan énergétique pour un système simple." + }, + { + "nom": "Connaître les sources d'énergie." + }, + { + "nom": "Connaître la notion de transferts d'énergie." + }, + { + "nom": "Connaître la notion de conversion d'un type d'énergie en un autre." + }, + { + "nom": "Connaître la notion de conservation de l'énergie." + }, + { + "nom": "Connaitre les unités d'énergie." + }, + { + "nom": "Utiliser la relation liant puissance, énergie et durée." + }, + { + "nom": "Connaître et utiliser la notion de puissance." + }, + { + "nom": "Élaborer et mettre en œuvre un protocole expérimental simple visant à réaliser un circuit électrique répondant à un cahier des charges simple ou à vérifier une loi de l'électricité." + }, + { + "nom": "Exploiter les lois de l'électricité." + }, + { + "nom": "Connaître les notions de dipôles en série, dipôles en dérivation." + }, + { + "nom": "Savoir que l'intensité du courant électrique est la même en tout point d'un circuit qui ne compte que des dipôles en série." + }, + { + "nom": "Connaître la loi d'additivité des tensions (circuit à une seule maille)." + }, + { + "nom": "Connaître la loi d'additivité des intensités (circuit à deux mailles)." + }, + { + "nom": "Connaître la relation tension-courant: loi d'Ohm." + }, + { + "nom": "Connaître la loi d'unicité des tensions." + }, + { + "nom": "Mettre en relation les lois de l'électricité et les règles de sécurité dans ce domaine." + }, + { + "nom": "Conduire un calcul de consommation d'énergie électrique relatif à une situation de la vie courante." + }, + { + "nom": "Connaître la notion de puissance électrique P= U.I." + }, + { + "nom": "Connaître la relation liant l'énergie, la puissance électrique et la durée." + } + ] + }, + { + "nom": "Des signaux pour observer et communiquer", + "competences": [ + { + "nom": "Caractériser différents types de signaux (lumineux, sonores, radio...).", + "fin_cycle": true + }, + { + "nom": "Utiliser les propriétés de ces signaux.", + "fin_cycle": true + }, + { + "nom": "Distinguer une source primaire (objet lumineux) d'un objet diffusant." + }, + { + "nom": "Exploiter expérimentalement la propagation rectiligne de la lumière dans le vide et le modèle du rayon lumineux." + }, + { + "nom": "Utiliser l'unité « année-lumière » comme unité de distance." + }, + { + "nom": "Connaître la notion de lumière : sources, propagation, vitesse de propagation, année-lumière." + }, + { + "nom": "Savoir représenter un modèle du rayon lumineux." + }, + { + "nom": "Décrire les conditions de propagation d'un son." + }, + { + "nom": "Relier la distance parcourue par un son à la durée de propagation." + }, + { + "nom": "Connaître la notion de vitesse de propagation." + }, + { + "nom": "Connaître la notion de fréquence : sons audibles, infrasons et ultrasons." + }, + { + "nom": "Comprendre que l'utilisation du son et de la lumière permet d'émettre, de transporter un signal donc une information." + } + ] + } + ] + }, + { + "nom": "Sciences de la vie et de la Terre", + "categories": [] + }, + { + "nom": "Technologie", + "categories": [ + { + "nom": "Design, innovation et créativité", + "competences": [ + { + "nom": "Imaginer des solutions en réponse aux besoins, matérialiser des idées en intégrant une dimension design.", + "fin_cycle": true + }, + { + "nom": "Réaliser, de manière collaborative, le prototype d'un objet communicant.", + "fin_cycle": true + }, + { + "nom": "Identifier un besoin (biens matériels ou services) et énoncer un problème technique ; identifier les conditions, contraintes (normes et règlements) et ressources correspondantes, qualifier et quantifier simplement les performances d'un objet technique existant ou à créer." + }, + { + "nom": "Connaître les notions de besoin, contraintes, normalisation." + }, + { + "nom": "Connaître les principaux éléments d'un cahier des charges." + }, + { + "nom": "Imaginer, synthétiser et formaliser une procédure, un protocole." + }, + { + "nom": "Connaître les outils numériques de présentation." + }, + { + "nom": "Comprendre ce qu'est un charte graphique." + }, + { + "nom": "Participer à l'organisation de projets, la définition des rôles, la planification (se projeter et anticiper) et aux revues de projet." + }, + { + "nom": "Connaître l'organisation d'un groupe de projet, le rôle des participants, le planning, la revue de projets." + }, + { + "nom": "Imaginer des solutions pour produire des objets et des éléments de programmes informatiques en réponse au besoin." + }, + { + "nom": "Connaître la notion de design." + }, + { + "nom": "Connaître les notions d'innovation et de créativité." + }, + { + "nom": "Comprendre l'utilité de la veille." + }, + { + "nom": "Représenter des solutions (croquis, schémas, algorithmes)." + }, + { + "nom": "Comprendre la notion de réalité augmentée." + }, + { + "nom": "Connaître les objets connectés." + }, + { + "nom": "Organiser, structurer et stocker des ressources numériques." + }, + { + "nom": "Connaître la notion d'arborescence." + }, + { + "nom": "Présenter à l'oral et à l'aide de supports numériques multimédia des solutions techniques au moment des revues de projet." + }, + { + "nom": "Connaître et utiliser les outils numériques de présentation." + }, + { + "nom": "Connaître la notion de charte graphique." + }, + { + "nom": "Réaliser, de manière collaborative, le prototype d'un objet pour valider une solution." + }, + { + "nom": "Prototyper rapidement des structures et des circuits de commande à partir de cartes standard." + } + ] + }, + { + "nom": "Les objets techniques, les services et les changements induits dans la société", + "competences": [ + { + "nom": "Comparer et commenter les évolutions des objets et systèmes.", + "fin_cycle": true + }, + { + "nom": "Exprimer sa pensée à l'aide d'outils de description adaptés.", + "fin_cycle": true + }, + { + "nom": "Développer les bonnes pratiques de l'usage des objets communicants.", + "fin_cycle": true + }, + { + "nom": "Regrouper des objets en familles et lignées." + }, + { + "nom": "Connaître l'évolution des objets." + }, + { + "nom": "Connaître les impacts sociétaux et environnementaux dus aux objets." + }, + { + "nom": "Connaître la notion de cycle de vie." + }, + { + "nom": "Connaître et appliquer les règles d'un usage raisonné des objets communicants en respectant la propriété intellectuelle et l'intégrité d'autrui." + }, + { + "nom": "Relier les évolutions technologiques aux inventions et innovations qui marquent des ruptures dans les solutions techniques." + }, + { + "nom": "Comparer et commenter les évolutions des objets en articulant différents points de vue : fonctionnel, structurel, environnemental, technique, scientifique, social, historique, économique." + }, + { + "nom": "Élaborer un document qui synthétise ces comparaisons et ces commentaires." + }, + { + "nom": "Connaître et utiliser les outils numériques de présentation." + }, + { + "nom": "Connaître la notion de charte graphique." + }, + { + "nom": "Exprimer sa pensée à l'aide d'outils de description adaptés : croquis, schémas, graphes, diagrammes, tableaux." + }, + { + "nom": "Savoir faire des croquis à main levée." + }, + { + "nom": "Savoir faire différents schémas." + }, + { + "nom": "Savoir faire une carte heuristique." + }, + { + "nom": "Connaître la notion d'algorithme." + }, + { + "nom": "Lire, utiliser et produire, à l'aide d'outils de représentation numérique, des choix de solutions sous forme de dessins ou de schémas." + }, + { + "nom": "Connaître et utiliser des outils numériques de description des objets techniques." + } + ] + }, + { + "nom": "La modélisation et la simulation des objets et systèmes techniques", + "competences": [ + { + "nom": "Analyser le fonctionnement et la structure d'un objet.", + "fin_cycle": true + }, + { + "nom": "Utiliser une modélisation et simuler le comportement d'un objet.", + "fin_cycle": true + }, + { + "nom": "Respecter une procédure de travail garantissant un résultat en respectant les règles de sécurité et d'utilisation des outils mis à disposition." + }, + { + "nom": "Connaître les notions de procédures, de protocoles." + }, + { + "nom": "Connaître la notion d'ergonomie." + }, + { + "nom": "Associer des solutions techniques à des fonctions." + }, + { + "nom": "Connaître la notion d'analyse fonctionnelle systémique." + }, + { + "nom": "Analyser le fonctionnement et la structure d'un objet, identifier les entrées et sorties." + }, + { + "nom": "Savoir faire une représentation fonctionnelle des systèmes." + }, + { + "nom": "Connaître la notion de structure des systèmes." + }, + { + "nom": "Connaître la notion de chaîne d'énergie." + }, + { + "nom": "Connaître la notion de chaîne d'information." + }, + { + "nom": "Identifier le(s) matériau(x), les flux d'énergie et d'information sur un objet et décrire les transformations qui s'opèrent." + }, + { + "nom": "Connaître les familles de matériaux avec leurs principales caractéristiques." + }, + { + "nom": "Connaître les sources d'énergies." + }, + { + "nom": "Connaître la notion de chaîne d'énergie." + }, + { + "nom": "Connaître la notion de chaîne d'information." + }, + { + "nom": "Décrire, en utilisant les outils et langages de descriptions adaptés, le fonctionnement, la structure et le comportement des objets." + }, + { + "nom": "Connaître et utiliser les outils de description d'un fonctionnement, d'une structure et d'un comportement." + }, + { + "nom": "Mesurer des grandeurs de manière directe ou indirecte." + }, + { + "nom": "Connaître et utiliser les instruments de mesure usuels." + }, + { + "nom": "Connaître le principe de fonctionnement d'un capteur, d'un codeur, d'un détecteur." + }, + { + "nom": "Connaître la nature du signal : analogique ou numérique." + }, + { + "nom": "Connaître la nature d'une information : logique ou analogique." + }, + { + "nom": "Interpréter des résultats expérimentaux, en tirer une conclusion et la communiquer en argumentant." + }, + { + "nom": "Connaître les notions d'écarts entre les attentes fixées par le cahier des charges et les résultats de l'expérimentation." + }, + { + "nom": "Utiliser une modélisation pour comprendre, formaliser, partager, construire, investiguer, prouver." + }, + { + "nom": "Connaître et utiliser les outils de description d'un fonctionnement, d'une structure et d'un comportement." + }, + { + "nom": "Simuler numériquement la structure et/ou le comportement d'un objet. Interpréter le comportement de l'objet technique et le communiquer en argumentant." + }, + { + "nom": "Connaître les notions d'écarts entre les attentes fixées par le cahier des charges et les résultats de la simulation." + } + ] + } + ] + }, + { + "nom": "Mathématiques", + "categories": [ + { + "nom": "Nombres et calculs", + "competences": [ + { + "nom": "Utiliser les nombres pour comparer, calculer et résoudre des problèmes.", + "fin_cycle": true + }, + { + "nom": "Comprendre et utiliser les notions de divisibilité et de nombres premiers.", + "fin_cycle": true + }, + { + "nom": "Utiliser le calcul littéral.", + "fin_cycle": true + }, + { + "nom": "Connaître les nombres décimaux (positifs et négatifs), la notion d'opposé." + }, + { + "nom": "Connaître les fractions, les nombres rationnels (positifs et négatifs), la notion d'inverse." + }, + { + "nom": "Connaître les carrés parfaits de 1 à 144." + }, + { + "nom": "Connaître la définition de la racine carrée." + }, + { + "nom": "Connaître les préfixes de nano à giga." + }, + { + "nom": "Utiliser diverses représentations d'un même nombre (écriture décimale ou fractionnaire, notation scientifique, repérage sur une droite graduée)." + }, + { + "nom": "Passer d'une représentation d'un nombre à une autre." + }, + { + "nom": "Connaître la notion d'égalité de fractions (démonstration possible à partir de la définition du quotient)." + }, + { + "nom": "Connaître la notion d'ordre sur les nombres rationnels en écriture décimale ou fractionnaire." + }, + { + "nom": "Comparer, ranger, encadrer des nombres rationnels en écriture décimale, fractionnaire ou scientifique." + }, + { + "nom": "Repérer et placer un nombre rationnel sur une droite graduée." + }, + { + "nom": "Associer à des objets des ordres de grandeur (par exemple taille d'un atome, d'une bactérie, d'une alvéole pulmonaire, longueur de l'intestin, capacité de stockage d'un disque dur, vitesses du son et de la lumière, populations française et mondiale, distance Terre-Lune, distance du Soleil à l'étoile la plus proche, etc.)." + }, + { + "nom": "Connaître les notions de somme, différence, produit, quotient de nombres décimaux, de deux nombres rationnels." + }, + { + "nom": "Connaître la définition des puissances d'un nombre (exposants entiers, positifs ou négatifs)." + }, + { + "nom": "Connaître la notation scientifique." + }, + { + "nom": "Calculer avec des nombres relatifs, des fractions, des nombres décimaux." + }, + { + "nom": "Vérifier la vraisemblance d'un résultat, notamment en estimant son ordre de grandeur." + }, + { + "nom": "Effectuer des calculs numériques simples impliquant des puissances, notamment en utilisant la notation scientifique." + }, + { + "nom": "Utiliser la racine carrée pour résoudre des problèmes, notamment géométriques." + }, + { + "nom": "Effectuer des calculs et des comparaisons pour traiter des problèmes." + }, + { + "nom": "Connaître la notion de multiples et diviseurs." + }, + { + "nom": "Connaître les critères de divisibilité par 2, 3, 5, 9." + }, + { + "nom": "Comprendre la division euclidienne (quotient, reste)." + }, + { + "nom": "Connaître la définition d'un nombre premier ; connaître la lliste des nombres premiers inférieurs ou égaux à 30." + }, + { + "nom": "Connaître la notion de fractions irréductibles." + }, + { + "nom": "Déterminer si un entier est ou n'est pas multiple ou diviseur d'un autre entier." + }, + { + "nom": "Déterminer les nombres premiers inférieurs ou égaux à 100." + }, + { + "nom": "Utiliser les critères de divisibilité par 2, 3, 5, 9, 10." + }, + { + "nom": "Déterminer les diviseurs d'un nombre à la main, à l'aide d'un tableur, d'une calculatrice." + }, + { + "nom": "Décomposer un nombre entier en produit de facteurs premiers (à la main ou à l'aide d'un logiciel)." + }, + { + "nom": "Simplifier une fraction pour la rendre irréductible." + }, + { + "nom": "Modéliser et résoudre des problèmes mettant en jeu la divisibilité (engrenages, conjonction de phénomènes, etc.)." + }, + { + "nom": "Connaître les notions d'inconnue, d'équation, d'indéterminée, d'identité." + }, + { + "nom": "Connaître les propriétés de distributivité (simple et double)." + }, + { + "nom": "Connaître la notion d'annulation d'un produit (démonstration possible par disjonction de cas)." + }, + { + "nom": "Connaître la factorisation de a² – b²." + }, + { + "nom": "Développer, factoriser, réduire des expressions algébriques dans des cas très simples." + }, + { + "nom": "Utiliser le calcul littéral pour traduire une propriété générale (par exemple la distributivité simple), pour démontrer un résultat général (par exemple que la somme de trois entiers consécutifs est un multiple de trois), pour valider ou réfuter une conjecture, pour modéliser une situation." + }, + { + "nom": "Mettre un problème en équation en vue de sa résolution." + }, + { + "nom": "Résoudre algébriquement des équations du premier degré ou s'y ramenant (équations produits), en particulier des équations du type x² = a." + } + ] + }, + { + "nom": "Organisation et gestion de données, fonctions", + "competences": [ + { + "nom": "Interpréter, représenter et traiter des données.", + "fin_cycle": true + }, + { + "nom": "Comprendre et utiliser des notions élémentaires de probabilités.", + "fin_cycle": true + }, + { + "nom": "Résoudre des problèmes de proportionnalité.", + "fin_cycle": true + }, + { + "nom": "Comprendre et utiliser la notion de fonction.", + "fin_cycle": true + }, + { + "nom": "Comprendre les notions d'effectifs, de fréquences." + }, + { + "nom": "Connaître la notion d'indicateurs de position : moyenne, médiane." + }, + { + "nom": "Connaître la notion d'indicateurs de dispersion : étendue." + }, + { + "nom": "Recueillir des données, les organiser." + }, + { + "nom": "Lire et interpréter des données sous forme de données brutes, de tableau, de diagramme (diagramme en bâtons, diagramme circulaire, histogramme)." + }, + { + "nom": "Utiliser un tableur-grapheur pour présenter des données sous la forme d'un tableau ou d'un diagramme." + }, + { + "nom": "Calculer des effectifs, des fréquences." + }, + { + "nom": "Calculer et interpréter des indicateurs de position ou de dispersion d'une série statistique." + }, + { + "nom": "Connaître le vocabulaire des probabilités." + }, + { + "nom": "Comprendre la notion de probabilité ; la probabilité d'un événement est comprise entre 0 et 1." + }, + { + "nom": "Comprendre la notion de probabilité d'événements certains, impossibles, contraires." + }, + { + "nom": "Aborder les questions relatives au hasard à partir de problèmes simples." + }, + { + "nom": "Calculer des probabilités dans des cas simples (par exemple évaluation des chances de gain dans un jeu)." + }, + { + "nom": "Exprimer des probabilités sous diverses formes (décimale, fractionnaire, pourcentage)." + }, + { + "nom": "Faire le lien entre fréquence et probabilité." + }, + { + "nom": "Comprendre la notion de coefficient de proportionnalité." + }, + { + "nom": "Connaître les notions de taux d'évolution, coefficient multiplicateur." + }, + { + "nom": "Connaître la notion de ratio." + }, + { + "nom": "Reconnaître une situation de proportionnalité ou de non-proportionnalité." + }, + { + "nom": "Calculer une quatrième proportionnelle." + }, + { + "nom": "Partager une quantité (par exemple une somme d'argent) en deux ou trois parts selon un ratio donné." + }, + { + "nom": "Utiliser une formule liant deux grandeurs dans une situation de proportionnalité (par exemple la longueur d'un cercle en fonction de son rayon, la loi d'Ohm exprimant la tension en fonction de l'intensité, la distance parcourue en fonction du temps à vitesse constante, etc.)." + }, + { + "nom": "Résoudre des problèmes utilisant la proportionnalité (pourcentages, échelles, agrandissement réduction)." + }, + { + "nom": "Connaître le vocabulaire : variable, fonction, antécédent, image." + }, + { + "nom": "Connaître les différents modes de représentation d'une fonction (expression symbolique, tableau de valeurs, représentation graphique, programme de calcul)." + }, + { + "nom": "Connaître les notations f(x) et x → f(x)." + }, + { + "nom": "Connaître les notions de fonction linéaire, fonction affine." + }, + { + "nom": "Passer d'un mode de représentation d'une fonction à un autre." + }, + { + "nom": "Déterminer, à partir d'un mode de représentation, l'image ou un antécédent d'un nombre par une fonction." + }, + { + "nom": "Représenter graphiquement une fonction linéaire, une fonction affine." + }, + { + "nom": "Modéliser un phénomène continu par une fonction." + }, + { + "nom": "Modéliser une situation de proportionnalité à l'aide d'une fonction linéaire." + }, + { + "nom": "Résoudre des problèmes modélisés par des fonctions." + } + ] + }, + { + "nom": "Grandeurs et mesures", + "competences": [ + { + "nom": "Calculer avec des grandeurs mesurables ; exprimer les résultats dans les unités adaptées.", + "fin_cycle": true + }, + { + "nom": "Comprendre l'effet de quelques transformations sur les figures géométriques.", + "fin_cycle": true + }, + { + "nom": "Connaître la notion de grandeur produit et de grandeur quotient." + }, + { + "nom": "Mesurer l'aire du parallélogramme (obtenue à partir de celle du rectangle par découpage et recollement)." + }, + { + "nom": "Connaître la formule donnant le volume d'un prisme, d'une pyramide, d'un cylindre, d'un cône, d'une boule." + }, + { + "nom": "Connaître la correspondance entre unités de volume et de contenance (1 L = 1 dm3, 1 000 L = 1 m³)." + }, + { + "nom": "Mener des calculs impliquant des grandeurs mesurables, notamment des grandeurs composées, exprimer les résultats dans les unités adaptées." + }, + { + "nom": "Vérifier la cohérence des résultats du point de vue des unités." + }, + { + "nom": "Effectuer des conversions d'unités." + }, + { + "nom": "Comprendre l'effet d'un déplacement, d'un agrandissement ou d'une réduction sur les longueurs, les angles, les aires et les volumes." + }, + { + "nom": "Utiliser un rapport de réduction ou d'agrandissement (architecture, maquettes) pour calculer des longueurs, des aires, des volumes." + }, + { + "nom": "Utiliser l'échelle d'une carte." + }, + { + "nom": "Utiliser des transformations pour calculer des grandeurs géométriques." + }, + { + "nom": "Faire le lien entre la proportionnalité et certaines configurations ou transformations géométriques (agrandissement réduction, triangles semblables, homothéties)." + } + ] + }, + { + "nom": "Espace et géométrie", + "competences": [ + { + "nom": "Représenter l'espace.", + "fin_cycle": true + }, + { + "nom": "Utiliser les notions de géométrie plane pour démontrer.", + "fin_cycle": true + }, + { + "nom": "Connaître les notions d'abscisse, d'ordonnée, d'altitude." + }, + { + "nom": "Connaître les notions de latitude, longitude." + }, + { + "nom": "(Se) repérer sur une droite graduée, dans le plan muni d'un repère orthogonal, dans un parallélépipède rectangle, sur une sphère." + }, + { + "nom": "Reconnaître des solides (pavé droit, cube, cylindre, pyramide, cône, boule)." + }, + { + "nom": "Construire et mettre en relation des représentations de ces solides (vues en perspective cavalière, de face, de dessus, sections planes, patrons, etc.)." + }, + { + "nom": "Utiliser un logiciel de géométrie dynamique pour représenter des solides." + }, + { + "nom": "Connaître la caractérisation angulaire du parallélisme : angles alternes internes, angles correspondants." + }, + { + "nom": "Connaître les caractéristiques du triangle : somme des angles d'un triangle (démonstration possible en utilisant les angles correspondants), hauteurs et médiatrices, inégalité triangulaire, cas d'égalité des triangles, triangles semblables (une définition et une propriété caractéristique)." + }, + { + "nom": "Connaître une définition et une propriété caractéristique du parallélogramme." + }, + { + "nom": "Connaître le théorème de Thalès et sa réciproque (configurations des triangles emboîtés et du Papillon)." + }, + { + "nom": "Connaître le théorème de Pythagore et sa réciproque." + }, + { + "nom": "Connaître les notions de lignes trigonométriques dans le triangle rectangle : cosinus, sinus, tangente." + }, + { + "nom": "Mettre en œuvre ou écrire un protocole de construction d'une figure géométrique." + }, + { + "nom": "Faire le lien entre les cas d'égalité des triangles et la construction d'un triangle à partir de la donnée de longueurs des côtés et/ou de mesures d'angles." + }, + { + "nom": "Comprendre l'effet d'une translation, d'une symétrie (axiale et centrale), d'une rotation, d'une homothétie sur une figure." + }, + { + "nom": "Mobiliser les connaissances des figures, des configurations et des transformations au programme pour déterminer des grandeurs géométriques." + }, + { + "nom": "Mener des raisonnements et s'initier à la démonstration en utilisant les propriétés des figures, des configurations et des transformations." + } + ] + }, + { + "nom": "Algorithmique et programmation", + "competences": [ + { + "nom": "Écrire, mettre au point et exécuter un programme simple.", + "fin_cycle": true + }, + { + "nom": "Connaître les notions d'algorithme et de programme." + }, + { + "nom": "Connaître la notion de variable informatique." + }, + { + "nom": "Connaître le déclenchement d'une action par un événement." + }, + { + "nom": "Connaître les séquences d'instructions, boucles, instructions conditionnelles." + }, + { + "nom": "Écrire, mettre au point (tester, corriger) et exécuter un programme en réponse à un problème donné." + } + ] + } + ] + }, + { + "nom": "Éducation aux médias et à l'information", + "categories": [] + }, + { + "nom": "Enseignement moral et civique", + "categories": [] + } + ] +} \ No newline at end of file diff --git a/Back-End/db.sqlite3 b/Back-End/db.sqlite3 deleted file mode 100644 index bb7dd98..0000000 Binary files a/Back-End/db.sqlite3 and /dev/null differ diff --git a/Back-End/requirements.txt b/Back-End/requirements.txt index b12640e..c930b58 100644 Binary files a/Back-End/requirements.txt and b/Back-End/requirements.txt differ diff --git a/Back-End/start.py b/Back-End/start.py index 0b0aff0..510e4c8 100644 --- a/Back-End/start.py +++ b/Back-End/start.py @@ -10,24 +10,41 @@ def run_command(command): print(f"stderr: {stderr.decode()}") return process.returncode +test_mode = os.getenv('test_mode', 'false').lower() == 'true' + commands = [ ["python", "manage.py", "collectstatic", "--noinput"], - ["python", "manage.py", "flush", "--noinput"], - ["python", "manage.py", "makemigrations", "Subscriptions"], - ["python", "manage.py", "makemigrations", "GestionNotification"], - ["python", "manage.py", "makemigrations", "GestionMessagerie"], - ["python", "manage.py", "makemigrations", "Auth"], - ["python", "manage.py", "makemigrations", "School"], - ["python", "manage.py", "migrate"] + #["python", "manage.py", "flush", "--noinput"], + # ["python", "manage.py", "makemigrations", "Common", "--noinput"], + # ["python", "manage.py", "makemigrations", "Establishment", "--noinput"], + # ["python", "manage.py", "makemigrations", "Settings", "--noinput"], + # ["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"], + # ["python", "manage.py", "makemigrations", "Planning", "--noinput"], + # ["python", "manage.py", "makemigrations", "GestionNotification", "--noinput"], + # ["python", "manage.py", "makemigrations", "GestionEmail", "--noinput"], + # ["python", "manage.py", "makemigrations", "GestionMessagerie", "--noinput"], + # ["python", "manage.py", "makemigrations", "Auth", "--noinput"], + # ["python", "manage.py", "makemigrations", "School", "--noinput"], + ["python", "manage.py", "migrate", "--noinput"] +] + +test_commands = [ + ["python", "manage.py", "init_mock_datas"] ] for command in commands: if run_command(command) != 0: exit(1) +#if test_mode: +# for test_command in test_commands: +# if run_command(test_command) != 0: +# exit(1) + # Lancer les processus en parallèle + processes = [ - subprocess.Popen(["python", "manage.py", "runserver", "0.0.0.0:8080"]), + subprocess.Popen(["daphne", "-b", "0.0.0.0", "-p", "8080", "N3wtSchool.asgi:application"]), subprocess.Popen(["celery", "-A", "N3wtSchool", "worker", "--loglevel=info"]), subprocess.Popen(["celery", "-A", "N3wtSchool", "beat", "--loglevel=info", "--scheduler", "django_celery_beat.schedulers:DatabaseScheduler"]) ] diff --git a/Back-End/static/img/logo_min.svg b/Back-End/static/img/logo_min.svg index ddcbde6..c8ee7dc 100644 --- a/Back-End/static/img/logo_min.svg +++ b/Back-End/static/img/logo_min.svg @@ -1,8 +1,42 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a516dac --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM postgres:latest + +# Installer curl +RUN apt-get update && apt-get install -y curl ruby ruby-dev build-essential +RUN gem install bcrypt + +# Copier le script d'initialisation +COPY initDocusealUsers.sh /docker-entrypoint-initdb.d/initDocusealUsers.sh + +# Donner les permissions d'exécution au script +RUN chmod +x /docker-entrypoint-initdb.d/initDocusealUsers.sh + +# Commande par défaut pour démarrer le conteneur +ENTRYPOINT ["/bin/bash", "/docker-entrypoint-initdb.d/initDocusealUsers.sh"] \ No newline at end of file diff --git a/Front-End/.babelrc b/Front-End/.babelrc new file mode 100644 index 0000000..9fcef03 --- /dev/null +++ b/Front-End/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["next/babel"], + "plugins": [] +} diff --git a/Front-End/.env b/Front-End/.env deleted file mode 100644 index f95783e..0000000 --- a/Front-End/.env +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_API_URL=http://localhost:8080 -NEXT_PUBLIC_USE_FAKE_DATA='false' \ No newline at end of file diff --git a/Front-End/.eslintignore b/Front-End/.eslintignore new file mode 100644 index 0000000..c555e65 --- /dev/null +++ b/Front-End/.eslintignore @@ -0,0 +1,3 @@ +node_modules/ +build/ +public/ \ No newline at end of file diff --git a/Front-End/.eslintrc.json b/Front-End/.eslintrc.json index bffb357..fa324d1 100644 --- a/Front-End/.eslintrc.json +++ b/Front-End/.eslintrc.json @@ -1,3 +1,10 @@ { - "extends": "next/core-web-vitals" + "extends": ["next", "next/core-web-vitals"], + "rules": { + // Ajoutez vos règles personnalisées ici + "react/react-in-jsx-scope": "off", // Désactive l'obligation d'importer React + "no-console": "error", // Avertissement pour les console.log + "semi": ["error", "always"], // Exige un point-virgule à la fin des lignes + "quotes": ["error", "single", { "avoidEscape": true }] // Exige des guillemets simples, sauf si l'on utilise des guillemets doubles à l'intérieur + } } diff --git a/Front-End/.prettierignore b/Front-End/.prettierignore new file mode 100644 index 0000000..7ee78a6 --- /dev/null +++ b/Front-End/.prettierignore @@ -0,0 +1,3 @@ +node_modules/ +build/ +dist/ \ No newline at end of file diff --git a/Front-End/.prettierrc b/Front-End/.prettierrc new file mode 100644 index 0000000..92f97e7 --- /dev/null +++ b/Front-End/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5" +} diff --git a/Front-End/.vscode/settings.json b/Front-End/.vscode/settings.json deleted file mode 100644 index c99754e..0000000 --- a/Front-End/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "i18n-ally.localesPaths": [ - "messages" - ], - "i18n-ally.keystyle": "nested" -} \ No newline at end of file diff --git a/Front-End/Dockerfile b/Front-End/Dockerfile new file mode 100644 index 0000000..e4f9948 --- /dev/null +++ b/Front-End/Dockerfile @@ -0,0 +1,45 @@ +# Build argument pour choisir le mode +ARG BUILD_MODE=production + +# Build stage pour production uniquement +FROM node:18-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY . . +COPY prod.env .env +RUN npm run build + +# Development stage +FROM node:18-alpine AS development +ENV NODE_ENV=development +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +# Ajout de la surveillance des fichiers pour le hot reload +ENV WATCHPACK_POLLING=true +ENV CHOKIDAR_USEPOLLING=true +COPY . . +EXPOSE 3000 +CMD ["npm", "run", "dev"] + +# Production stage +FROM node:18-alpine AS production +WORKDIR /app +ENV NODE_ENV=production +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/messages ./messages +COPY docker/entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +RUN chown 1001:1001 -R /app +USER nextjs +ENV HOSTNAME="0.0.0.0" +EXPOSE 3000 +ENTRYPOINT ["/app/entrypoint.sh"] +CMD ["node", "server.js"] + +# Final stage selection +FROM ${BUILD_MODE} diff --git a/Front-End/docker/entrypoint.sh b/Front-End/docker/entrypoint.sh new file mode 100644 index 0000000..7f6499e --- /dev/null +++ b/Front-End/docker/entrypoint.sh @@ -0,0 +1,28 @@ +#!/bin/sh + + +# Fonction pour échapper les caractères spéciaux +escape_value() { + echo "$1" | sed 's/[\/&]/\\&/g' +} + +replace_value() { + key=$1 + value=$2 + file=$3 + escaped_value=$(escape_value "$value") + find . -type f -exec sed -i "s|_${key}_|${value}|g" {} \; +} + +# Lire les clés et valeurs depuis un fichier .env +if [ -f .env ]; then + while IFS='=' read -r key value; do + # Ignorer les lignes vides et les commentaires + [ -z "$key" ] && continue + [ "${key#\#}" != "$key" ] && continue + + replace_value $key $value /app/ + done < .env +fi + +exec "$@" \ No newline at end of file diff --git a/Front-End/docs/api-messagerie-technique.md b/Front-End/docs/api-messagerie-technique.md new file mode 100644 index 0000000..101e250 --- /dev/null +++ b/Front-End/docs/api-messagerie-technique.md @@ -0,0 +1,340 @@ +# API Messagerie Instantanée - Guide Développeur + +## Vue d'ensemble + +Cette documentation technique présente l'implémentation du système de messagerie instantanée, incluant les APIs WebSocket et REST, l'architecture des composants React et les fonctions utilitaires. + +## API WebSocket + +### Connexion + +**URL de connexion :** + +```javascript +// Développement +ws://localhost:8000/ws/chat/{userId}/ + +// Production +wss://[domaine]/ws/chat/{userId}/ +``` + +### Messages WebSocket + +#### Messages entrants (serveur → client) + +```javascript +// Liste des conversations +{ + "type": "conversations_list", + "conversations": [...] +} + +// Nouveau message reçu +{ + "type": "new_message", + "message": { + "id": 123, + "conversation_id": 456, + "sender_id": 789, + "content": "Contenu du message", + "timestamp": "2024-01-01T12:00:00Z" + } +} + +// Utilisateur en train d'écrire +{ + "type": "typing_start", + "conversation_id": 456, + "user_id": 789 +} + +// Utilisateur a arrêté d'écrire +{ + "type": "typing_stop", + "conversation_id": 456, + "user_id": 789 +} +``` + +#### Messages sortants (client → serveur) + +```javascript +// Envoyer un message +{ + "type": "chat_message", + "conversation_id": 456, + "message": "Contenu du message" +} + +// Signaler début de frappe +{ + "type": "typing_start", + "conversation_id": 456 +} + +// Signaler fin de frappe +{ + "type": "typing_stop", + "conversation_id": 456 +} + +// Marquer comme lu +{ + "type": "mark_as_read", + "conversation_id": 456 +} + +// Rejoindre une conversation +{ + "type": "join_conversation", + "conversation_id": 456 +} +``` + +## API REST + +### Endpoints disponibles + +```javascript +// Récupérer les conversations +GET /api/messagerie/conversations/{userId}/ +Response: Array + +// Récupérer les messages d'une conversation +GET /api/messagerie/messages/{conversationId}/ +Response: Array + +// Rechercher des destinataires +GET /api/messagerie/search/{establishmentId}/?q={query} +Response: Array + +// Créer une conversation +POST /api/messagerie/conversations/create/ +Body: { "participants": [userId1, userId2] } +Response: Conversation + +// Envoyer un email (séparé de la messagerie instantanée) +POST /api/email/send/ +Body: { "recipients": [...], "subject": "...", "content": "..." } +``` + +## Composants React + +### InstantChat + +**Props :** + +```javascript +{ + userProfileId: number, // ID de l'utilisateur connecté + establishmentId: number // ID de l'établissement +} +``` + +**États principaux :** + +- `conversations` : Liste des conversations +- `selectedConversation` : Conversation active +- `messages` : Messages de la conversation active +- `searchQuery` : Terme de recherche +- `searchResults` : Résultats de recherche de contacts + +### useWebSocket Hook + +**Paramètres :** + +```javascript +useWebSocket( + userProfileId, // ID utilisateur + onMessage, // Callback pour messages reçus + onConnectionChange // Callback changement de connexion +); +``` + +**Valeurs retournées :** + +```javascript +{ + isConnected: boolean, + connectionStatus: string, + sendChatMessage: (conversationId, content) => boolean, + sendTypingStart: (conversationId) => void, + sendTypingStop: (conversationId) => void, + markAsRead: (conversationId) => void, + joinConversation: (conversationId) => void, + reconnect: () => void +} +``` + +## Actions Redux/State + +### messagerieAction.js + +```javascript +// Récupérer les conversations +fetchConversations(userId): Promise> + +// Récupérer les messages +fetchMessages(conversationId): Promise> + +// Rechercher des destinataires +searchMessagerieRecipients(establishmentId, query): Promise> + +// Créer une conversation +createConversation(participants): Promise +``` + +### emailAction.js + +```javascript +// Envoyer un email +sendEmail(recipients, subject, content, csrfToken): Promise + +// Rechercher des destinataires email +searchEmailRecipients(establishmentId, query): Promise> +``` + +## Modèles de Données + +### Conversation + +```javascript +{ + conversation_id: number, + participants: Array, + last_message: Message, + created_at: string, + updated_at: string +} +``` + +### Message + +```javascript +{ + id: number, + conversation_id: number, + sender_id: number, + content: string, + timestamp: string, + is_read: boolean +} +``` + +### User + +```javascript +{ + id: number, + first_name: string, + last_name: string, + email: string, + role: string +} +``` + +## Gestion des Erreurs + +### WebSocket + +```javascript +// Reconnexion automatique +const reconnectWebSocket = () => { + setConnectionStatus('reconnecting'); + // Logique de reconnexion avec backoff exponentiel +}; + +// Gestion des erreurs de connexion +wsRef.current.onerror = (error) => { + logger.error('Erreur WebSocket:', error); + setIsConnected(false); +}; +``` + +### API REST + +```javascript +// Wrapper avec gestion d'erreur +const apiCall = async (url, options) => { + try { + const response = await fetch(url, options); + if (!response.ok) { + throw new Error(`Erreur ${response.status}`); + } + return await response.json(); + } catch (error) { + logger.error('Erreur API:', error); + throw error; + } +}; +``` + +## Configuration des Tests + +### Jest Setup + +```javascript +// jest.setup.js +global.WebSocket = class MockWebSocket { + // Mock complet du WebSocket pour les tests +}; + +global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve([]), + }) +); +``` + +### Tests des Composants + +```javascript +// Exemple de test +test('renders InstantChat component', async () => { + await act(async () => { + render(); + }); + + expect(screen.getByText('Messages')).toBeInTheDocument(); +}); +``` + +## Intégration Backend + +### Consumer Django + +```python +# consumers.py +class ChatConsumer(AsyncWebsocketConsumer): + async def connect(self): + # Logique de connexion + + async def chat_message(self, event): + # Traitement des messages +``` + +### URLs Configuration + +```python +# routing.py +websocket_urlpatterns = [ + re_path(r'ws/chat/(?P\w+)/$', ChatConsumer.as_asgi()), +] +``` + +## Optimisations + +### Performance + +- Pagination des messages anciens (load on scroll) +- Debounce pour la recherche de contacts (300ms) +- Memoization des composants avec React.memo +- Lazy loading des conversations + +### UX + +- Reconnexion automatique avec feedback visuel +- Sauvegarde locale des messages en cours de frappe +- Indicateurs de livraison des messages +- Scrolling automatique vers les nouveaux messages diff --git a/Front-End/docs/messagerie-instantanee.md b/Front-End/docs/messagerie-instantanee.md new file mode 100644 index 0000000..dbfcf8a --- /dev/null +++ b/Front-End/docs/messagerie-instantanee.md @@ -0,0 +1,126 @@ +# Système de Messagerie Instantanée + +## Présentation + +Le système de messagerie instantanée de N3WT-SCHOOL permet aux utilisateurs de l'établissement (administrateurs, professeurs, parents, étudiants) de communiquer en temps réel via une interface chat moderne et intuitive. + +## Fonctionnalités + +### Chat en Temps Réel + +- Envoi et réception de messages instantanés +- Notification de statut de frappe (utilisateur en train d'écrire) +- Indicateur de statut de connexion WebSocket +- Reconnexion automatique en cas de perte de connexion + +### Gestion des Conversations + +- Liste des conversations existantes +- Création de nouvelles conversations +- Recherche de destinataires par nom ou email +- Compteur de messages non lus + +### Interface Utilisateur + +- Interface moderne en deux panneaux (conversations + chat) +- Bulles de messages différenciées (expéditeur/destinataire) +- Indicateurs visuels de statut de connexion +- Recherche temps réel de contacts + +## Utilisation + +### Accès au Chat + +Le système de messagerie est accessible via les pages suivantes : + +- **Parents** : `/[locale]/parents/messagerie` +- **Administrateurs** : Intégré dans le panneau d'administration + +### Créer une Conversation + +1. Cliquer sur le bouton "+" en haut à droite de la liste des conversations +2. Rechercher un contact en tapant son nom ou email +3. Sélectionner le destinataire dans les résultats +4. La conversation se crée automatiquement + +### Envoyer un Message + +1. Sélectionner une conversation dans la liste de gauche +2. Taper le message dans le champ de saisie en bas +3. Appuyer sur Entrée ou cliquer sur le bouton d'envoi + +## Architecture Technique + +### Frontend (React/Next.js) + +**Composants principaux :** + +- `InstantChat` : Composant principal du chat +- `ConnectionStatus` : Affichage du statut de connexion +- `ConversationItem` : Élément de liste de conversation +- `MessageBubble` : Bulle de message individuelle +- `MessageInput` : Zone de saisie de message +- `TypingIndicator` : Indicateur de frappe + +**Hook personnalisé :** + +- `useWebSocket` : Gestion de la connexion WebSocket et des événements + +### Backend (Django) + +**Module GestionMessagerie :** + +- `consumers.py` : Consumer WebSocket pour la messagerie temps réel +- `routing.py` : Configuration des routes WebSocket +- `urls.py` : URLs API REST pour les conversations et messages + +**Module GestionEmail :** + +- `views.py` : Vues pour l'envoi d'emails classiques +- `urls.py` : URLs pour les fonctions email + +### Communication + +- **WebSocket** : Communication bidirectionnelle temps réel +- **REST API** : Chargement initial des données et recherche +- **Channels** : Gestion des groupes de conversation Django + +## Configuration + +### URLs WebSocket + +Les URLs sont configurées automatiquement selon l'environnement : + +- **Développement** : `ws://localhost:8000/ws/chat/` +- **Production** : `wss://[domaine]/ws/chat/` + +### Variables d'Environnement + +Le système utilise les configurations standard de l'application pour : + +- Base de données (conversations, messages, utilisateurs) +- Authentification (sessions Django) +- Établissements (filtrage par établissement) + +## Sécurité + +- Authentification requise pour accéder au chat +- Filtrage des conversations par établissement +- Validation côté serveur de tous les messages +- Gestion des permissions selon le rôle utilisateur + +## Tests + +Le système dispose de tests unitaires Jest couvrant : + +- Rendu des composants +- Gestion des connexions WebSocket +- Recherche de contacts +- Envoi de messages +- Indicateurs de frappe + +Exécution des tests : + +```bash +npm test +``` diff --git a/Front-End/jest.config.js b/Front-End/jest.config.js new file mode 100644 index 0000000..6fdad4c --- /dev/null +++ b/Front-End/jest.config.js @@ -0,0 +1,33 @@ +const nextJest = require('next/jest'); + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files + dir: './', +}); + +// Add any custom config to be passed to Jest +const customJestConfig = { + setupFilesAfterEnv: ['/jest.setup.js'], + testEnvironment: 'jest-environment-jsdom', + testPathIgnorePatterns: ['/.next/', '/node_modules/'], + moduleNameMapper: { + '^@/(.*)$': '/src/$1', + }, + collectCoverageFrom: [ + 'src/**/*.{js,jsx}', + '!src/**/*.stories.{js,jsx}', + '!src/pages/_app.js', + '!src/pages/_document.js', + ], + coverageThreshold: { + global: { + branches: 70, + functions: 70, + lines: 70, + statements: 70, + }, + }, +}; + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +module.exports = createJestConfig(customJestConfig); diff --git a/Front-End/jest.setup.js b/Front-End/jest.setup.js new file mode 100644 index 0000000..7eaf3fb --- /dev/null +++ b/Front-End/jest.setup.js @@ -0,0 +1,95 @@ +import '@testing-library/jest-dom'; + +// Supprimer les avertissements React act() en environnement de test +global.IS_REACT_ACT_ENVIRONMENT = true; + +// Mock window.matchMedia +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // deprecated + removeListener: jest.fn(), // deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +// Mock IntersectionObserver +global.IntersectionObserver = class IntersectionObserver { + constructor() {} + observe() { + return null; + } + disconnect() { + return null; + } + unobserve() { + return null; + } +}; + +// Mock WebSocket +global.WebSocket = class WebSocket { + constructor(url) { + this.url = url; + this.readyState = WebSocket.CONNECTING; + setTimeout(() => { + this.readyState = WebSocket.OPEN; + if (this.onopen) this.onopen(); + }, 10); + } + + send(data) { + // Mock send + } + + close() { + this.readyState = WebSocket.CLOSED; + if (this.onclose) { + this.onclose({ + code: 1000, + reason: 'Normal closure', + wasClean: true, + }); + } + } + + static get CONNECTING() { + return 0; + } + static get OPEN() { + return 1; + } + static get CLOSING() { + return 2; + } + static get CLOSED() { + return 3; + } +}; + +// Mock global pour fetch +global.fetch = jest.fn(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve([]), + }) +); + +// Mock ResizeObserver +global.ResizeObserver = class ResizeObserver { + constructor() {} + observe() { + return null; + } + disconnect() { + return null; + } + unobserve() { + return null; + } +}; diff --git a/Front-End/messages/en/ResponsableInputFields.json b/Front-End/messages/en/ResponsableInputFields.json index 36105a6..c2bf0d9 100644 --- a/Front-End/messages/en/ResponsableInputFields.json +++ b/Front-End/messages/en/ResponsableInputFields.json @@ -1,4 +1,3 @@ - { "responsable": "Guardian", "delete": "Delete", @@ -10,4 +9,4 @@ "profession": "Profession", "address": "Address", "add_responsible": "Add guardian" -} \ No newline at end of file +} diff --git a/Front-End/messages/en/dashboard.json b/Front-End/messages/en/dashboard.json index 5c10d64..339d16e 100644 --- a/Front-End/messages/en/dashboard.json +++ b/Front-End/messages/en/dashboard.json @@ -1,9 +1,10 @@ { - "dashboard": "Dashboard", - "totalStudents": "Total Students", - "averageInscriptionTime": "Average Registration Time", - "reInscriptionRate": "Re-enrollment Rate", - "structureCapacity": "Structure Capacity", - "inscriptionTrends": "Enrollment Trends", - "upcomingEvents": "Upcoming Events" -} \ No newline at end of file + "dashboard": "Dashboard", + "totalStudents": "Total Students", + "pendingRegistrations": "Pending Registration", + "reInscriptionRate": "Re-enrollment Rate", + "structureCapacity": "Structure Capacity", + "capacityRate": "Capacity Rate", + "inscriptionTrends": "Enrollment Trends", + "upcomingEvents": "Upcoming Events" +} diff --git a/Front-End/messages/en/homePage.json b/Front-End/messages/en/homePage.json index c3d3d78..f6c601a 100644 --- a/Front-End/messages/en/homePage.json +++ b/Front-End/messages/en/homePage.json @@ -1,5 +1,5 @@ { - "welcomeParents": "Welcome Parents", - "pleaseLogin": "Please login to access your account", - "loginButton": "Go to login page" -} \ No newline at end of file + "welcomeParents": "Welcome Parents", + "pleaseLogin": "Please login to access your account", + "loginButton": "Go to login page" +} diff --git a/Front-End/messages/en/pagination.json b/Front-End/messages/en/pagination.json index 7b8cb98..3467b18 100644 --- a/Front-End/messages/en/pagination.json +++ b/Front-End/messages/en/pagination.json @@ -1,6 +1,6 @@ { - "page": "Page", - "of": "of", - "previous": "Previous", - "next": "Next" -} \ No newline at end of file + "page": "Page", + "of": "of", + "previous": "Previous", + "next": "Next" +} diff --git a/Front-End/messages/en/sidebar.json b/Front-End/messages/en/sidebar.json index ce09e55..16cc0fe 100644 --- a/Front-End/messages/en/sidebar.json +++ b/Front-End/messages/en/sidebar.json @@ -1,9 +1,11 @@ { - "dashboard": "Dashboard", - "subscriptions": "Subscriptions", - "structure": "Structure", - "planning": "Schedule", - "grades": "Grades", - "settings": "Settings", - "schoolAdmin": "School Administration" -} \ No newline at end of file + "dashboard": "Dashboard", + "subscriptions": "Subscriptions", + "structure": "Structure", + "directory": "Directory", + "events": "Events", + "educational_monitoring": "Educational Monitoring", + "settings": "Settings", + "schoolAdmin": "School Administration", + "messagerie": "Messenger" +} diff --git a/Front-End/messages/en/subscriptions.json b/Front-End/messages/en/subscriptions.json index ffde89a..53bf5cb 100644 --- a/Front-End/messages/en/subscriptions.json +++ b/Front-End/messages/en/subscriptions.json @@ -1,33 +1,35 @@ { - "headerBarTitle": "Administration", - "addStudent": "New", - "allStudents": "All Students", - "pending": "Pending Registrations", - "subscribed": "Subscribed", - "archived": "Archived", - "name": "Name", - "class": "Class", - "status": "Status", - "attendance": "Attendance", - "lastEvaluation": "Last Evaluation", - "active": "Active", - "pendingStatus": "Pending", - "goodAttendance": "Good", - "averageAttendance": "Average", - "lowAttendance": "Poor", - "searchStudent": "Search for a student...", - "title": "Registration", - "information": "Information", - "no_records": "There are currently no registration records.", - "add_button": "Add", - "create_first_record": "Please click the ADD button to create your first registration record.", - "studentName":"Student name", - "studentFistName":"Student first name", - "mainContactMail":"Main contact email", - "phone":"Phone", - "lastUpdateDate":"Last update", - "classe":"Class", - "registrationFileStatus":"Registration file status", - "files":"Files", - "subscribeFiles":"Subscribe files" -} \ No newline at end of file + "headerBarTitle": "Administration", + "addStudent": "New", + "allStudents": "All Students", + "pending": "Pending Registrations", + "subscribed": "Subscribed", + "archived": "Archived", + "photo": "Photo", + "name": "Name", + "class": "Class", + "status": "Status", + "attendance": "Attendance", + "lastEvaluation": "Last Evaluation", + "active": "Active", + "pendingStatus": "Pending", + "goodAttendance": "Good", + "averageAttendance": "Average", + "lowAttendance": "Poor", + "searchStudent": "Search for a student...", + "title": "Registration", + "information": "Information", + "no_records": "There are currently no registration records.", + "add_button": "Add", + "create_first_record": "Please click the ADD button to create your first registration record.", + "studentName": "Student name", + "studentFistName": "Student first name", + "mainContactMail": "Main contact email", + "phone": "Phone", + "lastUpdateDate": "Last update", + "classe": "Class", + "registrationFileStatus": "Registration file status", + "files": "Files", + "subscribeFiles": "Subscribe files", + "historical": "Historical" +} diff --git a/Front-End/messages/fr/ResponsableInputFields.json b/Front-End/messages/fr/ResponsableInputFields.json index 86ab314..461ce67 100644 --- a/Front-End/messages/fr/ResponsableInputFields.json +++ b/Front-End/messages/fr/ResponsableInputFields.json @@ -1,4 +1,3 @@ - { "responsable": "Responsable", "delete": "Supprimer", @@ -8,6 +7,5 @@ "phone": "Téléphone", "birthdate": "Date de naissance", "profession": "Profession", - "address": "Adresse", - "add_responsible": "Ajouter un responsable" + "address": "Adresse" } diff --git a/Front-End/messages/fr/dashboard.json b/Front-End/messages/fr/dashboard.json index 4ac34ef..c3b5a17 100644 --- a/Front-End/messages/fr/dashboard.json +++ b/Front-End/messages/fr/dashboard.json @@ -1,9 +1,10 @@ { - "dashboard": "Tableau de bord", - "totalStudents": "Total des étudiants", - "averageInscriptionTime": "Temps moyen d'inscription", - "reInscriptionRate": "Taux de réinscription", - "structureCapacity": "Remplissage de la structure", - "inscriptionTrends": "Tendances d'inscription", - "upcomingEvents": "Événements à venir" -} \ No newline at end of file + "dashboard": "Tableau de bord", + "totalStudents": "Total d'étudiants inscrits", + "pendingRegistrations": "Inscriptions en attente", + "reInscriptionRate": "Taux de réinscription", + "structureCapacity": "Capacité de la structure", + "capacityRate": "Remplissage de la structure", + "inscriptionTrends": "Tendances d'inscription", + "upcomingEvents": "Événements à venir" +} diff --git a/Front-End/messages/fr/homePage.json b/Front-End/messages/fr/homePage.json index 21561d1..e2e582f 100644 --- a/Front-End/messages/fr/homePage.json +++ b/Front-End/messages/fr/homePage.json @@ -1,5 +1,5 @@ { - "welcomeParents": "Bienvenue aux parents", - "pleaseLogin": "Veuillez vous connecter pour accéder à votre compte", - "loginButton": "Accéder à la page de login" -} \ No newline at end of file + "welcomeParents": "Bienvenue aux parents", + "pleaseLogin": "Veuillez vous connecter pour accéder à votre compte", + "loginButton": "Accéder à la page de login" +} diff --git a/Front-End/messages/fr/pagination.json b/Front-End/messages/fr/pagination.json index 60f6f77..6bb5772 100644 --- a/Front-End/messages/fr/pagination.json +++ b/Front-End/messages/fr/pagination.json @@ -1,6 +1,6 @@ { - "page": "Page", - "of": "sur", - "previous": "Précédent", - "next": "Suivant" -} \ No newline at end of file + "page": "Page", + "of": "sur", + "previous": "Précédent", + "next": "Suivant" +} diff --git a/Front-End/messages/fr/sidebar.json b/Front-End/messages/fr/sidebar.json index 3982d5f..aba45e7 100644 --- a/Front-End/messages/fr/sidebar.json +++ b/Front-End/messages/fr/sidebar.json @@ -1,9 +1,11 @@ - { - "dashboard": "Tableau de bord", - "subscriptions": "Inscriptions", - "structure": "Structure", - "planning": "Emploi du temps", - "grades": "Notes", - "settings": "Paramètres", - "schoolAdmin": "Administration Scolaire" - } \ No newline at end of file +{ + "dashboard": "Tableau de bord", + "subscriptions": "Inscriptions", + "structure": "Structure", + "directory": "Annuaire", + "events": "Evenements", + "educational_monitoring": "Suivi pédagogique", + "settings": "Paramètres", + "schoolAdmin": "Administration Scolaire", + "messagerie": "Messagerie" +} diff --git a/Front-End/messages/fr/subscriptions.json b/Front-End/messages/fr/subscriptions.json index eb1f509..413f570 100644 --- a/Front-End/messages/fr/subscriptions.json +++ b/Front-End/messages/fr/subscriptions.json @@ -1,33 +1,35 @@ { - "headerBarTitle":"Administration", - "addStudent": "Nouveau", - "allStudents": "Tous les élèves", - "pending": "Inscriptions en attente", - "subscribed": "Inscrits", - "archived": "Archivés", - "name": "Nom", - "class": "Classe", - "status": "Statut", - "attendance": "Assiduité", - "lastEvaluation": "Dernière évaluation", - "active": "Actif", - "pendingStatus": "En attente", - "goodAttendance": "Bonne", - "averageAttendance": "Moyenne", - "lowAttendance": "Faible", - "searchStudent": "Rechercher un élève...", - "title": "Inscription", - "information": "Information", - "no_records": "Il n'y a actuellement aucun dossier d'inscription.", - "add_button": "Ajouter", - "create_first_record": "Veuillez cliquer sur le bouton AJOUTER pour créer votre premier dossier d'inscription.", - "studentName":"Nom de l'élève", - "studentFistName":"Prénom de l'élève", - "mainContactMail":"Email de contact principal", - "phone":"Téléphone", - "lastUpdateDate":"Dernière mise à jour", - "classe":"Classe", - "registrationFileStatus":"État du dossier d'inscription", - "files":"Fichiers", - "subscribeFiles":"Fichiers d'inscription" -} \ No newline at end of file + "headerBarTitle": "Administration", + "addStudent": "Nouveau", + "allStudents": "Tous les élèves", + "pending": "Inscriptions en attente", + "subscribed": "Inscrits", + "archived": "Archivés", + "photo": "Photo", + "name": "Nom", + "class": "Classe", + "status": "Statut", + "attendance": "Assiduité", + "lastEvaluation": "Dernière évaluation", + "active": "Actif", + "pendingStatus": "En attente", + "goodAttendance": "Bonne", + "averageAttendance": "Moyenne", + "lowAttendance": "Faible", + "searchStudent": "Rechercher un élève...", + "title": "Inscription", + "information": "Information", + "no_records": "Il n'y a actuellement aucun dossier d'inscription.", + "add_button": "Ajouter", + "create_first_record": "Veuillez cliquer sur le bouton AJOUTER pour créer votre premier dossier d'inscription.", + "studentName": "Nom de l'élève", + "studentFistName": "Prénom de l'élève", + "mainContactMail": "Email de contact principal", + "phone": "Téléphone", + "lastUpdateDate": "Dernière mise à jour", + "classe": "Classe", + "registrationFileStatus": "État du dossier d'inscription", + "files": "Fichiers", + "subscribeFiles": "Fichiers d'inscription", + "historical": "Historique" +} diff --git a/Front-End/next.config.mjs b/Front-End/next.config.mjs index ff2db5f..01cf87a 100644 --- a/Front-End/next.config.mjs +++ b/Front-End/next.config.mjs @@ -1,8 +1,61 @@ import createNextIntlPlugin from 'next-intl/plugin'; +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); +const pkg = require('./package.json'); const withNextIntl = createNextIntlPlugin(); /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + output: 'standalone', + reactStrictMode: true, + experimental: { + instrumentationHook: true, + }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'www.gravatar.com', + }, + { + protocol: 'https', + hostname: 'api.demo.n3wtschool.com', + }, + { + protocol: 'https', + hostname: 'api.prod.n3wtschool.com', + }, + { + protocol: 'http', + hostname: 'localhost', + port: '8080', + }, + ], + }, + env: { + NEXT_PUBLIC_APP_VERSION: pkg.version, + NEXT_PUBLIC_API_URL: + process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080', + NEXT_PUBLIC_WSAPI_URL: + process.env.NEXT_PUBLIC_WSAPI_URL || 'ws://localhost:8080', + NEXT_PUBLIC_USE_FAKE_DATA: process.env.NEXT_PUBLIC_USE_FAKE_DATA || 'false', + AUTH_SECRET: process.env.AUTH_SECRET || 'false', + NEXTAUTH_URL: process.env.NEXTAUTH_URL || 'http://localhost:3000', + DOCUSEAL_API_KEY: process.env.DOCUSEAL_API_KEY, + }, + async rewrites() { + return [ + { + source: '/api/documents/:path*', + destination: 'https://api.docuseal.com/v1/documents/:path*', + }, + { + source: '/api/auth/:path*', + destination: '/api/auth/:path*', // Exclure les routes NextAuth des réécritures de proxy + }, + ]; + }, +}; -export default withNextIntl(nextConfig); \ No newline at end of file +export default withNextIntl(nextConfig); diff --git a/Front-End/package-lock.json b/Front-End/package-lock.json index 48831dc..c5f800b 100644 --- a/Front-End/package-lock.json +++ b/Front-End/package-lock.json @@ -1,40 +1,57 @@ { "name": "n3wt-school-front-end", "version": "0.0.1", - "lockfileVersion": 3, + "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "n3wt-school-front-end", "version": "0.0.1", "dependencies": { + "@docuseal/react": "^1.0.56", "@radix-ui/react-dialog": "^1.1.2", "@tailwindcss/forms": "^0.5.9", "date-fns": "^4.1.0", + "dayjs": "^1.11.13", "framer-motion": "^11.11.11", "ics": "^3.8.1", + "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "lucide-react": "^0.453.0", "next": "14.2.11", + "next-auth": "^4.24.11", "next-intl": "^3.24.0", + "next-logger": "^5.0.1", + "pino": "^9.6.0", "react": "^18", + "react-circular-progressbar": "^2.2.0", "react-cookie": "^7.2.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18", - "react-phone-number-input": "^3.4.8", + "react-international-phone": "^4.5.0", + "react-quill": "^2.0.0", "react-tooltip": "^5.28.0" }, "devDependencies": { - "@babel/parser": "^7.26.2", - "@babel/traverse": "^7.25.9", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^14.4.3", "autoprefixer": "^10.4.20", "eslint": "^8", "eslint-config-next": "14.2.11", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "postcss": "^8.4.47", "tailwindcss": "^3.4.14" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", + "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", + "dev": true + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -46,28 +63,80 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -76,31 +145,108 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "dev": true, "dependencies": { - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -109,43 +255,261 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, "dependencies": { - "regenerator-runtime": "^0.14.0" + "@babel/helper-plugin-utils": "^7.8.0" }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -153,54 +517,62 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@docuseal/react": { + "version": "1.0.66", + "resolved": "https://registry.npmjs.org/@docuseal/react/-/react-1.0.66.tgz", + "integrity": "sha512-rYG58gv8Uw1cTtjbHdgWgWBWpLMbIwDVsS3kN27w4sz/eDJilZieePUDS4eLKJ8keBN05BSjxD/iWQpaTBKZLg==" + }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, - "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -219,91 +591,126 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/js": { - "version": "8.57.0", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@floating-ui/core": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", - "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", "dependencies": { - "@floating-ui/utils": "^0.2.8" + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.11", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", - "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.8" + "@floating-ui/core": "^1.7.0", + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", - "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.1.tgz", - "integrity": "sha512-O4ywpkdJybrjFc9zyL8qK5aklleIAi5O4nYhBVJaOFtCkNrnU+lKFeJOFC48zpsZQmR8Aok2V79hGpHnzbmFpg==", - "license": "MIT", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", "dependencies": { - "@formatjs/fast-memoize": "2.2.2", - "@formatjs/intl-localematcher": "0.5.6", - "tslib": "2" + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "dependencies": { + "tslib": "^2.8.0" } }, "node_modules/@formatjs/fast-memoize": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.2.tgz", - "integrity": "sha512-mzxZcS0g1pOzwZTslJOBTmLzDXseMLLvnh25ymRilCm8QLMObsQ7x/rj9GNrH0iUhZMlFisVOD6J1n6WQqpKPQ==", - "license": "MIT", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", "dependencies": { - "tslib": "2" + "tslib": "^2.8.0" } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.1.tgz", - "integrity": "sha512-7AYk4tjnLi5wBkxst2w7qFj38JLMJoqzj7BhdEl7oTlsWMlqwgx4p9oMmmvpXWTSDGNwOKBRc1SfwMh5MOHeNg==", - "license": "MIT", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.1", - "@formatjs/icu-skeleton-parser": "1.8.5", - "tslib": "2" + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.5.tgz", - "integrity": "sha512-zRZ/e3B5qY2+JCLs7puTzWS1Jb+t/K+8Jur/gEZpA2EjWeLDE17nsx8thyo9P48Mno7UmafnPupV2NCJXX17Dg==", - "license": "MIT", + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.1", - "tslib": "2" + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.6.tgz", - "integrity": "sha512-roz1+Ba5e23AHX6KUAWmLEyTRZegM5YDuxuvkHCyK3RJddf/UXB2f+s7pOMm9ktfPGla0g+mQXOn5vsuYirnaA==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", + "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", "dependencies": { "tslib": "2" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -313,8 +720,9 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -325,12 +733,15 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", - "dev": true, - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -345,7 +756,8 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -355,7 +767,8 @@ }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -366,10 +779,515 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/core/node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -411,12 +1329,14 @@ }, "node_modules/@next/env": { "version": "14.2.11", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.11.tgz", + "integrity": "sha512-HYsQRSIXwiNqvzzYThrBwq6RhXo3E0n8j8nQnAs8i4fCEo2Zf/3eS0IiRA8XnRg9Ha0YnpkyJZIZg1qEwemrHw==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.11.tgz", + "integrity": "sha512-7mw+xW7Y03Ph4NTCcAzYe+vu4BNjEHZUfZayyF3Y1D9RX6c5NIe25m1grHEAkyUuaqjRxOYhnCNeglOkIqLkBA==", "dev": true, - "license": "MIT", "dependencies": { "glob": "10.3.10" } @@ -543,10 +1463,11 @@ }, "node_modules/@next/swc-win32-x64-msvc": { "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.11.tgz", + "integrity": "sha512-gQpS7mcgovWoaTG1FbS5/ojF7CGfql1Q0ZLsMrhcsi2Sr9HEqsUZ70MPJyaYBXbk6iEAP7UXMD9HC8KY1qNwvA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -557,7 +1478,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -568,14 +1490,16 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -586,31 +1510,39 @@ }, "node_modules/@nolyfill/is-core-module": { "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12.4.0" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "optional": true, "engines": { "node": ">=14" } }, "node_modules/@radix-ui/primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", - "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==", - "license": "MIT" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" }, "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", - "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", - "license": "MIT", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -622,10 +1554,9 @@ } }, "node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", - "license": "MIT", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -637,25 +1568,24 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", - "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", - "license": "MIT", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.13.tgz", + "integrity": "sha512-ARFmqUyhIVS3+riWzwGTe7JLjqwqgnODBUZdqpWar/z1WFs9z76fuOs/2BOWCR+YboRn4/WN9aoaGVwqNRr8VA==", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -673,16 +1603,15 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", - "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", - "license": "MIT", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", + "integrity": "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -700,10 +1629,9 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", - "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", - "license": "MIT", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -715,14 +1643,13 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", - "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", - "license": "MIT", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", + "integrity": "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -740,12 +1667,11 @@ } }, "node_modules/@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -758,13 +1684,12 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", - "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", - "license": "MIT", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", + "integrity": "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==", "dependencies": { - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -782,13 +1707,12 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", - "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", - "license": "MIT", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -806,12 +1730,11 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", - "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", - "license": "MIT", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", + "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", "dependencies": { - "@radix-ui/react-slot": "1.1.0" + "@radix-ui/react-slot": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -829,12 +1752,11 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", - "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", - "license": "MIT", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", + "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -847,10 +1769,9 @@ } }, "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", - "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -862,12 +1783,29 @@ } }, "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", - "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", - "license": "MIT", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -880,12 +1818,11 @@ } }, "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", - "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -898,10 +1835,9 @@ } }, "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", - "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -915,53 +1851,356 @@ "node_modules/@react-dnd/asap": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", - "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==", - "license": "MIT" + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" }, "node_modules/@react-dnd/invariant": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", - "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==", - "license": "MIT" + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" }, "node_modules/@react-dnd/shallowequal": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", - "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", - "license": "MIT" + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, "node_modules/@rtsao/scc": { "version": "1.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.4", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", + "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "license": "MIT" + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } }, "node_modules/@swc/counter": { "version": "0.1.3", - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, "node_modules/@swc/helpers": { "version": "0.5.5", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", "dependencies": { "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, "node_modules/@tailwindcss/forms": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz", - "integrity": "sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==", - "license": "MIT", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", "dependencies": { "mini-svg-data-uri": "^1.2.3" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20" + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "peer": true + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/react/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/react/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@testing-library/react/node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@testing-library/react/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" } }, "node_modules/@types/cookie": { @@ -969,48 +2208,200 @@ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", - "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", + "devOptional": true, + "dependencies": { + "undici-types": "~6.21.0" + } }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "dependencies": { + "parchment": "^1.1.2" + } }, "node_modules/@types/react": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", - "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "version": "18.3.22", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.22.tgz", + "integrity": "sha512-vUhG0YmQZ7kL/tmKLrD3g5zXbXXreZXB3pmROW8bg3CnLnpjkRVwUlLne7Ufa2r9yJ8+/6B73RzhAek5TBKh2Q==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "devOptional": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, "node_modules/@types/semver": { - "version": "7.5.8", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", "dev": true, - "license": "MIT" + "dependencies": { + "@types/jest": "*" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "7.2.0", @@ -1041,10 +2432,23 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/parser": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "7.2.0", "@typescript-eslint/types": "7.2.0", @@ -1070,8 +2474,9 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.2.0", "@typescript-eslint/visitor-keys": "7.2.0" @@ -1086,8 +2491,9 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "7.2.0", "@typescript-eslint/utils": "7.2.0", @@ -1112,8 +2518,9 @@ }, "node_modules/@typescript-eslint/types": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", "dev": true, - "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -1124,8 +2531,9 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "7.2.0", "@typescript-eslint/visitor-keys": "7.2.0", @@ -1151,16 +2559,18 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1171,10 +2581,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", @@ -1195,10 +2618,23 @@ "eslint": "^8.56.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "7.2.0", "eslint-visitor-keys": "^3.4.1" @@ -1212,14 +2648,36 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz", + "integrity": "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC" + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true }, "node_modules/acorn": { - "version": "8.12.1", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1227,18 +2685,54 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1250,16 +2744,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { "color-convert": "^2.0.1" }, @@ -1294,14 +2817,14 @@ }, "node_modules/argparse": { "version": "2.0.1", - "dev": true, - "license": "Python-2.0" + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/aria-hidden": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "license": "MIT", "dependencies": { "tslib": "^2.0.0" }, @@ -1310,20 +2833,22 @@ } }, "node_modules/aria-query": { - "version": "5.1.3", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "deep-equal": "^2.0.5" + "engines": { + "node": ">= 0.4" } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -1334,8 +2859,9 @@ }, "node_modules/array-includes": { "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -1353,16 +2879,18 @@ }, "node_modules/array-union": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/array.prototype.findlast": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -1379,16 +2907,18 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -1398,14 +2928,15 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.2", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1415,14 +2946,15 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1433,8 +2965,9 @@ }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -1447,18 +2980,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -1469,13 +3002,37 @@ }, "node_modules/ast-types-flow": { "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "license": "MIT" + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } }, "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "dev": true, "funding": [ { @@ -1492,11 +3049,11 @@ } ], "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -1511,8 +3068,9 @@ }, "node_modules/available-typed-arrays": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, - "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -1524,24 +3082,137 @@ } }, "node_modules/axe-core": { - "version": "4.10.0", + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", "dev": true, - "license": "MPL-2.0", "engines": { "node": ">=4" } }, "node_modules/axobject-query": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@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", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -1556,8 +3227,9 @@ }, "node_modules/brace-expansion": { "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1565,7 +3237,8 @@ }, "node_modules/braces": { "version": "3.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { "fill-range": "^7.1.1" }, @@ -1574,9 +3247,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", "dev": true, "funding": [ { @@ -1593,10 +3266,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -1605,8 +3278,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "node_modules/busboy": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dependencies": { "streamsearch": "^1.1.0" }, @@ -1615,15 +3310,41 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "dev": true, - "license": "MIT", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -1634,8 +3355,18 @@ }, "node_modules/callsites": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -1649,9 +3380,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001672", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001672.tgz", - "integrity": "sha512-XhW1vRo1ob6aeK2w3rTohwTPBLse/rvjq+s3RTSBwnlZqoFFjx9cHsShJjAIbLsLjyoacaTxpLZy9v3gg6zypw==", + "version": "1.0.30001717", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", + "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==", "funding": [ { "type": "opencollective", @@ -1669,8 +3400,9 @@ }, "node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1682,6 +3414,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1716,6 +3457,27 @@ "node": ">= 6" } }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -1723,11 +3485,88 @@ }, "node_modules/client-only": { "version": "0.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true }, "node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { "color-name": "~1.1.4" }, @@ -1737,7 +3576,20 @@ }, "node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, "node_modules/commander": { "version": "4.1.1", @@ -1749,28 +3601,147 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/country-flag-icons": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.13.tgz", - "integrity": "sha512-4JwHNqaKZ19doQoNcBjsoYA+I7NqCH/mC/6f5cBWvdKzcK5TMmzLpq3Z/syVHMHJuDGFwJ+rPpGizvrqJybJow==" + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/create-jest/node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/create-jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1780,6 +3751,12 @@ "node": ">= 8" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1791,6 +3768,30 @@ "node": ">=4" } }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -1798,17 +3799,33 @@ }, "node_modules/damerau-levenshtein": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", "dev": true, - "license": "BSD-2-Clause" + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } }, "node_modules/data-view-buffer": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1818,27 +3835,29 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -1853,16 +3872,22 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", - "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debug": { - "version": "4.3.7", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -1875,29 +3900,36 @@ } } }, - "node_modules/deep-equal": { - "version": "2.2.3", + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", "dev": true, - "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", "object-is": "^1.1.5", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" + "regexp.prototype.flags": "^1.5.1" }, "engines": { "node": ">= 0.4" @@ -1908,13 +3940,23 @@ }, "node_modules/deep-is": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=0.10.0" + } }, "node_modules/define-data-property": { "version": "1.1.4", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -1929,8 +3971,8 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -1943,21 +3985,58 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -1974,7 +4053,6 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", - "license": "MIT", "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", @@ -1983,8 +4061,9 @@ }, "node_modules/doctrine": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -1992,83 +4071,152 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } }, "node_modules/electron-to-chromium": { - "version": "1.5.47", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz", - "integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==", + "version": "1.5.151", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.151.tgz", + "integrity": "sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA==", "dev": true }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, - "node_modules/enhanced-resolve": { - "version": "5.17.1", + "node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, "engines": { - "node": ">=10.13.0" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" } }, "node_modules/es-abstract": { - "version": "1.23.3", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, - "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -2078,28 +4226,26 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } }, "node_modules/es-errors": { "version": "1.3.0", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "engines": { "node": ">= 0.4" } }, "node_modules/es-get-iterator": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", @@ -2116,33 +4262,36 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.19", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "dev": true, - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { "es-errors": "^1.3.0" }, @@ -2151,34 +4300,41 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, - "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, - "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { - "version": "1.2.1", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, - "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -2198,8 +4354,9 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -2207,16 +4364,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "8.57.0", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -2263,8 +4443,9 @@ }, "node_modules/eslint-config-next": { "version": "14.2.11", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.11.tgz", + "integrity": "sha512-gGIoBoHCJuLn6vaV1Ke8UurVvgb7JjQv6oRlWmI6RAAxz7KwJOYxxm2blctavA0a3eofbE9TdgKvvTb2G55OHQ==", "dev": true, - "license": "MIT", "dependencies": { "@next/eslint-plugin-next": "14.2.11", "@rushstack/eslint-patch": "^1.3.3", @@ -2289,8 +4470,9 @@ }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -2299,31 +4481,32 @@ }, "node_modules/eslint-import-resolver-node/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.6.3", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, - "license": "ISC", "dependencies": { "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.3.5", - "enhanced-resolve": "^5.15.0", - "eslint-module-utils": "^2.8.1", - "fast-glob": "^3.3.2", - "get-tsconfig": "^4.7.5", - "is-bun-module": "^1.0.2", - "is-glob": "^4.0.3" + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", @@ -2340,9 +4523,10 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.11.0", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -2357,16 +4541,18 @@ }, "node_modules/eslint-module-utils/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import": { - "version": "2.30.0", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, - "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -2376,7 +4562,7 @@ "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.9.0", + "eslint-module-utils": "^2.12.0", "hasown": "^2.0.2", "is-core-module": "^2.15.1", "is-glob": "^4.0.3", @@ -2385,27 +4571,30 @@ "object.groupby": "^1.0.3", "object.values": "^1.2.0", "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import/node_modules/doctrine": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -2413,20 +4602,13 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.0", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, - "license": "MIT", "dependencies": { - "aria-query": "~5.1.3", + "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", @@ -2434,14 +4616,13 @@ "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.19", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.0" + "string.prototype.includes": "^2.0.1" }, "engines": { "node": ">=4.0" @@ -2451,27 +4632,28 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.36.1", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.19", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -2482,9 +4664,10 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", + "version": "5.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -2494,8 +4677,9 @@ }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -2505,8 +4689,9 @@ }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -2519,18 +4704,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-scope": { "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -2544,8 +4722,9 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2553,10 +4732,38 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -2569,10 +4776,24 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -2582,8 +4803,9 @@ }, "node_modules/esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -2593,33 +4815,106 @@ }, "node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" }, "node_modules/fast-glob": { - "version": "3.3.2", - "license": "MIT", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -2627,7 +4922,8 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dependencies": { "is-glob": "^4.0.1" }, @@ -2637,25 +4933,46 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "engines": { + "node": ">=6" + } }, "node_modules/fastq": { - "version": "1.17.1", - "license": "ISC", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -2665,7 +4982,8 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2675,8 +4993,9 @@ }, "node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -2690,8 +5009,9 @@ }, "node_modules/flat-cache": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -2702,23 +5022,32 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "dev": true, - "license": "ISC" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true }, "node_modules/for-each": { - "version": "0.3.3", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, - "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { - "version": "3.3.0", - "license": "ISC", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -2728,6 +5057,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -2742,16 +5086,18 @@ } }, "node_modules/framer-motion": { - "version": "11.11.11", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.11.11.tgz", - "integrity": "sha512-tuDH23ptJAKUHGydJQII9PhABNJBpB+z0P1bmgKK9QFIssHGlfPd6kxMq00LSKwE27WFsb2z0ovY0bpUyMvfRw==", + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/is-prop-valid": { @@ -2767,8 +5113,9 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -2785,20 +5132,24 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/function.prototype.name": { - "version": "1.1.6", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -2809,22 +5160,45 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2837,19 +5211,52 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, - "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dependencies": { - "call-bind": "^1.0.5", + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -2859,9 +5266,10 @@ } }, "node_modules/get-tsconfig": { - "version": "4.8.1", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", "dev": true, - "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -2871,7 +5279,8 @@ }, "node_modules/glob": { "version": "10.3.10", - "license": "ISC", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", @@ -2891,7 +5300,8 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dependencies": { "is-glob": "^4.0.3" }, @@ -2901,14 +5311,16 @@ }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/glob/node_modules/minimatch": { "version": "9.0.5", - "license": "ISC", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2920,23 +5332,19 @@ } }, "node_modules/globals": { - "version": "13.24.0", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/globalthis": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -2950,8 +5358,9 @@ }, "node_modules/globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -2968,11 +5377,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2980,33 +5389,40 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "license": "ISC" + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/graphemer": { "version": "1.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "node_modules/has-bigints": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { "es-define-property": "^1.0.0" }, @@ -3015,9 +5431,13 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, - "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -3026,9 +5446,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "dev": true, - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -3038,8 +5458,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { "has-symbols": "^1.0.3" }, @@ -3052,7 +5472,8 @@ }, "node_modules/hasown": { "version": "2.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -3068,6 +5489,72 @@ "react-is": "^16.7.0" } }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/ics/-/ics-3.8.1.tgz", @@ -3080,16 +5567,18 @@ }, "node_modules/ignore": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { - "version": "3.3.0", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3101,18 +5590,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3120,58 +5640,42 @@ }, "node_modules/inherits": { "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/input-format": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/input-format/-/input-format-0.3.10.tgz", - "integrity": "sha512-5cFv/kOZD7Ch0viprVkuYPDkAU7HBZYBx8QrIpQ6yXUWbAQ0+RQ8IIojDJOf/RO6FDJLL099HDSK2KoVZ2zevg==", - "dependencies": { - "prop-types": "^15.8.1" - } + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/internal-slot": { - "version": "1.0.7", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/intl-messageformat": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.3.tgz", - "integrity": "sha512-AAo/3oyh7ROfPhDuh7DxTTydh97OC+lv7h1Eq5LuHWuLsUMKOhtzTYuyXlUReuwZ9vANDHo4CS1bGRrn7TZRtg==", - "license": "BSD-3-Clause", + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.1", - "@formatjs/fast-memoize": "2.2.2", - "@formatjs/icu-messageformat-parser": "2.9.1", - "tslib": "2" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" } }, "node_modules/is-arguments": { - "version": "1.1.1", - "dev": true, - "license": "MIT", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3181,12 +5685,14 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -3195,12 +5701,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-async-function": { - "version": "2.0.0", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3210,11 +5727,15 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, - "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3232,12 +5753,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3247,17 +5769,31 @@ } }, "node_modules/is-bun-module": { - "version": "1.2.1", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, - "license": "MIT", "dependencies": { - "semver": "^7.6.3" + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/is-callable": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3266,8 +5802,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "license": "MIT", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { "hasown": "^2.0.2" }, @@ -3279,10 +5816,13 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, - "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -3293,11 +5833,12 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "dev": true, - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3308,17 +5849,22 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "engines": { "node": ">=0.10.0" } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3326,17 +5872,31 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { "node": ">=8" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3347,7 +5907,8 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dependencies": { "is-extglob": "^2.1.1" }, @@ -3357,19 +5918,9 @@ }, "node_modules/is-map": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3379,17 +5930,20 @@ }, "node_modules/is-number": { "version": "7.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "engines": { "node": ">=0.12.0" } }, "node_modules/is-number-object": { - "version": "1.0.7", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3400,19 +5954,28 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, "node_modules/is-regex": { - "version": "1.1.4", - "dev": true, - "license": "MIT", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3423,8 +5986,9 @@ }, "node_modules/is-set": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3433,11 +5997,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -3446,12 +6011,26 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-string": { - "version": "1.0.7", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3461,11 +6040,14 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, - "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3475,11 +6057,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, - "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -3490,8 +6073,9 @@ }, "node_modules/is-weakmap": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3500,23 +6084,28 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -3527,28 +6116,114 @@ }, "node_modules/isarray": { "version": "2.0.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/iterator.prototype": { - "version": "1.1.2", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, - "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/jackspeak": { "version": "2.3.6", - "license": "BlueOak-1.0.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -3562,22 +6237,965 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-cli/node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "bin": { "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -3585,10 +7203,55 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "bin": { "jsesc": "bin/jsesc" @@ -3599,34 +7262,77 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/json5": { - "version": "1.0.2", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/jsx-ast-utils": { "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -3637,23 +7343,54 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", - "dev": true, - "license": "CC0-1.0" + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true }, "node_modules/language-tags": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, - "license": "MIT", "dependencies": { "language-subtag-registry": "^0.3.20" }, @@ -3661,10 +7398,20 @@ "node": ">=0.10" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -3673,17 +7420,15 @@ "node": ">= 0.8.0" } }, - "node_modules/libphonenumber-js": { - "version": "1.11.8", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.8.tgz", - "integrity": "sha512-0fv/YKpJBAgXKy0kaS3fnqoUVN8901vUYAKIGD/MWZaDfhJt1nZjPL3ZzdZBt/G8G8Hw2J1xOIrXWdNHFHPAvg==" - }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -3693,8 +7438,9 @@ }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -3708,17 +7454,53 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, "node_modules/lodash.merge": { "version": "4.6.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, "node_modules/loose-envify": { "version": "1.4.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -3727,8 +7509,13 @@ } }, "node_modules/lru-cache": { - "version": "10.4.3", - "license": "ISC" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } }, "node_modules/lucide-react": { "version": "0.453.0", @@ -3738,16 +7525,77 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "node_modules/merge2": { "version": "1.4.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { "version": "4.0.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -3756,19 +7604,58 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/mini-svg-data-uri": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", - "license": "MIT", "bin": { "mini-svg-data-uri": "cli.js" } }, "node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3778,23 +7665,38 @@ }, "node_modules/minimist": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { "version": "7.1.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==" + }, "node_modules/ms": { "version": "2.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mz": { "version": "2.7.0", @@ -3807,14 +7709,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3822,14 +7725,39 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-postinstall": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.3.tgz", + "integrity": "sha512-Mi7JISo/4Ij2tDZ2xBE2WH+/KvVlkhA6juEjpEeRAVPNCpN3nxJo/5FhDNKgBcdmcmhaH6JjgST4xY/23ZYK0w==", + "dev": true, + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } }, "node_modules/next": { "version": "14.2.11", - "license": "MIT", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.11.tgz", + "integrity": "sha512-8MDFqHBhdmR2wdfaWc8+lW3A/hppFe1ggQ9vgIu/g2/2QEMYJrPoQP6b+VNk56gIug/bStysAmrpUKtj3XN8Bw==", "dependencies": { "@next/env": "14.2.11", "@swc/helpers": "0.5.5", @@ -3875,34 +7803,76 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.11", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz", + "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.7.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "@auth/core": "0.34.2", + "next": "^12.2.5 || ^13 || ^14 || ^15", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18 || ^19", + "react-dom": "^17.0.2 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@auth/core": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/next-intl": { - "version": "3.24.0", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.24.0.tgz", - "integrity": "sha512-48X68QsI92grir2dH1W15yhyVnEjW4c9qmwNt+du+k6mI1QtlE6GyANWHoL4/leTixHv8knZ1y9B/Ys06gmKLg==", + "version": "3.26.5", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.26.5.tgz", + "integrity": "sha512-EQlCIfY0jOhRldiFxwSXG+ImwkQtDEfQeSOEQp6ieAGSLWGlgjdb/Ck/O7wMfC430ZHGeUKVKax8KGusTPKCgg==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/amannn" } ], - "license": "MIT", "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "negotiator": "^1.0.0", - "use-intl": "^3.24.0" + "use-intl": "^3.26.5" }, "peerDependencies": { "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, - "node_modules/next-intl/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node_modules/next-logger": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/next-logger/-/next-logger-5.0.1.tgz", + "integrity": "sha512-zWTPtS0YwTB+4iSK4VxUVtCYt+zg8+Sx2Tjbtgmpd4SXsFnWdmCbXAeFZFKtEH8yNlucLCUaj0xqposMQ9rKRg==", + "dependencies": { + "lilconfig": "^3.1.2" + }, + "peerDependencies": { + "next": ">=9.0.0", + "pino": "^8.0.0 || ^9.0.0", + "winston": "^3.0.0" + }, + "peerDependenciesMeta": { + "pino": { + "optional": true + }, + "winston": { + "optional": true + } } }, "node_modules/next/node_modules/postcss": { @@ -3932,10 +7902,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true }, "node_modules/normalize-path": { @@ -3955,25 +7931,50 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "dev": true + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "engines": { "node": ">= 6" } }, "node_modules/object-inspect": { - "version": "1.13.2", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3983,8 +7984,8 @@ }, "node_modules/object-is": { "version": "1.1.6", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" @@ -3998,20 +7999,23 @@ }, "node_modules/object-keys": { "version": "1.1.1", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { - "version": "4.1.5", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -4022,13 +8026,15 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -4036,8 +8042,9 @@ }, "node_modules/object.fromentries": { "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -4053,8 +8060,9 @@ }, "node_modules/object.groupby": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -4065,11 +8073,13 @@ } }, "node_modules/object.values": { - "version": "1.2.0", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -4080,18 +8090,81 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oidc-token-hash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", + "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "ISC", "dependencies": { "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "dependencies": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/openid-client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -4104,10 +8177,28 @@ "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -4120,8 +8211,9 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -4132,10 +8224,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -4143,36 +8250,71 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", - "license": "BlueOak-1.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -4184,21 +8326,29 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, "node_modules/path-type": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/picocolors": { - "version": "1.1.0", - "license": "ISC" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "engines": { "node": ">=8.6" }, @@ -4214,26 +8364,125 @@ "node": ">=0.10.0" } }, + "node_modules/pino": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz", + "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" + }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "engines": { "node": ">= 6" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -4249,8 +8498,8 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -4291,51 +8540,6 @@ "postcss": "^8.4.21" } }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", @@ -4377,17 +8581,73 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/preact": { + "version": "10.26.6", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.6.tgz", + "integrity": "sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, + "node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/prop-types": { "version": "15.8.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -4399,16 +8659,53 @@ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/punycode": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "funding": [ { "type": "github", @@ -4422,12 +8719,43 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } }, "node_modules/react": { "version": "18.3.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -4435,10 +8763,19 @@ "node": ">=0.10.0" } }, + "node_modules/react-circular-progressbar": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.2.0.tgz", + "integrity": "sha512-cgyqEHOzB0nWMZjKfWN3MfSa1LV3OatcDjPz68lchXQUEiBD5O1WsAtoVK4/DSL0B4USR//cTdok4zCBkq8X5g==", + "license": "MIT", + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/react-cookie": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.2.0.tgz", - "integrity": "sha512-mqhPERUyfOljq5yJ4woDFI33bjEtigsl8JDJdPPeNhr0eSVZmBc/2Vdf8mFxOUktQxhxTR1T+uF0/FRTZyBEgw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.2.2.tgz", + "integrity": "sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg==", "dependencies": { "@types/hoist-non-react-statics": "^3.3.5", "hoist-non-react-statics": "^3.3.2", @@ -4452,7 +8789,6 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", - "license": "MIT", "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", @@ -4482,14 +8818,14 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", - "license": "MIT", "dependencies": { "dnd-core": "^16.0.1" } }, "node_modules/react-dom": { "version": "18.3.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -4498,44 +8834,50 @@ "react": "^18.3.1" } }, + "node_modules/react-international-phone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-international-phone/-/react-international-phone-4.5.0.tgz", + "integrity": "sha512-wjwHv+VfiwM49B5/6El4Z5vZKmf3ILpUeiOCI9X+b0Dq4g5nL8gROcwCdVcTXywxznbDSoxSassBX3i9tPZX6g==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-is": { "version": "16.13.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/react-phone-number-input": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/react-phone-number-input/-/react-phone-number-input-3.4.8.tgz", - "integrity": "sha512-CoJ0OrG42NtAoAl78xCbw4nuhNc4llCxfcS8zB8wSzY1IJ5ib0nSHkOeKqIfm1rzZhTnwjxgecA8wEtzlargUg==", + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", "dependencies": { - "classnames": "^2.5.1", - "country-flag-icons": "^1.5.11", - "input-format": "^0.3.10", - "libphonenumber-js": "^1.11.8", - "prop-types": "^15.8.1" + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" }, "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" } }, "node_modules/react-remove-scroll": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", - "integrity": "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==", - "license": "MIT", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", "dependencies": { - "react-remove-scroll-bar": "^2.3.6", - "react-style-singleton": "^2.2.1", + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4544,20 +8886,19 @@ } }, "node_modules/react-remove-scroll-bar": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", - "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", - "license": "MIT", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "dependencies": { - "react-style-singleton": "^2.2.1", + "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -4566,21 +8907,19 @@ } }, "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "license": "MIT", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "dependencies": { "get-nonce": "^1.0.0", - "invariant": "^2.2.4", "tslib": "^2.0.0" }, "engines": { "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4589,9 +8928,9 @@ } }, "node_modules/react-tooltip": { - "version": "5.28.0", - "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.0.tgz", - "integrity": "sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==", + "version": "5.28.1", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.1.tgz", + "integrity": "sha512-ZA4oHwoIIK09TS7PvSLFcRlje1wGZaxw6xHvfrzn6T82UcMEfEmHVCad16Gnr4NDNDh93HyN037VK4HDi5odfQ==", "dependencies": { "@floating-ui/dom": "^1.6.1", "classnames": "^2.3.0" @@ -4620,27 +8959,49 @@ "node": ">=8.10.0" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.9.2" } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -4649,21 +9010,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "dev": true, - "license": "MIT", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4672,40 +9029,92 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/resolve": { - "version": "1.22.8", - "license": "MIT", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/reusify": { - "version": "1.0.4", - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -4713,8 +9122,10 @@ }, "node_modules/rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -4727,8 +9138,10 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4746,6 +9159,8 @@ }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "funding": [ { "type": "github", @@ -4760,7 +9175,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -4771,13 +9185,15 @@ "integrity": "sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==" }, "node_modules/safe-array-concat": { - "version": "1.1.2", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -4787,14 +9203,33 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-regex-test": { - "version": "1.0.3", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "isarray": "^2.0.5" }, "engines": { "node": ">= 0.4" @@ -4803,28 +9238,70 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } }, "node_modules/semver": { - "version": "7.6.3", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/set-function-length": { "version": "1.2.2", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -4839,8 +9316,8 @@ }, "node_modules/set-function-name": { "version": "2.0.2", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -4851,9 +9328,24 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -4863,20 +9355,76 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "engines": { "node": ">=8" } }, "node_modules/side-channel": { - "version": "1.0.6", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4887,7 +9435,8 @@ }, "node_modules/signal-exit": { "version": "4.1.0", - "license": "ISC", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "engines": { "node": ">=14" }, @@ -4895,27 +9444,105 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, "node_modules/slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "license": "BSD-3-Clause", + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "license": "MIT", "dependencies": { - "internal-slot": "^1.0.4" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4923,13 +9550,29 @@ }, "node_modules/streamsearch": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "engines": { "node": ">=10.0.0" } }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/string-width": { "version": "5.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -4945,7 +9588,8 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -4957,11 +9601,13 @@ }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -4971,7 +9617,8 @@ }, "node_modules/string-width/node_modules/strip-ansi": { "version": "7.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -4983,31 +9630,38 @@ } }, "node_modules/string.prototype.includes": { - "version": "2.0.0", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5018,22 +9672,27 @@ }, "node_modules/string.prototype.repeat": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5043,22 +9702,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5073,7 +9738,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5084,7 +9750,8 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5094,16 +9761,39 @@ }, "node_modules/strip-bom": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -5113,7 +9803,8 @@ }, "node_modules/styled-jsx": { "version": "5.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "dependencies": { "client-only": "0.0.1" }, @@ -5155,8 +9846,9 @@ }, "node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5166,7 +9858,8 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "engines": { "node": ">= 0.4" }, @@ -5174,33 +9867,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "node_modules/tailwindcss": { - "version": "3.4.14", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", - "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.0", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", @@ -5210,18 +9909,88 @@ "node": ">=14.0.0" } }, - "node_modules/tapable": { - "version": "2.2.1", - "dev": true, - "license": "MIT", + "node_modules/tailwindcss/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "engines": { - "node": ">=6" + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/text-table": { "version": "0.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/thenify": { "version": "3.3.1", @@ -5242,14 +10011,71 @@ "node": ">=0.8" } }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/tiny-case": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, "node_modules/to-regex-range": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dependencies": { "is-number": "^7.0.0" }, @@ -5262,10 +10088,38 @@ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, - "node_modules/ts-api-utils": { - "version": "1.3.0", + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, - "license": "MIT", "engines": { "node": ">=16" }, @@ -5280,8 +10134,9 @@ }, "node_modules/tsconfig-paths": { "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -5289,14 +10144,28 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { - "version": "2.7.0", - "license": "0BSD" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -5304,40 +10173,51 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -5347,16 +10227,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -5366,16 +10248,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -5385,9 +10268,10 @@ } }, "node_modules/typescript": { - "version": "5.6.2", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, - "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", @@ -5398,33 +10282,83 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true + }, "node_modules/universal-cookie": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.2.tgz", "integrity": "sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ==", - "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.7.2" } }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unrs-resolver": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz", + "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "napi-postinstall": "^0.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/JounQin" + }, + "optionalDependencies": { + "@unrs/resolver-binding-darwin-arm64": "1.7.2", + "@unrs/resolver-binding-darwin-x64": "1.7.2", + "@unrs/resolver-binding-freebsd-x64": "1.7.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.7.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-musl": "1.7.2", + "@unrs/resolver-binding-wasm32-wasi": "1.7.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.7.2" + } + }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -5442,7 +10376,7 @@ ], "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -5453,17 +10387,27 @@ }, "node_modules/uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "license": "MIT", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "dependencies": { "tslib": "^2.0.0" }, @@ -5471,8 +10415,8 @@ "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5481,23 +10425,21 @@ } }, "node_modules/use-intl": { - "version": "3.24.0", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.24.0.tgz", - "integrity": "sha512-lmrARod7yjMYehbyY9xBLjjgnlNcJsl1UAltAPlgspRG7RH6H0JYaGo4C3PZW/BTy0Dgmcvcl8rH/VemzGIhgQ==", - "license": "MIT", + "version": "3.26.5", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.26.5.tgz", + "integrity": "sha512-OdsJnC/znPvHCHLQH/duvQNXnP1w0hPfS+tkSi3mAbfjYBGh4JnyfdwkQBfIVf7t8gs9eSX/CntxUMvtKdG2MQ==", "dependencies": { "@formatjs/fast-memoize": "^2.2.0", "intl-messageformat": "^10.5.14" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "license": "MIT", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" @@ -5506,8 +10448,8 @@ "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5520,9 +10462,96 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/which": { "version": "2.0.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dependencies": { "isexe": "^2.0.0" }, @@ -5534,37 +10563,43 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -5575,8 +10610,9 @@ }, "node_modules/which-collection": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -5591,14 +10627,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -5610,15 +10649,17 @@ }, "node_modules/word-wrap": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/wrap-ansi": { "version": "8.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -5634,7 +10675,8 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5649,11 +10691,13 @@ }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5665,7 +10709,8 @@ }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -5675,7 +10720,8 @@ }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "6.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "engines": { "node": ">=12" }, @@ -5685,7 +10731,8 @@ }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -5698,13 +10745,84 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "license": "ISC" + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yaml": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", - "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", "bin": { "yaml": "bin.mjs" }, @@ -5712,10 +10830,58 @@ "node": ">= 14" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -5724,25 +10890,7708 @@ } }, "node_modules/yup": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz", - "integrity": "sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", "dependencies": { "property-expr": "^2.0.5", "tiny-case": "^1.0.3", "toposort": "^2.0.2", "type-fest": "^2.19.0" } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", + "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", + "dev": true }, - "node_modules/yup/node_modules/type-fest": { + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" + }, + "@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + } + }, + "@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true + }, + "@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + } + }, + "@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "dev": true, + "requires": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "requires": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + } + }, + "@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true + }, + "@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true + }, + "@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "dev": true, + "requires": { + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + } + }, + "@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "dev": true, + "requires": { + "@babel/types": "^7.27.1" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" + } + }, + "@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==" + }, + "@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + } + }, + "@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@docuseal/react": { + "version": "1.0.66", + "resolved": "https://registry.npmjs.org/@docuseal/react/-/react-1.0.66.tgz", + "integrity": "sha512-rYG58gv8Uw1cTtjbHdgWgWBWpLMbIwDVsS3kN27w4sz/eDJilZieePUDS4eLKJ8keBN05BSjxD/iWQpaTBKZLg==" + }, + "@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.4.3" + } + }, + "@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true + }, + "@floating-ui/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", + "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", + "requires": { + "@floating-ui/utils": "^0.2.9" + } + }, + "@floating-ui/dom": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", + "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", + "requires": { + "@floating-ui/core": "^1.7.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" + }, + "@formatjs/ecma402-abstract": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz", + "integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==", + "requires": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.1", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + }, + "dependencies": { + "@formatjs/intl-localematcher": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz", + "integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==", + "requires": { + "tslib": "^2.8.0" + } + } + } + }, + "@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "requires": { + "tslib": "^2.8.0" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz", + "integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==", + "requires": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/icu-skeleton-parser": "1.8.14", + "tslib": "^2.8.0" + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz", + "integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==", + "requires": { + "@formatjs/ecma402-abstract": "2.3.4", + "tslib": "^2.8.0" + } + }, + "@formatjs/intl-localematcher": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz", + "integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==", + "requires": { + "tslib": "2" + } + }, + "@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + } + }, + "@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "requires": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + } + }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + } + }, + "@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "requires": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@next/env": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.11.tgz", + "integrity": "sha512-HYsQRSIXwiNqvzzYThrBwq6RhXo3E0n8j8nQnAs8i4fCEo2Zf/3eS0IiRA8XnRg9Ha0YnpkyJZIZg1qEwemrHw==" + }, + "@next/eslint-plugin-next": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.11.tgz", + "integrity": "sha512-7mw+xW7Y03Ph4NTCcAzYe+vu4BNjEHZUfZayyF3Y1D9RX6c5NIe25m1grHEAkyUuaqjRxOYhnCNeglOkIqLkBA==", + "dev": true, + "requires": { + "glob": "10.3.10" + } + }, + "@next/swc-darwin-arm64": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.11.tgz", + "integrity": "sha512-eiY9u7wEJZWp/Pga07Qy3ZmNEfALmmSS1HtsJF3y1QEyaExu7boENz11fWqDmZ3uvcyAxCMhTrA1jfVxITQW8g==", + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.11.tgz", + "integrity": "sha512-lnB0zYCld4yE0IX3ANrVMmtAbziBb7MYekcmR6iE9bujmgERl6+FK+b0MBq0pl304lYe7zO4yxJus9H/Af8jbg==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.11.tgz", + "integrity": "sha512-Ulo9TZVocYmUAtzvZ7FfldtwUoQY0+9z3BiXZCLSUwU2bp7GqHA7/bqrfsArDlUb2xeGwn3ZuBbKtNK8TR0A8w==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.11.tgz", + "integrity": "sha512-fH377DnKGyUnkWlmUpFF1T90m0dADBfK11dF8sOQkiELF9M+YwDRCGe8ZyDzvQcUd20Rr5U7vpZRrAxKwd3Rzg==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.11.tgz", + "integrity": "sha512-a0TH4ZZp4NS0LgXP/488kgvWelNpwfgGTUCDXVhPGH6pInb7yIYNgM4kmNWOxBFt+TIuOH6Pi9NnGG4XWFUyXQ==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.11.tgz", + "integrity": "sha512-DYYZcO4Uir2gZxA4D2JcOAKVs8ZxbOFYPpXSVIgeoQbREbeEHxysVsg3nY4FrQy51e5opxt5mOHl/LzIyZBoKA==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.11.tgz", + "integrity": "sha512-PwqHeKG3/kKfPpM6of1B9UJ+Er6ySUy59PeFu0Un0LBzJTRKKAg2V6J60Yqzp99m55mLa+YTbU6xj61ImTv9mg==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.11.tgz", + "integrity": "sha512-0U7PWMnOYIvM74GY6rbH6w7v+vNPDVH1gUhlwHpfInJnNe5LkmUZqhp7FNWeNa5wbVgRcRi1F1cyxp4dmeLLvA==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.11.tgz", + "integrity": "sha512-gQpS7mcgovWoaTG1FbS5/ojF7CGfql1Q0ZLsMrhcsi2Sr9HEqsUZ70MPJyaYBXbk6iEAP7UXMD9HC8KY1qNwvA==", + "optional": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true + }, + "@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==" + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true + }, + "@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" + }, + "@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "requires": {} + }, + "@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "requires": {} + }, + "@radix-ui/react-dialog": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.13.tgz", + "integrity": "sha512-ARFmqUyhIVS3+riWzwGTe7JLjqwqgnODBUZdqpWar/z1WFs9z76fuOs/2BOWCR+YboRn4/WN9aoaGVwqNRr8VA==", + "requires": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + } + }, + "@radix-ui/react-dismissable-layer": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", + "integrity": "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==", + "requires": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + } + }, + "@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "requires": {} + }, + "@radix-ui/react-focus-scope": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", + "integrity": "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-callback-ref": "1.1.1" + } + }, + "@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "requires": { + "@radix-ui/react-use-layout-effect": "1.1.1" + } + }, + "@radix-ui/react-portal": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", + "integrity": "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==", + "requires": { + "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + } + }, + "@radix-ui/react-presence": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + } + }, + "@radix-ui/react-primitive": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", + "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", + "requires": { + "@radix-ui/react-slot": "1.2.2" + } + }, + "@radix-ui/react-slot": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", + "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", + "requires": { + "@radix-ui/react-compose-refs": "1.1.2" + } + }, + "@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "requires": {} + }, + "@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "requires": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + } + }, + "@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "requires": { + "@radix-ui/react-use-layout-effect": "1.1.1" + } + }, + "@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "requires": { + "@radix-ui/react-use-callback-ref": "1.1.1" + } + }, + "@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "requires": {} + }, + "@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, + "@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "@rushstack/eslint-patch": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", + "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", + "dev": true + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "requires": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, + "@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true + }, + "aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "peer": true, + "requires": { + "dequal": "^2.0.3" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "peer": true + } + } + }, + "@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "dependencies": { + "@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "requires": { + "deep-equal": "^2.0.5" + } + }, + "deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + } + } + }, + "@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "requires": {} + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + }, + "@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/node": { + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", + "devOptional": true, + "requires": { + "undici-types": "~6.21.0" + } + }, + "@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + }, + "@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "requires": { + "parchment": "^1.1.2" + } + }, + "@types/react": { + "version": "18.3.22", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.22.tgz", + "integrity": "sha512-vUhG0YmQZ7kL/tmKLrD3g5zXbXXreZXB3pmROW8bg3CnLnpjkRVwUlLne7Ufa2r9yJ8+/6B73RzhAek5TBKh2Q==", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "devOptional": true, + "requires": {} + }, + "@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "@types/testing-library__jest-dom": { + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", + "dev": true, + "requires": { + "@types/jest": "*" + } + }, + "@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/type-utils": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true + } + } + }, + "@typescript-eslint/utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.2.0", + "eslint-visitor-keys": "^3.4.1" + } + }, + "@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz", + "integrity": "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==", + "dev": true, + "optional": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true + }, + "acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "requires": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "requires": { + "acorn": "^8.11.0" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "requires": { + "tslib": "^2.0.0" + } + }, + "aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true + }, + "array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + } + }, + "array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + } + }, + "array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + } + }, + "array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + } + }, + "array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + } + }, + "ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, + "autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "requires": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, + "axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "dev": true + }, + "axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true + }, + "babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "requires": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + } + } + }, + "babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@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", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + } + }, + "babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "requires": { + "fill-range": "^7.1.1" + } + }, + "browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, + "call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "requires": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + } + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-lite": { + "version": "1.0.30001717", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", + "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true + }, + "classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + }, + "create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + } + }, + "data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==" + }, + "dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, + "debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" + }, + "dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "requires": {} + }, + "deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "requires": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "peer": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "requires": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "electron-to-chromium": { + "version": "1.5.151", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.151.tgz", + "integrity": "sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA==", + "dev": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + } + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + } + }, + "es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + } + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "requires": { + "hasown": "^2.0.2" + } + }, + "es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "requires": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + } + }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-next": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.11.tgz", + "integrity": "sha512-gGIoBoHCJuLn6vaV1Ke8UurVvgb7JjQv6oRlWmI6RAAxz7KwJOYxxm2blctavA0a3eofbE9TdgKvvTb2G55OHQ==", + "dev": true, + "requires": { + "@next/eslint-plugin-next": "14.2.11", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "requires": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + } + }, + "eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "requires": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "requires": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + } + }, + "eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "requires": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "5.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, + "fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==" + }, + "fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "requires": { + "is-callable": "^1.2.7" + } + }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + } + }, + "form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + } + }, + "fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true + }, + "framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "requires": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==" + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + } + }, + "get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "requires": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/ics/-/ics-3.8.1.tgz", + "integrity": "sha512-UqQlfkajfhrS4pUGQfGIJMYz/Jsl/ob3LqcfEhUmLbwumg+ZNkU0/6S734Vsjq3/FYNpEcZVKodLBoe+zBM69g==", + "requires": { + "nanoid": "^3.1.23", + "runes2": "^1.1.2", + "yup": "^1.2.0" + } + }, + "ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true + }, + "import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + } + }, + "intl-messageformat": { + "version": "10.7.16", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz", + "integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==", + "requires": { + "@formatjs/ecma402-abstract": "2.3.4", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.2", + "tslib": "^2.8.0" + } + }, + "is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, + "is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "requires": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + } + }, + "is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "requires": { + "has-bigints": "^1.0.2" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "requires": { + "semver": "^7.7.1" + }, + "dependencies": { + "semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true + } + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "requires": { + "hasown": "^2.0.2" + } + }, + "is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + } + }, + "is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "requires": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + } + }, + "is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.16" + } + }, + "is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true + }, + "is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "requires": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + } + }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + } + }, + "jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + } + }, + "jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "requires": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + } + }, + "jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } + } + }, + "jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + } + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==" + }, + "jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + } + }, + "jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" + } + } + }, + "jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + } + }, + "jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "requires": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true + }, + "language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "requires": { + "language-subtag-registry": "^0.3.20" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==" + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "lucide-react": { + "version": "0.453.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.453.0.tgz", + "integrity": "sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==", + "requires": {} + }, + "lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true + }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + }, + "dependencies": { + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true + } + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" + }, + "motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "requires": { + "motion-utils": "^11.18.1" + } + }, + "motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==" + }, + "napi-postinstall": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.3.tgz", + "integrity": "sha512-Mi7JISo/4Ij2tDZ2xBE2WH+/KvVlkhA6juEjpEeRAVPNCpN3nxJo/5FhDNKgBcdmcmhaH6JjgST4xY/23ZYK0w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==" + }, + "next": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.11.tgz", + "integrity": "sha512-8MDFqHBhdmR2wdfaWc8+lW3A/hppFe1ggQ9vgIu/g2/2QEMYJrPoQP6b+VNk56gIug/bStysAmrpUKtj3XN8Bw==", + "requires": { + "@next/env": "14.2.11", + "@next/swc-darwin-arm64": "14.2.11", + "@next/swc-darwin-x64": "14.2.11", + "@next/swc-linux-arm64-gnu": "14.2.11", + "@next/swc-linux-arm64-musl": "14.2.11", + "@next/swc-linux-x64-gnu": "14.2.11", + "@next/swc-linux-x64-musl": "14.2.11", + "@next/swc-win32-arm64-msvc": "14.2.11", + "@next/swc-win32-ia32-msvc": "14.2.11", + "@next/swc-win32-x64-msvc": "14.2.11", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "dependencies": { + "postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + } + } + }, + "next-auth": { + "version": "4.24.11", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz", + "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==", + "requires": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.7.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + } + }, + "next-intl": { + "version": "3.26.5", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.26.5.tgz", + "integrity": "sha512-EQlCIfY0jOhRldiFxwSXG+ImwkQtDEfQeSOEQp6ieAGSLWGlgjdb/Ck/O7wMfC430ZHGeUKVKax8KGusTPKCgg==", + "requires": { + "@formatjs/intl-localematcher": "^0.5.4", + "negotiator": "^1.0.0", + "use-intl": "^3.26.5" + } + }, + "next-logger": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/next-logger/-/next-logger-5.0.1.tgz", + "integrity": "sha512-zWTPtS0YwTB+4iSK4VxUVtCYt+zg8+Sx2Tjbtgmpd4SXsFnWdmCbXAeFZFKtEH8yNlucLCUaj0xqposMQ9rKRg==", + "requires": { + "lilconfig": "^3.1.2" + } + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "dev": true + }, + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" + }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true + }, + "object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + } + }, + "object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + } + }, + "object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + } + }, + "object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "oidc-token-hash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", + "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==" + }, + "on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "requires": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + } + }, + "own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "requires": { + "entities": "^6.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + } + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pino": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz", + "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==", + "requires": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + } + }, + "pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "requires": { + "split2": "^4.0.0" + } + }, + "pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" + }, + "pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==" + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } + }, + "possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true + }, + "postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "requires": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + } + }, + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "requires": { + "postcss-selector-parser": "^6.1.1" + } + }, + "postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "preact": { + "version": "10.26.6", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.6.tgz", + "integrity": "sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==" + }, + "preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "requires": { + "pretty-format": "^3.8.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, + "process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==" + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, + "psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "requires": { + "punycode": "^2.3.1" + } + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "requires": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "requires": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + } + }, + "react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-circular-progressbar": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.2.0.tgz", + "integrity": "sha512-cgyqEHOzB0nWMZjKfWN3MfSa1LV3OatcDjPz68lchXQUEiBD5O1WsAtoVK4/DSL0B4USR//cTdok4zCBkq8X5g==", + "requires": {} + }, + "react-cookie": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.2.2.tgz", + "integrity": "sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.5", + "hoist-non-react-statics": "^3.3.2", + "universal-cookie": "^7.0.0" + } + }, + "react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "requires": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + } + }, + "react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "requires": { + "dnd-core": "^16.0.1" + } + }, + "react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + } + }, + "react-international-phone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-international-phone/-/react-international-phone-4.5.0.tgz", + "integrity": "sha512-wjwHv+VfiwM49B5/6El4Z5vZKmf3ILpUeiOCI9X+b0Dq4g5nL8gROcwCdVcTXywxznbDSoxSassBX3i9tPZX6g==", + "requires": {} + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "requires": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + } + }, + "react-remove-scroll": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "requires": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + } + }, + "react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "requires": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + } + }, + "react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "requires": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + } + }, + "react-tooltip": { + "version": "5.28.1", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.1.tgz", + "integrity": "sha512-ZA4oHwoIIK09TS7PvSLFcRlje1wGZaxw6xHvfrzn6T82UcMEfEmHVCad16Gnr4NDNDh93HyN037VK4HDi5odfQ==", + "requires": { + "@floating-ui/dom": "^1.6.1", + "classnames": "^2.3.0" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + } + }, + "regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "requires": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true + }, + "resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true + }, + "reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "runes2": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/runes2/-/runes2-1.1.4.tgz", + "integrity": "sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==" + }, + "safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + } + }, + "safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + } + }, + "safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } + }, + "set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + } + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "requires": { + "atomic-sleep": "^1.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + } + }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + } + } + }, + "string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + } + }, + "string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + } + }, + "string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "requires": { + "client-only": "0.0.1" + } + }, + "sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "requires": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "dependencies": { + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "requires": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "requires": { + "real-require": "^0.2.0" + } + }, + "tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, + "tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "requires": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "dependencies": { + "fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "requires": {} + }, + "picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true + } + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, + "tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "requires": {} + }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, + "tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "engines": { - "node": ">=12.20" + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + }, + "typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + } + }, + "typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + } + }, + "typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "peer": true + }, + "unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + } + }, + "undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true + }, + "universal-cookie": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.2.tgz", + "integrity": "sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ==", + "requires": { + "@types/cookie": "^0.6.0", + "cookie": "^0.7.2" + } + }, + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + }, + "unrs-resolver": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz", + "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==", + "dev": true, + "requires": { + "@unrs/resolver-binding-darwin-arm64": "1.7.2", + "@unrs/resolver-binding-darwin-x64": "1.7.2", + "@unrs/resolver-binding-freebsd-x64": "1.7.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.7.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.7.2", + "@unrs/resolver-binding-linux-x64-musl": "1.7.2", + "@unrs/resolver-binding-wasm32-wasi": "1.7.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.7.2", + "napi-postinstall": "^0.2.2" + } + }, + "update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "requires": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "requires": { + "tslib": "^2.0.0" + } + }, + "use-intl": { + "version": "3.26.5", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.26.5.tgz", + "integrity": "sha512-OdsJnC/znPvHCHLQH/duvQNXnP1w0hPfS+tkSi3mAbfjYBGh4JnyfdwkQBfIVf7t8gs9eSX/CntxUMvtKdG2MQ==", + "requires": { + "@formatjs/fast-memoize": "^2.2.0", + "intl-messageformat": "^10.5.14" + } + }, + "use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "requires": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + } + }, + "w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "requires": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + } + }, + "which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + } + }, + "which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "requires": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + } + }, + "which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + } + }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "dependencies": { + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + } + } + }, + "ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "dev": true, + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==" + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, + "yup": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", + "requires": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" } } } diff --git a/Front-End/package.json b/Front-End/package.json index c880101..a98022e 100644 --- a/Front-End/package.json +++ b/Front-End/package.json @@ -1,39 +1,54 @@ { "name": "n3wt-school-front-end", - "version": "0.0.1", + "version": "0.0.2", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", - "check-strings": "node scripts/check-hardcoded-strings.js" + "lint-light": "next lint --quiet", + "check-strings": "node scripts/check-hardcoded-strings.js", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" }, "dependencies": { + "@docuseal/react": "^1.0.56", "@radix-ui/react-dialog": "^1.1.2", "@tailwindcss/forms": "^0.5.9", "date-fns": "^4.1.0", + "dayjs": "^1.11.13", "framer-motion": "^11.11.11", "ics": "^3.8.1", + "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "lucide-react": "^0.453.0", "next": "14.2.11", + "next-auth": "^4.24.11", "next-intl": "^3.24.0", + "next-logger": "^5.0.1", + "pino": "^9.6.0", "react": "^18", + "react-circular-progressbar": "^2.2.0", "react-cookie": "^7.2.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18", - "react-phone-number-input": "^3.4.8", + "react-international-phone": "^4.5.0", + "react-quill": "^2.0.0", "react-tooltip": "^5.28.0" }, "devDependencies": { - "@babel/parser": "^7.26.2", - "@babel/traverse": "^7.25.9", + "@testing-library/react": "^13.4.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/user-event": "^14.4.3", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "autoprefixer": "^10.4.20", "eslint": "^8", "eslint-config-next": "14.2.11", "postcss": "^8.4.47", "tailwindcss": "^3.4.14" } -} +} \ No newline at end of file diff --git a/Front-End/postcss.config.js b/Front-End/postcss.config.js index 33ad091..12a703d 100644 --- a/Front-End/postcss.config.js +++ b/Front-End/postcss.config.js @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/Front-End/prod.env b/Front-End/prod.env new file mode 100644 index 0000000..d83185a --- /dev/null +++ b/Front-End/prod.env @@ -0,0 +1,5 @@ +NEXT_PUBLIC_API_URL=_NEXT_PUBLIC_API_URL_ +NEXT_PUBLIC_WSAPI_URL=_NEXT_PUBLIC_WSAPI_URL_ +NEXT_PUBLIC_USE_FAKE_DATA=_NEXT_PUBLIC_USE_FAKE_DATA_ +AUTH_SECRET=_AUTH_SECRET_ +NEXTAUTH_URL=_NEXTAUTH_URL_ \ No newline at end of file diff --git a/Front-End/project.inlang/.gitignore b/Front-End/project.inlang/.gitignore deleted file mode 100644 index 5e46596..0000000 --- a/Front-End/project.inlang/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cache \ No newline at end of file diff --git a/Front-End/project.inlang/project_id b/Front-End/project.inlang/project_id deleted file mode 100644 index f461c3d..0000000 --- a/Front-End/project.inlang/project_id +++ /dev/null @@ -1 +0,0 @@ -2ff5cbbb4bc1c6d178400871dfa342ac4f0b18e9b86cb64a1110be1ec54238c1 \ No newline at end of file diff --git a/Front-End/project.inlang/settings.json b/Front-End/project.inlang/settings.json deleted file mode 100644 index fb6c9ff..0000000 --- a/Front-End/project.inlang/settings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - // official schema ensures that your project file is valid - "$schema": "https://inlang.com/schema/project-settings", - // the "source" language tag that is used in your project - "sourceLanguageTag": "fr", - // all the language tags you want to support in your project - "languageTags": ["fr", "en"], - "modules": [ - "https://cdn.jsdelivr.net/npm/@inlang/plugin-json@4/dist/index.js" - ], // or use another storage module: https://inlang.com/c/plugins (i18next, json, inlang message format) - "settings": {} -} \ No newline at end of file diff --git a/Front-End/scripts/check-hardcoded-strings.js b/Front-End/scripts/check-hardcoded-strings.js index db1a771..1eab648 100644 --- a/Front-End/scripts/check-hardcoded-strings.js +++ b/Front-End/scripts/check-hardcoded-strings.js @@ -29,7 +29,7 @@ const TAILWIND_PATTERNS = [ // États /^(hover|focus|active|disabled|group|dark):/, // Couleurs - /-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-[0-9]+$/ + /-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-[0-9]+$/, ]; // Nouveaux patterns pour ignorer les logs et imports @@ -58,7 +58,7 @@ function isHardcodedString(str) { // Vérifier si la chaîne fait partie d'un console.log ou d'un import const context = str.trim(); - if (CODE_PATTERNS.some(pattern => pattern.test(context))) { + if (CODE_PATTERNS.some((pattern) => pattern.test(context))) { return false; } @@ -68,19 +68,20 @@ function isHardcodedString(str) { } // Vérifier si c'est une chaîne dans un import - if (context.includes('from \'') || context.includes('from "')) { + if (context.includes("from '") || context.includes('from "')) { return false; } // Vérifier si c'est une classe Tailwind const classes = str.split(' '); - if (classes.some(cls => - TAILWIND_PATTERNS.some(pattern => pattern.test(cls)) - )) { + if ( + classes.some((cls) => + TAILWIND_PATTERNS.some((pattern) => pattern.test(cls)) + ) + ) { return false; } - // Autres patterns à ignorer const IGNORE_PATTERNS = [ /^[A-Z][A-Za-z]+$/, // Noms de composants @@ -95,7 +96,7 @@ function isHardcodedString(str) { /^className=/, // className attributes ]; - return !IGNORE_PATTERNS.some(pattern => pattern.test(str)); + return !IGNORE_PATTERNS.some((pattern) => pattern.test(str)); } async function scanFile(filePath) { @@ -106,7 +107,7 @@ async function scanFile(filePath) { const ast = babel.parse(content, { sourceType: 'module', plugins: ['jsx', 'typescript'], - locations: true // Active le tracking des positions + locations: true, // Active le tracking des positions }); traverse(ast, { @@ -140,7 +141,11 @@ async function scanDirectory(dir) { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); - if (stat.isDirectory() && !file.startsWith('.') && file !== 'node_modules') { + if ( + stat.isDirectory() && + !file.startsWith('.') && + file !== 'node_modules' + ) { Object.assign(results, await scanDirectory(filePath)); } else if ( stat.isFile() && @@ -189,4 +194,4 @@ async function main() { await logStringsToFile(results); } -main().catch(console.error); \ No newline at end of file +main().catch(console.error); diff --git a/Front-End/src/app/500.js b/Front-End/src/app/500.js new file mode 100644 index 0000000..17758f0 --- /dev/null +++ b/Front-End/src/app/500.js @@ -0,0 +1,21 @@ +import Link from 'next/link'; +import Logo from '../components/Logo'; + +export default function Custom500() { + return ( +
+
+ +

+ 500 | Erreur interne +

+

+ Une erreur interne est survenue. +

+ + Retour Accueil + +
+
+ ); +} diff --git a/Front-End/src/app/[locale]/admin/classes/page.js b/Front-End/src/app/[locale]/admin/classes/page.js deleted file mode 100644 index 30b6a9d..0000000 --- a/Front-End/src/app/[locale]/admin/classes/page.js +++ /dev/null @@ -1,49 +0,0 @@ -'use client' -import React, { useState, useEffect } from 'react'; -import Table from '@/components/Table'; -import Button from '@/components/Button'; - -const columns = [ - { name: 'Nom', transform: (row) => row.Nom }, - { name: 'Niveau', transform: (row) => row.Niveau }, - { name: 'Effectif', transform: (row) => row.Effectif }, -]; - -export default function Page() { - const [classes, setClasses] = useState([]); - const [currentPage, setCurrentPage] = useState(1); - const [totalPages, setTotalPages] = useState(1); - - useEffect(() => { - fetchClasses(); - }, [currentPage]); - - const fetchClasses = async () => { - const fakeData = { - classes: [ - { Nom: 'Classe A', Niveau: '1ère année', Effectif: 30 }, - { Nom: 'Classe B', Niveau: '2ème année', Effectif: 25 }, - { Nom: 'Classe C', Niveau: '3ème année', Effectif: 28 }, - ], - totalPages: 3 - }; - setClasses(fakeData.classes); - setTotalPages(fakeData.totalPages); - }; - - const handlePageChange = (page) => { - setCurrentPage(page); - }; - - const handleCreateClass = () => { - console.log('Créer une nouvelle classe'); - }; - - return ( -
-

Gestion des Classes

-