commit 53586dff0a0172e8fc4e85ca130c08625ecea311 Author: Maksim Skobaro Date: Thu Jul 18 12:40:05 2024 +0300 changes. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a42b2df --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ +/logs/app.log +/configurations/Start RDBMS.run.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec0ad8a --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Thesis Defense Management System (TDMS) + +# Server module +Contains backend server of service +> After building executable JAR file will be in `tdms/server/target` folder +> +> To start:`java -jar ` +> +> URL is: `http://localhost:8080` + +# Web module +Contains frontend part of service +> go to `tdms/web/` folder +> +> To start: `npm run dev` +> +> URL you will see in console + +# How to build +1. Install `Maven` +2. Install `Java 17` + +> While building, **web** project compiles frontend, and **server** project automatically copies its to `/server/target/classes/static` and into generated `` + +> In IntelliJ Idea you can run `Execute maven goal`, to write further commands + +| Description | Command | +|---------------------------|--------------------------------------| +| Clear & Run tests & Build | `mvn clear install` | +| Run tests & build | `mvn install` | +| Skip tests & build | `mvn install -Dmaven.test.skip=true` | +>To use multithreading during build process use `-T ` keyword + +### There is final result (add `clear` if you need) +```shell +mvn install -Dmaven.test.skip=true -T 4 +``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d89bc5a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +services: + db: + image: postgres:16.2-alpine3.19 + environment: + - "POSTGRES_DB=tdms" + - "POSTGRES_PASSWORD=root" + - "POSTGRES_USER=root" + ports: + - "5400:5432" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5a72f33 --- /dev/null +++ b/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + TDMS + Thesis-Defense-Management-System + 0.0.1 + + + + org.springframework.boot + spring-boot-starter-parent + 3.3.2 + + + + ru.mskobaro + tdms + 0.0.1 + pom + + ${name} + ${description} + + + web + server + + diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..ea78ef0 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,2 @@ +/target +/logs/app.log diff --git a/server/pom.xml b/server/pom.xml new file mode 100644 index 0000000..12901ed --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,158 @@ + + + 4.0.0 + + + ru.mskobaro + tdms + 0.0.1 + + + server + 0.0.1 + + TDMS::SERVER + + + 23 + 23 + UTF-8 + 1.18.34 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.flywaydb + flyway-core + + + org.flywaydb + flyway-database-postgresql + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-web + + + org.postgresql + postgresql + runtime + + + org.projectlombok + lombok + ${lombok.version} + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-testcontainers + test + + + org.springframework.security + spring-security-test + test + + + org.testcontainers + junit-jupiter + test + + + org.testcontainers + postgresql + test + + + org.springframework.boot + spring-boot-starter-actuator + + + org.apache.commons + commons-lang3 + 3.14.0 + + + org.apache.commons + commons-collections4 + 4.4 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + true + + + org.projectlombok + lombok + ${lombok.version} + + + 23 + 23 + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + deploy frontend + process-resources + + copy-resources + + + target/classes/static + + + ../web/dist + true + + + + + + + + + diff --git a/server/src/main/java/ru/mskobaro/tdms/TdmsApplication.java b/server/src/main/java/ru/mskobaro/tdms/TdmsApplication.java new file mode 100644 index 0000000..96357ab --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/TdmsApplication.java @@ -0,0 +1,21 @@ +package ru.mskobaro.tdms; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.Environment; + + +@SpringBootApplication +@Slf4j +public class TdmsApplication { + public static void main(String[] args) { + Thread.currentThread().setName("spring-bootstrapper"); + ConfigurableApplicationContext ctx = SpringApplication.run(TdmsApplication.class, args); + Environment environment = ctx.getEnvironment(); + log.info("Server listening on port: {}", environment.getProperty("server.port")); + ctx.start(); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/AuditInfo.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/AuditInfo.java new file mode 100644 index 0000000..2e43ca9 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/AuditInfo.java @@ -0,0 +1,21 @@ +package ru.mskobaro.tdms.business.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.Getter; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.time.LocalDateTime; +import java.time.ZonedDateTime; + +@Embeddable +@Getter +public class AuditInfo { + @CreationTimestamp + @Column(name = "created_at") + private LocalDateTime createdAt; + @UpdateTimestamp + @Column(name = "updated_at") + private LocalDateTime updatedAt; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/CommissionMemberData.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/CommissionMemberData.java new file mode 100644 index 0000000..3e283ee --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/CommissionMemberData.java @@ -0,0 +1,25 @@ +package ru.mskobaro.tdms.business.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "commission_member_data") +@Getter +@Setter +public class CommissionMemberData { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "partic_id", referencedColumnName = "id") + private Participant participant; + + private String workPlace; + private String workPosition; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/Defense.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/Defense.java new file mode 100644 index 0000000..795a1ee --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/Defense.java @@ -0,0 +1,40 @@ +package ru.mskobaro.tdms.business.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDate; +import java.util.List; + +@Entity +@Table(name = "defense") +@Getter +@Setter +public class Defense { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToMany + @JoinTable( + name = "defense_commission", + joinColumns = @JoinColumn(name = "defense_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "commission_member_data_id", referencedColumnName = "id")) + private List commissionMembers; + + private LocalDate defenseDate; + + @OneToMany(mappedBy = "defense") + private List groups; + + @ManyToMany + @JoinTable( + name = "defense_best_student_works", + joinColumns = @JoinColumn(name = "defense_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "student_data_id", referencedColumnName = "id")) + private List bestWorks; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/DiplomaTopic.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/DiplomaTopic.java new file mode 100644 index 0000000..e8652d2 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/DiplomaTopic.java @@ -0,0 +1,35 @@ +package ru.mskobaro.tdms.business.entity; + + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + + +@Getter +@Setter +@ToString +@Entity +@Table(name = "diploma_topic") +public class DiplomaTopic { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name") + private String name; + + @ManyToOne + @JoinColumn(name = "teacher_id") + private TeacherData teacher; + + @ManyToOne + @JoinColumn(name = "direction_of_preparation_id") + private DirectionOfPreparation directionOfPreparation; + + @Embedded + private AuditInfo auditInfo; +} + diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/DirectionOfPreparation.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/DirectionOfPreparation.java new file mode 100644 index 0000000..e39483c --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/DirectionOfPreparation.java @@ -0,0 +1,27 @@ +package ru.mskobaro.tdms.business.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Entity +@Table(name = "direction_of_preparation") +@Getter +@Setter +public class DirectionOfPreparation { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private String code; + + @OneToMany(mappedBy = "directionOfPreparation") + private List diplomaTopic; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/Group.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/Group.java new file mode 100644 index 0000000..4a93a33 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/Group.java @@ -0,0 +1,40 @@ +package ru.mskobaro.tdms.business.entity; + + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.ArrayList; +import java.util.List; + + +@Getter +@Setter +@ToString +@Entity +@Table(name = "`group`") +public class Group { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name") + private String name; + + @OneToMany(mappedBy = "group") + private List students = new ArrayList<>(); + + @ManyToOne + @JoinColumn(name = "direction_of_preparation_id") + private DirectionOfPreparation directionOfPreparation; + + @ManyToOne + @JoinColumn(name = "defense_id") + private Defense defense; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/Participant.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/Participant.java new file mode 100644 index 0000000..dd63780 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/Participant.java @@ -0,0 +1,67 @@ +package ru.mskobaro.tdms.business.entity; + +import io.micrometer.common.util.StringUtils; +import jakarta.persistence.*; +import lombok.*; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Entity +@Table(name = "participant") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class Participant { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String firstName; + private String lastName; + private String middleName; + + private String email; + + private String numberPhone; + + @OneToOne(mappedBy = "participant") + private User user; + + private boolean deleted; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable( + name = "participant_role", + joinColumns = @JoinColumn(name = "partic_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) + private List roles; + + @Embedded + private AuditInfo auditInfo; + + @Transient + public String getFullName() { + return Stream.of(lastName, firstName, middleName) + .filter(StringUtils::isNotBlank) + .collect(Collectors.joining(" ")); + } + + @Transient + public String getShortName() { + StringBuilder builder = new StringBuilder(); + if (StringUtils.isNotBlank(lastName)) { + builder.append(lastName); + } + if (StringUtils.isNotBlank(firstName)) { + builder.append(" ").append(firstName.charAt(0)).append("."); + } + if (StringUtils.isNotBlank(middleName)) { + builder.append(" ").append(middleName.charAt(0)).append("."); + } + return builder.toString().trim(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/Role.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/Role.java new file mode 100644 index 0000000..195db27 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/Role.java @@ -0,0 +1,27 @@ +package ru.mskobaro.tdms.business.entity; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; +import org.springframework.security.core.GrantedAuthority; + + +@Getter +@ToString +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "`role`") +@EqualsAndHashCode(of = "id") +public class Role implements GrantedAuthority { + @Id + @Column(name = "id") + private Long id; + @Column(name = "name") + private String name; + @Column(name = "authority") + private String authority; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/StudentData.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/StudentData.java new file mode 100644 index 0000000..dde5f67 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/StudentData.java @@ -0,0 +1,50 @@ +package ru.mskobaro.tdms.business.entity; + + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import ru.mskobaro.tdms.integration.database.TeacherDataRepository; + + +@Getter +@Setter +@ToString(exclude = "group") +@Entity +@Table(name = "student_data") +public class StudentData { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "partic_id") + private Participant participant; + + @ManyToOne + @JoinColumn(name = "group_id") + private Group group; + + @ManyToOne + @JoinColumn(name = "study_form_id") + private StudyForm form; + + private Integer protectionOrder; + private Integer protectionDay; + + private Integer markComment; + private Integer markPractice; + + @ManyToOne + @JoinColumn(name = "curator_id") + private TeacherData curator; + + @ManyToOne + @JoinColumn(name = "diploma_topic_id") + private DiplomaTopic diplomaTopic; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/StudyForm.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/StudyForm.java new file mode 100644 index 0000000..98ff818 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/StudyForm.java @@ -0,0 +1,21 @@ +package ru.mskobaro.tdms.business.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "study_form") +@Getter +@Setter +public class StudyForm { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name") + private String name; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/Task.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/Task.java new file mode 100644 index 0000000..102a4f2 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/Task.java @@ -0,0 +1,41 @@ +package ru.mskobaro.tdms.business.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; +import ru.mskobaro.tdms.business.taskfields.TaskFields; + +@Entity +@Table(name = "task") +@NoArgsConstructor +@Getter +@Setter +public class Task { + public enum Type { + DIPLOMA_TOPIC_AGREEMENT, + } + + public enum Status { + WAIT_FOR_TOPIC_AGREEMENT, + DONE, + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + private Type type; + + @Enumerated(EnumType.STRING) + private Status status; + + @JdbcTypeCode(SqlTypes.JSON) + private TaskFields fields; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/TeacherData.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/TeacherData.java new file mode 100644 index 0000000..6c7f0a7 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/TeacherData.java @@ -0,0 +1,24 @@ +package ru.mskobaro.tdms.business.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "teacher_data") +@Getter +@Setter +public class TeacherData { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "participant_id", referencedColumnName = "id") + private Participant participant; + + private String degree; + + @Embedded + private AuditInfo auditInfo; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/entity/User.java b/server/src/main/java/ru/mskobaro/tdms/business/entity/User.java new file mode 100644 index 0000000..c98dcda --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/entity/User.java @@ -0,0 +1,56 @@ +package ru.mskobaro.tdms.business.entity; + + +import jakarta.persistence.*; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; + + +@Getter +@Setter +@ToString +@EqualsAndHashCode(of = "id") +@Entity +@Table(name = "`user`") +public class User implements UserDetails { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "login") + private String login; + @Column(name = "password") + private String password; + @OneToOne + @JoinColumn(name = "partic_id") + private Participant participant; + + @Embedded + private AuditInfo auditInfo; + + @Override + public Collection getAuthorities() { + return participant.getRoles().stream() + .map(Role::getAuthority) + .map(SimpleGrantedAuthority::new) + .toList(); + } + + @Override + public boolean isEnabled() { + return !participant.isDeleted(); + } + + @Override + public String getUsername() { + return login; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/exception/AccessDeniedException.java b/server/src/main/java/ru/mskobaro/tdms/business/exception/AccessDeniedException.java new file mode 100644 index 0000000..d68250c --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/exception/AccessDeniedException.java @@ -0,0 +1,18 @@ +package ru.mskobaro.tdms.business.exception; + +import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO; + +public class AccessDeniedException extends BusinessException { + public AccessDeniedException() { + super("Access denied"); + } + + public AccessDeniedException(String message) { + super(message); + } + + @Override + public ErrorDTO.ErrorCode getErrorCode() { + return ErrorDTO.ErrorCode.ACCESS_DENIED; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/exception/BusinessException.java b/server/src/main/java/ru/mskobaro/tdms/business/exception/BusinessException.java new file mode 100644 index 0000000..de2b01c --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/exception/BusinessException.java @@ -0,0 +1,13 @@ +package ru.mskobaro.tdms.business.exception; + +import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO; + +public class BusinessException extends RuntimeException { + public BusinessException(String message) { + super(message); + } + + public ErrorDTO.ErrorCode getErrorCode() { + return ErrorDTO.ErrorCode.BUSINESS_ERROR; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/exception/NotFoundException.java b/server/src/main/java/ru/mskobaro/tdms/business/exception/NotFoundException.java new file mode 100644 index 0000000..afd2631 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/exception/NotFoundException.java @@ -0,0 +1,34 @@ +package ru.mskobaro.tdms.business.exception; + +import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO; + +public class NotFoundException extends BusinessException { + public NotFoundException(Class entityClass, Object id) { + super(entityClass.getSimpleName() + " с идентификатором " + id + " не существует"); + } + + public NotFoundException(Class entityClass) { + super(entityClass.getSimpleName() + " не существует"); + } + + public NotFoundException() { + super("Не существует"); + } + + public static void throwIfNull(Object object) { + if (object == null) { + throw new NotFoundException(); + } + } + + public static void throwIfNull(Object object, Class entityClass, Object id) { + if (object == null) { + throw new NotFoundException(entityClass, id); + } + } + + @Override + public ErrorDTO.ErrorCode getErrorCode() { + return ErrorDTO.ErrorCode.NOT_FOUND; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/AuthenticationService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/AuthenticationService.java new file mode 100644 index 0000000..6bdc862 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/AuthenticationService.java @@ -0,0 +1,61 @@ +package ru.mskobaro.tdms.business.service; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY; + +@Service +@Slf4j +public class AuthenticationService { + @Autowired + private HttpServletRequest request; + @Autowired + private AuthenticationManager authenticationManager; + @Autowired + private SessionRegistry sessionRegistry; + + public void logout() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return; + } + + String username = authentication.getName(); + + HttpSession session = request.getSession(false); + if (session != null) { + session.invalidate(); + } + + SecurityContextHolder.clearContext(); + + log.info("User {} logged out", username); + } + + public void logout(String username) { + sessionRegistry.getAllSessions(username, false).forEach(session -> { + log.info("Invalidating session for user {}: {}", username, session.getSessionId()); + session.expireNow(); + sessionRegistry.removeSessionInformation(session.getSessionId()); + }); + } + + @Transactional + public void login(String username, String password) { + var token = new UsernamePasswordAuthenticationToken(username, password); + var authenticated = authenticationManager.authenticate(token); + SecurityContextHolder.getContext().setAuthentication(authenticated); + request.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()); + log.info("User {} logged in, ip: {}", username, request.getRemoteAddr()); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/DefenceService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/DefenceService.java new file mode 100644 index 0000000..1510eca --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/DefenceService.java @@ -0,0 +1,20 @@ +package ru.mskobaro.tdms.business.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mskobaro.tdms.integration.database.DefenceRepository; +import ru.mskobaro.tdms.presentation.controller.payload.DefenceDTO; + +import java.util.List; + +@Service +@Transactional +public class DefenceService { + @Autowired + private DefenceRepository defenceRepository; + + public List getAllDefences() { + return defenceRepository.findAll().stream().map(DefenceDTO::from).toList(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/DiplomaTopicService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/DiplomaTopicService.java new file mode 100644 index 0000000..af341e7 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/DiplomaTopicService.java @@ -0,0 +1,55 @@ +package ru.mskobaro.tdms.business.service; + +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.mskobaro.tdms.business.entity.DiplomaTopic; +import ru.mskobaro.tdms.business.entity.DirectionOfPreparation; +import ru.mskobaro.tdms.business.entity.TeacherData; +import ru.mskobaro.tdms.integration.database.DiplomaTopicRepository; +import ru.mskobaro.tdms.integration.database.PreparationDirectionRepository; +import ru.mskobaro.tdms.integration.database.TeacherDataRepository; +import ru.mskobaro.tdms.presentation.controller.payload.DiplomaTopicDTO; + +import java.util.List; + +@Service +@Transactional +public class DiplomaTopicService { + @Autowired + private DiplomaTopicRepository diplomaTopicRepository; + @Autowired + private TeacherDataRepository teacherDataRepository; + @Autowired + private PreparationDirectionRepository preparationDirectionRepository; + + public List findAll() { + return diplomaTopicRepository.findAll(); + } + + public void save(DiplomaTopicDTO diplomaTopicDTO) { + boolean isEdit = diplomaTopicDTO.getId() != null; + DiplomaTopic diplomaTopic; + if (isEdit) { + diplomaTopic = diplomaTopicRepository.findByIdThrow(diplomaTopicDTO.getId()); + } else { + diplomaTopic = new DiplomaTopic(); + } + + diplomaTopic.setName(diplomaTopicDTO.getName()); + if (diplomaTopicDTO.getTeacher() != null) { + TeacherData teacherData = teacherDataRepository.findByIdThrow(diplomaTopicDTO.getTeacher().getId()); + diplomaTopic.setTeacher(teacherData); + } + if (diplomaTopicDTO.getPreparationDirection() != null) { + DirectionOfPreparation directionOfPreparation = preparationDirectionRepository.findByIdThrow(diplomaTopicDTO.getPreparationDirection().getId()); + diplomaTopic.setDirectionOfPreparation(directionOfPreparation); + } + + diplomaTopicRepository.save(diplomaTopic); + } + + public List findAllForStudent(Long studentId) { + return diplomaTopicRepository.findAllForStudentId(studentId); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/GroupService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/GroupService.java new file mode 100644 index 0000000..9518b84 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/GroupService.java @@ -0,0 +1,78 @@ +package ru.mskobaro.tdms.business.service; + +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import ru.mskobaro.tdms.business.entity.DirectionOfPreparation; +import ru.mskobaro.tdms.business.entity.Group; +import ru.mskobaro.tdms.business.entity.StudentData; +import ru.mskobaro.tdms.business.exception.BusinessException; +import ru.mskobaro.tdms.integration.database.GroupRepository; +import ru.mskobaro.tdms.integration.database.PreparationDirectionRepository; +import ru.mskobaro.tdms.integration.database.StudentDataRepository; +import ru.mskobaro.tdms.presentation.controller.payload.GroupDTO; +import ru.mskobaro.tdms.presentation.controller.payload.StudentDataDTO; + +import java.util.Collection; +import java.util.List; + +@Service +@Transactional +@Slf4j +public class GroupService { + @Autowired + private GroupRepository groupRepository; + @Autowired + private RoleService roleService; + @Autowired + private StudentDataRepository studentDataRepository; + @Autowired + private PreparationDirectionRepository preparationDirectionRepository; + + public Collection getAllGroups() { + List groups = groupRepository.findAll(); + return groups.stream().map(g -> GroupDTO.from(g, true)).toList(); + } + + public void save(@Valid GroupDTO groupDTO) { + boolean editMode = groupDTO.getId() != null; + log.info("Saving group: {}. Edit?: {}", groupDTO, editMode); + if (!editMode && groupRepository.existsByName(groupDTO.getName())) { + throw new BusinessException("Группа с именем " + groupDTO.getName() + " уже существует"); + } + + Group group = editMode ? groupRepository.findByIdThrow(groupDTO.getId()) : new Group(); + group.setName(groupDTO.getName()); + + studentDataRepository.findAllByGroup_Id(group.getId()).forEach(s -> { + s.setGroup(null); + studentDataRepository.save(s); + }); + group.getStudents().clear(); + + List studentIds = groupDTO.getStudents().stream().map(StudentDataDTO::getId).toList(); + if (!CollectionUtils.isEmpty(studentIds)) { + List students = studentDataRepository.findAllById(studentIds); + students.stream() + .filter(s -> roleService.isParticInAuthority(s.getParticipant(), RoleService.Authority.STUDENT)) + .forEach(s -> { + group.getStudents().add(s); + s.setGroup(group); + }); + } + + if (groupDTO.getPreparationDirection() != null) { + DirectionOfPreparation directionOfPreparation = preparationDirectionRepository.findByIdThrow(groupDTO.getPreparationDirection().getId()); + group.setDirectionOfPreparation(directionOfPreparation); + } + + groupRepository.save(group); + } + + public void deleteGroup(Long groupId) { + groupRepository.deleteById(groupId); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/ParticipantService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/ParticipantService.java new file mode 100644 index 0000000..0fe0974 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/ParticipantService.java @@ -0,0 +1,220 @@ +package ru.mskobaro.tdms.business.service; + +import io.micrometer.common.util.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mskobaro.tdms.business.entity.*; +import ru.mskobaro.tdms.business.exception.AccessDeniedException; +import ru.mskobaro.tdms.business.exception.BusinessException; +import ru.mskobaro.tdms.integration.database.*; +import ru.mskobaro.tdms.presentation.controller.payload.ParticipantSaveDTO; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Transactional +@Slf4j +public class ParticipantService { + @Autowired + private ParticipantRepository participantRepository; + @Autowired + private UserService userService; + @Autowired + private RoleService roleService; + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + private UserRepository userRepository; + @Autowired + private GroupRepository groupRepository; + @Autowired + private StudentDataRepository studentDataRepository; + @Autowired + private AuthenticationService authenticationService; + @Autowired + private TeacherDataRepository teacherDataRepository; + + public ParticipantService(ParticipantRepository participantRepository) { + this.participantRepository = participantRepository; + } + + public Collection getAllParticipants() { + return participantRepository.findAll(); + } + + public void saveParticipant(ParticipantSaveDTO participantSaveDTO) { + boolean editMode = participantSaveDTO.getId() != null; + log.info("Saving participant: {}. Edit: {}", participantSaveDTO, editMode); + Participant existingParticipant = null; + if (editMode) + existingParticipant = participantRepository.findByIdThrow(participantSaveDTO.getId()); + persistParticipant(participantSaveDTO, existingParticipant, editMode); + } + + private void persistParticipant(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode) { + Participant participant = !editMode ? new Participant() : existingParticipant; + User callerUser = userService.getCallerUser(); + if (callerUser == null) + throw new AccessDeniedException(); + + participant.setFirstName(participantSaveDTO.getFirstName()); + participant.setLastName(participantSaveDTO.getLastName()); + participant.setMiddleName(participantSaveDTO.getMiddleName()); + participant.setNumberPhone(participantSaveDTO.getNumberPhone()); + participant.setEmail(participantSaveDTO.getEmail()); + + List roles = persistRoles(participantSaveDTO, existingParticipant, editMode, callerUser, participant); + boolean credentialsChanged = persistUserData(participantSaveDTO, existingParticipant, editMode, participant); + persistStudentData(participantSaveDTO, existingParticipant, editMode, roles, participant); + persistTeacherData(participantSaveDTO, existingParticipant, editMode, roles, participant); + + // TODO: notification task + Participant saved = participantRepository.save(participant); + log.info("Participant saved: {}", saved.getFullName()); + + if (credentialsChanged) { + log.info("User {} changed credentials, logging out", saved.getUser().getUsername()); + authenticationService.logout(saved.getUser().getUsername()); + } + } + + private List persistRoles(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, User callerUser, Participant participant) { + boolean isAdmin = roleService.isParticInAuthority(callerUser.getParticipant(), RoleService.Authority.ADMIN); + boolean isSecretary = roleService.isParticInAuthority(callerUser.getParticipant(), RoleService.Authority.SECRETARY); + boolean isOwner = existingParticipant != null && existingParticipant.getUser() != null + && existingParticipant.getUser().getId().equals(callerUser.getId()); + if (!isAdmin && !isSecretary && !isOwner) + throw new AccessDeniedException(); + if (participantSaveDTO.getAuthorities() != null && participantSaveDTO.getAuthorities().contains(RoleService.Authority.ADMIN) && !isAdmin) + throw new AccessDeniedException("Недостаточно прав для назначения роли администратора"); + + List roles = participantSaveDTO.getAuthorities() != null + ? participantSaveDTO.getAuthorities() + .stream() + .map(roleService::getRoleByAuthority) + .collect(Collectors.toList()) + : null; + + if (editMode && isOwner && !isAdmin && roles != null && !ListUtils.isEqualList(roles, existingParticipant.getRoles())) { + throw new AccessDeniedException("Вы не можете изменять свои роли"); + } else if (roles != null) { + participant.setRoles(roles); + } + return roles; + } + + private boolean persistUserData(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, Participant participant) { + boolean credentialsChanged = false; + if (participantSaveDTO.getUserData() == null) { + return credentialsChanged; + } + + User user; + boolean wasBefore = false; + if (editMode && existingParticipant.getUser() != null) { + user = existingParticipant.getUser(); + wasBefore = true; + } else { + user = new User(); + } + + String login = participantSaveDTO.getUserData().getLogin(); + if (StringUtils.isNotBlank(login)) { + user.setLogin(login); + credentialsChanged = true; + } else if (!wasBefore) { + throw new BusinessException("Логин не может быть пустым"); + } + + String password = participantSaveDTO.getUserData().getPassword(); + if (StringUtils.isNotBlank(password)) { + user.setPassword(passwordEncoder.encode(password)); + credentialsChanged = true; + } else if (!wasBefore) { + throw new BusinessException("Пароль не может быть пустым"); + } + + user = userRepository.save(user); + participant.setUser(user); + user.setParticipant(participant); + return credentialsChanged; + } + + private void persistTeacherData(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, List roles, Participant participant) { + boolean shouldPersistTeacherData = participantSaveDTO.getTeacherData() != null && roles != null + && CollectionUtils.containsAny(roles, roleService.getRoleByAuthority(RoleService.Authority.TEACHER)); + if (!shouldPersistTeacherData) { + return; + } + + boolean alreadyExists = editMode && teacherDataRepository.existsByParticipant_IdAndParticipant_DeletedFalse(existingParticipant.getId()); + TeacherData teacherData; + if (alreadyExists) { + teacherData = teacherDataRepository.findByParticipant_Id(existingParticipant.getId()); + } else { + teacherData = new TeacherData(); + } + + teacherData.setDegree(participantSaveDTO.getTeacherData().getDegree()); + + teacherData = teacherDataRepository.save(teacherData); + teacherData.setParticipant(participant); + } + + private void persistStudentData(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, List roles, Participant participant) { + boolean shouldPersistStudentData = participantSaveDTO.getStudentData() != null && roles != null + && CollectionUtils.containsAny(roles, roleService.getRoleByAuthority(RoleService.Authority.STUDENT)); + if (!shouldPersistStudentData) { + return; + } + + boolean alreadyExists = editMode && studentDataRepository.existsByParticipant_IdAndParticipant_DeletedFalse(existingParticipant.getId()); + StudentData studentData; + if (alreadyExists) { + studentData = studentDataRepository.findStudentDataByParticipant_Id(existingParticipant.getId()); + } else { + studentData = new StudentData(); + } + + if (participantSaveDTO.getStudentData().getGroupId() != null) { + Group group = groupRepository.findByIdThrow(participantSaveDTO.getStudentData().getGroupId()); + studentData.setGroup(group); + if (!group.getStudents().contains(studentData)) + group.getStudents().add(studentData); + } else { + if (editMode) { + Group group = groupRepository.findByStudentsContaining(Collections.singletonList(studentData)); + if (group != null) + group.getStudents().remove(studentData); + } + studentData.setGroup(null); + } + + if (participantSaveDTO.getStudentData().getCuratorId() != null) { + TeacherData teacherData = teacherDataRepository.findByIdThrow(participantSaveDTO.getStudentData().getCuratorId()); + studentData.setCurator(teacherData); + } else { + studentData.setCurator(null); + } + + studentData = studentDataRepository.save(studentData); + studentData.setParticipant(participant); + } + + public void deleteParticipant(Long id) { + if (id == 1) { + throw new AccessDeniedException(); + } + + Participant partic = participantRepository.findByIdThrow(id); + partic.setDeleted(true); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/PreparationDirectionService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/PreparationDirectionService.java new file mode 100644 index 0000000..16437b3 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/PreparationDirectionService.java @@ -0,0 +1,34 @@ +package ru.mskobaro.tdms.business.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mskobaro.tdms.business.entity.DirectionOfPreparation; +import ru.mskobaro.tdms.integration.database.PreparationDirectionRepository; +import ru.mskobaro.tdms.presentation.controller.payload.PreparationDirectionDTO; + +import java.util.List; + +@Service +@Transactional +public class PreparationDirectionService { + @Autowired + private PreparationDirectionRepository preparationDirectionRepository; + + public List getAll() { + return preparationDirectionRepository.findAll(); + } + + public void save(PreparationDirectionDTO preparationDirectionDTO) { + boolean editMode = preparationDirectionDTO.getId() != null; + DirectionOfPreparation preparationDirection; + if (editMode) { + preparationDirection = preparationDirectionRepository.findByIdThrow(preparationDirectionDTO.getId()); + } else { + preparationDirection = new DirectionOfPreparation(); + } + preparationDirection.setName(preparationDirectionDTO.getName()); + preparationDirection.setCode(preparationDirectionDTO.getCode()); + preparationDirectionRepository.save(preparationDirection); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/RoleService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/RoleService.java new file mode 100644 index 0000000..a667413 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/RoleService.java @@ -0,0 +1,82 @@ +package ru.mskobaro.tdms.business.service; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mskobaro.tdms.business.entity.Participant; +import ru.mskobaro.tdms.business.entity.Role; +import ru.mskobaro.tdms.integration.database.RoleRepository; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Service +@Slf4j +public class RoleService { + @RequiredArgsConstructor + public enum Authority { + ADMIN("ROLE_ADMINISTRATOR"), + COMM_MEMBER("ROLE_COMMISSION_MEMBER"), + TEACHER("ROLE_TEACHER"), + PLAGIARISM_CHECKER("ROLE_PLAGIARISM_CHECKER"), + SECRETARY("ROLE_SECRETARY"), + STUDENT("ROLE_STUDENT"), + ; + + private final String authority; + + @JsonValue + public String getAuthority() { + return authority; + } + + @JsonCreator + public static Authority from(String authority) { + for (Authority value : values()) { + if (value.getAuthority().equals(authority)) { + return value; + } + } + throw new IllegalArgumentException("No such authority: " + authority); + } + } + + private final Map roles = new ConcurrentHashMap<>(); + + @Autowired + private RoleRepository roleRepository; + + @PostConstruct + @Transactional + public void bootstrapRolesCache() { + roleRepository.findAll().forEach(role -> roles.put(role.getAuthority(), role)); + log.info("Roles initialized: {}", roles); + } + + public Role getRoleByAuthority(Authority authority) { + return roles.get(authority.getAuthority()); + } + + public boolean isParticInAuthority(Participant participant, Authority authority) { + return participant.getRoles().stream() + .anyMatch(role -> role.getAuthority().equals(authority.getAuthority())); + } + + public boolean isParticInAnyAuthority(Participant participant, Authority... authority) { + return participant.getRoles().stream() + .anyMatch(role -> { + for (Authority auth : authority) { + if (role.getAuthority().equals(auth.getAuthority())) { + return true; + } + } + return false; + }); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/StudentDataService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/StudentDataService.java new file mode 100644 index 0000000..a10ff24 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/StudentDataService.java @@ -0,0 +1,29 @@ +package ru.mskobaro.tdms.business.service; + +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.mskobaro.tdms.business.entity.Participant; +import ru.mskobaro.tdms.business.entity.StudentData; +import ru.mskobaro.tdms.integration.database.StudentDataRepository; +import ru.mskobaro.tdms.integration.database.UserRepository; + +import java.util.Collection; +import java.util.List; + +@Service +@Transactional +@Slf4j +public class StudentDataService { + @Autowired + private StudentDataRepository studentDataRepository; + + public StudentData getStudentByParticIdThrow(Long particId) { + return studentDataRepository.findStudentDataByParticipant_Id(particId); + } + + public Collection getAllStudentsWithoutGroup() { + return studentDataRepository.findByGroupIsNull(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/SysInfoService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/SysInfoService.java new file mode 100644 index 0000000..38a068e --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/SysInfoService.java @@ -0,0 +1,14 @@ +package ru.mskobaro.tdms.business.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class SysInfoService { + @Value("${application.version}") + private String version; + + public String getVersion() { + return version; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/TaskService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/TaskService.java new file mode 100644 index 0000000..9075194 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/TaskService.java @@ -0,0 +1,63 @@ +package ru.mskobaro.tdms.business.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mskobaro.tdms.business.entity.StudentData; +import ru.mskobaro.tdms.business.entity.Task; +import ru.mskobaro.tdms.business.entity.User; +import ru.mskobaro.tdms.business.taskfields.DiplomaTopicAgreementTaskFields; +import ru.mskobaro.tdms.business.taskfields.TaskFields; +import ru.mskobaro.tdms.integration.database.StudentDataRepository; +import ru.mskobaro.tdms.integration.database.TaskRepository; +import ru.mskobaro.tdms.presentation.controller.TaskController; + +import java.util.List; + +@Service +@Transactional +public class TaskService { + @Autowired + private TaskRepository taskRepository; + @Autowired + private UserService userService; + @Autowired + private StudentDataRepository studentDataRepository; + + public void createTask(Task.Type type, Task.Status status, TaskFields taskFields) { + Task task = new Task(); + task.setType(type); + task.setStatus(status); + task.setFields(taskFields); + taskRepository.save(task); + } + + public Task findDiplomaTopicAgreementTaskCallerMaker() { + User user = userService.getCallerUser(); + List diplomaTopicAgreementTaskByMakerId = taskRepository.findDiplomaTopicAgreementTaskByMakerId( + user.getParticipant().getId(), Task.Type.DIPLOMA_TOPIC_AGREEMENT + ); + if (diplomaTopicAgreementTaskByMakerId.isEmpty()) { + return null; + } + + if (diplomaTopicAgreementTaskByMakerId.size() > 1) { + throw new IllegalStateException(); + } + + return diplomaTopicAgreementTaskByMakerId.get(0); + } + + public void createDiplomaAgreementTask(TaskController.DiplomaTopicAgreementDTO diplomaTopicAgreementDTO) { + DiplomaTopicAgreementTaskFields taskFields = new DiplomaTopicAgreementTaskFields(); + User user = userService.getCallerUser(); + StudentData studentData = studentDataRepository.findStudentDataByParticipant_Id(user.getParticipant().getId()); + + taskFields.setCheckerParticipantId(user.getParticipant().getId()); + taskFields.setDiplomaTopicId(diplomaTopicAgreementDTO.getDiplomaTopicId()); + taskFields.setDiplomaTopicName(diplomaTopicAgreementDTO.getDiplomaTopicName()); + taskFields.setCheckerParticipantId(studentData.getCurator().getId()); + + createTask(Task.Type.DIPLOMA_TOPIC_AGREEMENT, Task.Status.WAIT_FOR_TOPIC_AGREEMENT, taskFields); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/TeacherDataService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/TeacherDataService.java new file mode 100644 index 0000000..f873a85 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/TeacherDataService.java @@ -0,0 +1,30 @@ +package ru.mskobaro.tdms.business.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.mskobaro.tdms.business.entity.TeacherData; +import ru.mskobaro.tdms.business.exception.NotFoundException; +import ru.mskobaro.tdms.integration.database.TeacherDataRepository; + +import java.util.List; + +@Service +@Transactional +public class TeacherDataService { + @Autowired + private TeacherDataRepository teacherDataRepository; + + public List findAll() { + return teacherDataRepository.findAll(); + } + + public TeacherData getTeacherDataByParticipantId(Long participantId) { + TeacherData teacher = teacherDataRepository.findByParticipant_Id(participantId); + if (teacher == null) { + throw new NotFoundException(TeacherData.class, participantId); + } + + return teacher; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/service/UserService.java b/server/src/main/java/ru/mskobaro/tdms/business/service/UserService.java new file mode 100644 index 0000000..51fbd6d --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/service/UserService.java @@ -0,0 +1,50 @@ +package ru.mskobaro.tdms.business.service; + +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import ru.mskobaro.tdms.business.entity.User; +import ru.mskobaro.tdms.integration.database.UserRepository; +import ru.mskobaro.tdms.presentation.controller.payload.UserDTO; + +import java.util.List; + +@Service +@Transactional +@Slf4j +public class UserService implements UserDetailsService { + @Autowired + private UserRepository userRepository; + + @Override + public User loadUserByUsername(String username) throws UsernameNotFoundException { + log.debug("Loading user with username: {}", username); + User user = userRepository.findUserByLogin(username).orElseThrow( + () -> new UsernameNotFoundException("User with login " + username + " not found")); + log.debug("User with login {} loaded", username); + return user; + } + + public List getAllUsers() { + log.debug("Loading all users"); + List users = userRepository.findAll().stream() + .map(UserDTO::fromEntity) + .toList(); + log.info("{} users loaded", users.size()); + return users; + } + + public User getCallerUser() { + Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + if (!(principal instanceof User)) { + return null; + } + + return (User) principal; + } +} + diff --git a/server/src/main/java/ru/mskobaro/tdms/business/taskfields/DiplomaTopicAgreementTaskFields.java b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/DiplomaTopicAgreementTaskFields.java new file mode 100644 index 0000000..07fa3c3 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/DiplomaTopicAgreementTaskFields.java @@ -0,0 +1,11 @@ +package ru.mskobaro.tdms.business.taskfields; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class DiplomaTopicAgreementTaskFields extends MakerCheckerTaskFields { + private String diplomaTopicName; + private Long diplomaTopicId; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/taskfields/MakerCheckerTaskFields.java b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/MakerCheckerTaskFields.java new file mode 100644 index 0000000..dce60eb --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/MakerCheckerTaskFields.java @@ -0,0 +1,13 @@ +package ru.mskobaro.tdms.business.taskfields; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class MakerCheckerTaskFields extends MakerTaskFields { + private Long checkerParticipantId; + private LocalDateTime approvedAt; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/taskfields/MakerTaskFields.java b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/MakerTaskFields.java new file mode 100644 index 0000000..c27b1ac --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/MakerTaskFields.java @@ -0,0 +1,10 @@ +package ru.mskobaro.tdms.business.taskfields; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MakerTaskFields extends TaskFields { + private Long makerParticipantId; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/business/taskfields/TaskFields.java b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/TaskFields.java new file mode 100644 index 0000000..20b8a27 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/business/taskfields/TaskFields.java @@ -0,0 +1,10 @@ +package ru.mskobaro.tdms.business.taskfields; + +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class TaskFields { + private LocalDateTime createdAt; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/DefenceRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/DefenceRepository.java new file mode 100644 index 0000000..5736206 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/DefenceRepository.java @@ -0,0 +1,25 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.Defense; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.Optional; + +@Repository +public interface DefenceRepository extends JpaRepository { + default Defense findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(Defense.class, id)); + } + + @Override + @Query("SELECT d from Defense d " + + "left join fetch d.bestWorks bw " + + "left join fetch d.commissionMembers cm " + + "where d.id = :id " + + "and bw.participant.deleted = false " + + "and cm.participant.deleted = false") + Optional findById(Long id); +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/DiplomaTopicRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/DiplomaTopicRepository.java new file mode 100644 index 0000000..c9a6f20 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/DiplomaTopicRepository.java @@ -0,0 +1,29 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.DiplomaTopic; +import ru.mskobaro.tdms.business.entity.DirectionOfPreparation; +import ru.mskobaro.tdms.business.entity.StudentData; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface DiplomaTopicRepository extends JpaRepository { + default DiplomaTopic findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id)); + } + + @Override + @Query("SELECT d FROM DiplomaTopic d WHERE d.id = :id AND d.teacher.participant.deleted = false") + Optional findById(Long id); + + @Query("SELECT d FROM DiplomaTopic d " + + "inner join d.directionOfPreparation dp " + + "inner join StudentData sd on sd.id = :studentIdwhere " + + "where dp = sd.group.directionOfPreparation") + List findAllForStudentId(Long studentId); +} \ No newline at end of file diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/GroupRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/GroupRepository.java new file mode 100644 index 0000000..6421637 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/GroupRepository.java @@ -0,0 +1,27 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.Group; +import ru.mskobaro.tdms.business.entity.StudentData; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface GroupRepository extends JpaRepository { + default Group findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(Group.class, id)); + } + + @Override + @Query("SELECT g FROM Group g left join fetch g.students sd WHERE g.id = :id") + Optional findById(Long id); + + boolean existsByName(String name); + + @Query("SELECT g FROM Group g left join fetch g.students sd WHERE sd IN :students") + Group findByStudentsContaining(List students); +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/ParticipantRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/ParticipantRepository.java new file mode 100644 index 0000000..3fde395 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/ParticipantRepository.java @@ -0,0 +1,27 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.DiplomaTopic; +import ru.mskobaro.tdms.business.entity.Participant; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface ParticipantRepository extends JpaRepository { + + default Participant findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id)); + } + + @Override + @Query("SELECT p FROM Participant p WHERE p.id = :id AND p.deleted = false") + Optional findById(Long id); + + @Override + @Query("SELECT p from Participant p where p.deleted = false") + List findAll(); +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/PreparationDirectionRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/PreparationDirectionRepository.java new file mode 100644 index 0000000..98bc83e --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/PreparationDirectionRepository.java @@ -0,0 +1,13 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.DirectionOfPreparation; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +@Repository +public interface PreparationDirectionRepository extends JpaRepository { + default DirectionOfPreparation findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(DirectionOfPreparation.class, id)); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/RoleRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/RoleRepository.java new file mode 100644 index 0000000..5950a89 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/RoleRepository.java @@ -0,0 +1,9 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.Role; + +@Repository +public interface RoleRepository extends JpaRepository { +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/StudentDataRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/StudentDataRepository.java new file mode 100644 index 0000000..671fe19 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/StudentDataRepository.java @@ -0,0 +1,39 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.StudentData; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface StudentDataRepository extends JpaRepository { + default StudentData findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(StudentData.class, id)); + } + + @EntityGraph(type = EntityGraph.EntityGraphType.LOAD, attributePaths = {"group.students"}) + @Query("SELECT s FROM StudentData s join fetch s.participant p WHERE p.id = :id AND p.deleted = false") + StudentData findStudentDataByParticipant_Id(Long id); + + boolean existsByParticipant_IdAndParticipant_DeletedFalse(Long id); + + @Override + @EntityGraph(type = EntityGraph.EntityGraphType.LOAD, attributePaths = {"participant.roles"}) + @Query("SELECT s FROM StudentData s join fetch s.participant p WHERE s.id in :ids AND p.deleted = false") + List findAllById(Iterable ids); + + @Query("SELECT s FROM StudentData s join fetch s.participant p WHERE s.group is null and p.deleted = false") + List findByGroupIsNull(); + + @Query("SELECT s FROM StudentData s join fetch s.participant p join fetch s.group g WHERE g.id = :id AND p.deleted = false") + List findAllByGroup_Id(Long id); + + @Override + @Query("SELECT s FROM StudentData s join fetch s.participant p WHERE s.id = :id AND p.deleted = false") + Optional findById(Long id); +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/TaskRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/TaskRepository.java new file mode 100644 index 0000000..7d61ab8 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/TaskRepository.java @@ -0,0 +1,21 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.Task; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.List; + +@Repository +public interface TaskRepository extends JpaRepository { + default Task findByIdThrow(Long id) { + return findById(id).orElseThrow(() -> new NotFoundException(Task.class, id)); + } + + @Query(value = "SELECT t FROM task t " + + "WHERE t.type = :type " + + "and t.fields->>'makerParticipantId' = :id", nativeQuery = true) + List findDiplomaTopicAgreementTaskByMakerId(Long id, Task.Type type); +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/TeacherDataRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/TeacherDataRepository.java new file mode 100644 index 0000000..d4f415f --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/TeacherDataRepository.java @@ -0,0 +1,30 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.TeacherData; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface TeacherDataRepository extends JpaRepository { + default TeacherData findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(TeacherData.class, id)); + } + + @Override + @Query("SELECT t FROM TeacherData t WHERE t.id = :id AND t.participant.deleted = false") + Optional findById(Long id); + + boolean existsByParticipant_IdAndParticipant_DeletedFalse(Long id); + + @Query("SELECT t FROM TeacherData t WHERE t.participant.id = :id AND t.participant.deleted = false") + TeacherData findByParticipant_Id(Long id); + + @Override + @Query("select t from TeacherData t where t.participant.deleted = false") + List findAll(); +} diff --git a/server/src/main/java/ru/mskobaro/tdms/integration/database/UserRepository.java b/server/src/main/java/ru/mskobaro/tdms/integration/database/UserRepository.java new file mode 100644 index 0000000..03f466d --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/integration/database/UserRepository.java @@ -0,0 +1,23 @@ +package ru.mskobaro.tdms.integration.database; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +import ru.mskobaro.tdms.business.entity.User; +import ru.mskobaro.tdms.business.exception.NotFoundException; + +import java.util.Optional; + +@Repository +public interface UserRepository extends JpaRepository { + default User findByIdThrow(Long id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(User.class, id)); + } + + @Override + @Query("SELECT u from User u join fetch u.participant p where u.id = :id and p.deleted = false") + Optional findById(Long id); + + @Query("SELECT u from User u join fetch u.participant p where u.login = :login and p.deleted = false") + Optional findUserByLogin(String login); +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/DefenceController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/DefenceController.java new file mode 100644 index 0000000..0ef7a5c --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/DefenceController.java @@ -0,0 +1,22 @@ +package ru.mskobaro.tdms.presentation.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.mskobaro.tdms.business.service.DefenceService; +import ru.mskobaro.tdms.presentation.controller.payload.DefenceDTO; + +import java.util.List; + +@RestController +@RequestMapping("/api/v1/defence") +public class DefenceController { + @Autowired + private DefenceService defenceService; + + @GetMapping("/all") + public List getAllDefences() { + return defenceService.getAllDefences(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/DiplomaTopicController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/DiplomaTopicController.java new file mode 100644 index 0000000..7294156 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/DiplomaTopicController.java @@ -0,0 +1,34 @@ +package ru.mskobaro.tdms.presentation.controller; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.mskobaro.tdms.business.service.DiplomaTopicService; +import ru.mskobaro.tdms.presentation.controller.payload.DiplomaTopicDTO; + +import java.util.List; + + +@RestController +@RequestMapping("/api/v1/diploma-topic/") +@Validated +public class DiplomaTopicController { + @Autowired + private DiplomaTopicService diplomaTopicService; + + @GetMapping("/all") + public List getAll() { + return diplomaTopicService.findAll().stream().map(DiplomaTopicDTO::from).toList(); + } + + @PostMapping("/save") + public void save(@RequestBody DiplomaTopicDTO diplomaTopicDTO) { + diplomaTopicService.save(diplomaTopicDTO); + } + + @GetMapping("/all-for-student") + public List getAllForStudent(@RequestParam Long studentId) { + return diplomaTopicService.findAllForStudent(studentId).stream().map(DiplomaTopicDTO::from).toList(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/GroupController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/GroupController.java new file mode 100644 index 0000000..2eeb7e0 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/GroupController.java @@ -0,0 +1,31 @@ +package ru.mskobaro.tdms.presentation.controller; + +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.mskobaro.tdms.business.service.GroupService; +import ru.mskobaro.tdms.presentation.controller.payload.GroupDTO; + +import java.util.Collection; + +@RestController +@RequestMapping("/api/v1/group") +public class GroupController { + @Autowired + private GroupService groupService; + + @GetMapping("/get-all-groups") + public Collection getAllGroups() { + return groupService.getAllGroups(); + } + + @PostMapping("/save") + public void save(@RequestBody @Valid GroupDTO groupDTO) { + groupService.save(groupDTO); + } + + @PostMapping("/delete") + public void delete(@RequestParam(value = "id") Long groupId) { + groupService.deleteGroup(groupId); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/ParticipantController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/ParticipantController.java new file mode 100644 index 0000000..7a949c1 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/ParticipantController.java @@ -0,0 +1,35 @@ +package ru.mskobaro.tdms.presentation.controller; + +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.mskobaro.tdms.business.service.ParticipantService; +import ru.mskobaro.tdms.presentation.controller.payload.IdDto; +import ru.mskobaro.tdms.presentation.controller.payload.ParticipantDTO; +import ru.mskobaro.tdms.presentation.controller.payload.ParticipantSaveDTO; + +import java.util.Collection; + +@RestController +@RequestMapping("/api/v1/participant") +public class ParticipantController { + @Autowired + private ParticipantService participantService; + + @GetMapping("/get-all-participants") + public Collection getAllParticipants() { + return participantService.getAllParticipants().stream() + .map(ParticipantDTO::fromEntity) + .toList(); + } + + @PostMapping("/save") + public void registerParticipant(@Valid @RequestBody ParticipantSaveDTO participantSaveDTO) { + participantService.saveParticipant(participantSaveDTO); + } + + @PostMapping("/delete") + public void deleteParticipant(@RequestBody IdDto id) { + participantService.deleteParticipant(id.getId()); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/PreparationDirectionController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/PreparationDirectionController.java new file mode 100644 index 0000000..e2c06be --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/PreparationDirectionController.java @@ -0,0 +1,26 @@ +package ru.mskobaro.tdms.presentation.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.mskobaro.tdms.business.service.PreparationDirectionService; +import ru.mskobaro.tdms.presentation.controller.payload.PreparationDirectionDTO; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/prep-direction") +public class PreparationDirectionController { + @Autowired + private PreparationDirectionService preparationDirectionService; + + @GetMapping("/get-all") + public List getAll() { + return preparationDirectionService.getAll().stream().map(PreparationDirectionDTO::from).collect(Collectors.toList()); + } + + @PostMapping("save") + public void save(@RequestBody PreparationDirectionDTO preparationDirectionDTO) { + preparationDirectionService.save(preparationDirectionDTO); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/StudentController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/StudentController.java new file mode 100644 index 0000000..cd436f2 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/StudentController.java @@ -0,0 +1,33 @@ +package ru.mskobaro.tdms.presentation.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import ru.mskobaro.tdms.business.entity.StudentData; +import ru.mskobaro.tdms.business.service.StudentDataService; +import ru.mskobaro.tdms.presentation.controller.payload.GroupDTO; +import ru.mskobaro.tdms.presentation.controller.payload.StudentDataDTO; + +import java.util.Collection; + +@RestController +@RequestMapping("/api/v1/student/") +public class StudentController { + @Autowired + private StudentDataService studentDataService; + + @GetMapping(value = "/by-partic-id") + public StudentDataDTO getCurrentStudent(@RequestParam(value = "id") Long particId) { + StudentData studentData = studentDataService.getStudentByParticIdThrow(particId); + return StudentDataDTO.from(studentData, true); + } + + @GetMapping(value = "all-without-group") + public Collection getAllStudentsWithoutGroup() { + return studentDataService.getAllStudentsWithoutGroup().stream() + .map(sd -> StudentDataDTO.from(sd, true)) + .toList(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/SysInfoController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/SysInfoController.java new file mode 100644 index 0000000..eca4bf3 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/SysInfoController.java @@ -0,0 +1,21 @@ +package ru.mskobaro.tdms.presentation.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.mskobaro.tdms.business.service.SysInfoService; + +@RestController +@RequestMapping("/api/v1/sysinfo") +public class SysInfoController { + @Autowired + private SysInfoService sysInfoService; + + @GetMapping("/version") + public String getVersion() { + return sysInfoService.getVersion(); + } + + // @GetMapping("/status") +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/TaskController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/TaskController.java new file mode 100644 index 0000000..8266edf --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/TaskController.java @@ -0,0 +1,34 @@ +package ru.mskobaro.tdms.presentation.controller; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.mskobaro.tdms.business.service.TaskService; +import ru.mskobaro.tdms.presentation.controller.payload.TaskDto; + +@RestController +@RequestMapping("/api/v1/task") +public class TaskController { + @Autowired + private TaskService taskService; + + @GetMapping("/diploma-agreement-maker") + public TaskDto diplomaTopicAgreementMaker() { + return TaskDto.from(taskService.findDiplomaTopicAgreementTaskCallerMaker()); + } + + @PostMapping("/create-topic-agreement") + public void createDiplomaTopicAgreement(@RequestBody DiplomaTopicAgreementDTO diplomaTopicAgreementDTO) { + taskService.createDiplomaAgreementTask(diplomaTopicAgreementDTO); + } + + @NoArgsConstructor + @Getter + @Setter + public static class DiplomaTopicAgreementDTO { + private String diplomaTopicName; + private Long diplomaTopicId; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/TeacherDataController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/TeacherDataController.java new file mode 100644 index 0000000..5e712e3 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/TeacherDataController.java @@ -0,0 +1,28 @@ +package ru.mskobaro.tdms.presentation.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import ru.mskobaro.tdms.business.service.TeacherDataService; +import ru.mskobaro.tdms.presentation.controller.payload.TeacherDataDTO; + +import java.util.List; + +@RestController +@RequestMapping("/api/v1/teacher-data") +public class TeacherDataController { + @Autowired + private TeacherDataService teacherDataService; + + @GetMapping("/get-all") + public List getAllTeacherData() { + return teacherDataService.findAll().stream().map(TeacherDataDTO::from).toList(); + } + + @GetMapping("/by-partic-id") + public TeacherDataDTO getTeacherDataByParticipantId(@RequestParam Long id) { + return TeacherDataDTO.from(teacherDataService.getTeacherDataByParticipantId(id)); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/UserController.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/UserController.java new file mode 100644 index 0000000..b02b0be --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/UserController.java @@ -0,0 +1,41 @@ +package ru.mskobaro.tdms.presentation.controller; + +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.mskobaro.tdms.business.entity.User; +import ru.mskobaro.tdms.business.service.AuthenticationService; +import ru.mskobaro.tdms.business.service.UserService; +import ru.mskobaro.tdms.presentation.controller.payload.LoginDTO; +import ru.mskobaro.tdms.presentation.controller.payload.UserDTO; + +@RestController +@RequestMapping("/api/v1/user") +@Slf4j +public class UserController { + @Autowired + private AuthenticationService authenticationService; + @Autowired + private UserService userService; + + @GetMapping("/current") + public ResponseEntity getCurrentUser() { + User user = userService.getCallerUser(); + return user == null + ? ResponseEntity.status(HttpStatus.UNAUTHORIZED).build() + : ResponseEntity.ok(UserDTO.fromEntity(user)); + } + + @PostMapping("/logout") + public void logout() { + authenticationService.logout(); + } + + @PostMapping("/login") + public void login(@RequestBody @Valid LoginDTO loginDTO) { + authenticationService.login(loginDTO.getLogin(), loginDTO.getPassword()); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/CommissionMemberDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/CommissionMemberDTO.java new file mode 100644 index 0000000..2f5b95a --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/CommissionMemberDTO.java @@ -0,0 +1,31 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.mskobaro.tdms.business.entity.CommissionMemberData; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +public class CommissionMemberDTO { + private Long id; + private ParticipantDTO participant; + private String workPlace; + private String workPosition; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static CommissionMemberDTO from(CommissionMemberData commissionMemberData) { + CommissionMemberDTO dto = new CommissionMemberDTO(); + dto.setId(commissionMemberData.getId()); + dto.setParticipant(ParticipantDTO.fromEntity(commissionMemberData.getParticipant())); + dto.setWorkPlace(commissionMemberData.getWorkPlace()); + dto.setWorkPosition(commissionMemberData.getWorkPosition()); + dto.setCreatedAt(commissionMemberData.getAuditInfo().getCreatedAt()); + dto.setUpdatedAt(commissionMemberData.getAuditInfo().getUpdatedAt()); + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/DefenceDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/DefenceDTO.java new file mode 100644 index 0000000..767267d --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/DefenceDTO.java @@ -0,0 +1,39 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; +import ru.mskobaro.tdms.business.entity.Defense; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@Setter +public class DefenceDTO { + private Long id; + private List commissionMembers; + private List groups; + + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static DefenceDTO from(Defense defense) { + DefenceDTO dto = new DefenceDTO(); + dto.setId(defense.getId()); + + if (CollectionUtils.isNotEmpty(defense.getCommissionMembers())) { + dto.setCommissionMembers( + defense.getCommissionMembers().stream() + .map(CommissionMemberDTO::from) + .collect(Collectors.toList()) + ); + } + + dto.setGroups(defense.getGroups().stream().map(g -> GroupDTO.from(g, true)).toList()); + dto.setCreatedAt(defense.getAuditInfo().getCreatedAt()); + dto.setUpdatedAt(defense.getAuditInfo().getUpdatedAt()); + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/DiplomaTopicDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/DiplomaTopicDTO.java new file mode 100644 index 0000000..33f72a6 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/DiplomaTopicDTO.java @@ -0,0 +1,37 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import ru.mskobaro.tdms.business.entity.DiplomaTopic; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@ToString +public class DiplomaTopicDTO { + private Long id; + private String name; + private TeacherDataDTO teacher; + private PreparationDirectionDTO preparationDirection; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static DiplomaTopicDTO from(DiplomaTopic diplomaTopic) { + DiplomaTopicDTO dto = new DiplomaTopicDTO(); + dto.setId(diplomaTopic.getId()); + dto.setName(diplomaTopic.getName()); + if (diplomaTopic.getTeacher() != null) { + dto.setTeacher(TeacherDataDTO.from(diplomaTopic.getTeacher())); + } + if (diplomaTopic.getDirectionOfPreparation() != null) { + dto.setPreparationDirection(PreparationDirectionDTO.from(diplomaTopic.getDirectionOfPreparation())); + } + dto.setCreatedAt(diplomaTopic.getAuditInfo().getCreatedAt()); + dto.setUpdatedAt(diplomaTopic.getAuditInfo().getUpdatedAt()); + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ErrorDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ErrorDTO.java new file mode 100644 index 0000000..d099897 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ErrorDTO.java @@ -0,0 +1,20 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +public record ErrorDTO(String message, ErrorCode errorCode) { + @RequiredArgsConstructor + @Getter + public enum ErrorCode { + BUSINESS_ERROR(HttpStatus.BAD_REQUEST), + VALIDATION_ERROR(HttpStatus.BAD_REQUEST), + INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR), + NOT_FOUND(HttpStatus.NOT_FOUND), + ACCESS_DENIED(HttpStatus.FORBIDDEN), + ; + + private final HttpStatus httpStatus; + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/GroupDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/GroupDTO.java new file mode 100644 index 0000000..b8fcf21 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/GroupDTO.java @@ -0,0 +1,47 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import ru.mskobaro.tdms.business.entity.Group; + +import java.time.LocalDateTime; +import java.util.List; + +@Getter +@Setter +@ToString +public class GroupDTO { + private Long id; + @NotEmpty(message = "Имя группы не может быть пустым") + @Size(min = 3, max = 50, message = "Имя группы должно быть от 3 до 50 символов") + @Pattern(regexp = "^[а-яА-ЯёЁ0-9_-]*$", message = "Имя группы должно содержать только русские буквы, дефис, нижнее подчеркивание и цифры") + private String name; + private List students; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + private PreparationDirectionDTO preparationDirection; + + public static GroupDTO from(Group group, boolean includeStudents) { + GroupDTO dto = new GroupDTO(); + dto.setId(group.getId()); + dto.setName(group.getName()); + if (includeStudents && group.getStudents() != null) { + dto.setStudents( + group.getStudents() + .stream() + .filter(sd -> !sd.getParticipant().isDeleted()) + .map(sd -> StudentDataDTO.from(sd, false)) + .toList()); + } + if (group.getDirectionOfPreparation() != null) { + dto.setPreparationDirection(PreparationDirectionDTO.from(group.getDirectionOfPreparation())); + } + dto.setCreatedAt(group.getAuditInfo().getCreatedAt()); + dto.setUpdatedAt(group.getAuditInfo().getUpdatedAt()); + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/IdDto.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/IdDto.java new file mode 100644 index 0000000..98fd6cc --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/IdDto.java @@ -0,0 +1,14 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@NoArgsConstructor +@Getter +@Setter +@ToString +public class IdDto { + private Long id; +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/LoginDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/LoginDTO.java new file mode 100644 index 0000000..1fcf3eb --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/LoginDTO.java @@ -0,0 +1,18 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; + +@Getter +public class LoginDTO { + @NotEmpty(message = "Логин не может быть пустым") + @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Логин должен содержать только латинские буквы, цифры и знак подчеркивания") + @Size(min = 5, message = "Логин должен содержать минимум 5 символов") + @Size(max = 32, message = "Логин должен содержать максимум 32 символов") + private String login; + @NotEmpty(message = "Пароль не может быть пустым") + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "Пароль должен содержать хотя бы одну цифру, одну заглавную и одну строчную букву, минимум 8 символов") + private String password; +} \ No newline at end of file diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ParticipantDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ParticipantDTO.java new file mode 100644 index 0000000..fc7f4e9 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ParticipantDTO.java @@ -0,0 +1,48 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import ru.mskobaro.tdms.business.entity.Participant; + +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.List; + +@Getter +@Setter +@ToString +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class ParticipantDTO { + private Long id; + private String firstName; + private String lastName; + private String middleName; + private String email; + private String numberPhone; + private UserDTO user; + private List roles; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static ParticipantDTO fromEntity(Participant participant) { + ParticipantDTO participantDTO = new ParticipantDTO(); + participantDTO.setId(participant.getId()); + participantDTO.setFirstName(participant.getFirstName()); + participantDTO.setLastName(participant.getLastName()); + participantDTO.setMiddleName(participant.getMiddleName()); + participantDTO.setEmail(participant.getEmail()); + participantDTO.setNumberPhone(participant.getNumberPhone()); + participantDTO.setRoles(RoleDTO.from(participant)); + participantDTO.setCreatedAt(participant.getAuditInfo().getCreatedAt()); + participantDTO.setUpdatedAt(participant.getAuditInfo().getUpdatedAt()); + + if (participant.getUser() != null) { + participantDTO.setUser(UserDTO.fromEntity(participant.getUser(), participantDTO)); + } + + return participantDTO; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ParticipantSaveDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ParticipantSaveDTO.java new file mode 100644 index 0000000..a9b5fbb --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/ParticipantSaveDTO.java @@ -0,0 +1,64 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import lombok.Getter; +import lombok.ToString; +import ru.mskobaro.tdms.business.service.RoleService.Authority; + +import java.util.List; + +@Getter +@ToString +public class ParticipantSaveDTO { + private Long id; + @NotEmpty(message = "Имя не может быть пустым") + @Pattern(regexp = "^[a-zA-Zа-яА-ЯёЁ\\s-]+$", message = "Имя должно содержать только буквы английского или русского алфавита, пробелы и дефис") + private String firstName; + @NotEmpty(message = "Фамилия не может быть пустой") + @Pattern(regexp = "^[a-zA-Zа-яА-ЯёЁ\\s-]+$", message = "Фамилия должна содержать только буквы английского или русского алфавита, пробелы и дефис") + private String lastName; + private String middleName; + @NotNull(message = "Почта не может быть пустой") + @Email(message = "Почта должна быть валидным адресом электронной почты") + private String email; + @NotNull(message = "Номер телефона не может быть пустым") + @Pattern(regexp = "^\\+[1-9]\\d{6,14}$", message = "Номер телефона должен начинаться с '+' и содержать от 7 до 15 цифр") + private String numberPhone; + private List authorities; + + @Valid + private UserRegistrationDTO userData; + @Valid + private StudentRegistrationDTO studentData; + @Valid + private TeacherRegistrationDTO teacherData; + + @Getter + @ToString + public static class UserRegistrationDTO { + @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Логин должен содержать только латинские буквы, цифры и знак подчеркивания") + @Size(min = 5, message = "Логин должен содержать минимум 5 символов") + @Size(max = 32, message = "Логин должен содержать максимум 32 символов") + private String login; + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "Пароль должен содержать хотя бы одну цифру, одну заглавную и одну строчную букву, минимум 8 символов") + private String password; + } + + @Getter + @ToString + public static class StudentRegistrationDTO { + private Long groupId; + private Long curatorId; + private Long diplomaTopicId; + } + + @Getter + @ToString + public static class TeacherRegistrationDTO { + private List curatingGroups; + private List advisingStudents; + private String degree; + } + +} \ No newline at end of file diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/PreparationDirectionDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/PreparationDirectionDTO.java new file mode 100644 index 0000000..7568ed4 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/PreparationDirectionDTO.java @@ -0,0 +1,29 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.mskobaro.tdms.business.entity.DirectionOfPreparation; + +import java.time.LocalDateTime; + +@NoArgsConstructor +@Getter +@Setter +public class PreparationDirectionDTO { + private Long id; + private String name; + private String code; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public static PreparationDirectionDTO from(DirectionOfPreparation preparationDirection) { + PreparationDirectionDTO dto = new PreparationDirectionDTO(); + dto.setId(preparationDirection.getId()); + dto.setName(preparationDirection.getName()); + dto.setCode(preparationDirection.getCode()); + dto.setCreatedAt(preparationDirection.getAuditInfo().getCreatedAt()); + dto.setUpdatedAt(preparationDirection.getAuditInfo().getUpdatedAt()); + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/RoleDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/RoleDTO.java new file mode 100644 index 0000000..8192de3 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/RoleDTO.java @@ -0,0 +1,19 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + + +import ru.mskobaro.tdms.business.entity.Participant; +import ru.mskobaro.tdms.business.entity.Role; + +import java.util.List; + + +public record RoleDTO(String name, String code) { + + public static RoleDTO from(Role role) { + return new RoleDTO(role.getName(), role.getAuthority()); + } + + public static List from(Participant participant) { + return participant.getRoles().stream().map(RoleDTO::from).toList(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/StudentDataDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/StudentDataDTO.java new file mode 100644 index 0000000..bdd2173 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/StudentDataDTO.java @@ -0,0 +1,31 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.Setter; +import ru.mskobaro.tdms.business.entity.StudentData; + +@Setter +@Getter +public class StudentDataDTO { + private Long id; + private GroupDTO group; + private ParticipantDTO participant; + private TeacherDataDTO curator; + private DiplomaTopicDTO diplomaTopic; + + public static StudentDataDTO from(StudentData studentData, boolean includeGroup) { + StudentDataDTO dto = new StudentDataDTO(); + dto.setId(studentData.getId()); + if (includeGroup && studentData.getGroup() != null) { + dto.setGroup(GroupDTO.from(studentData.getGroup(), false)); + } + dto.setParticipant(ParticipantDTO.fromEntity(studentData.getParticipant())); + if (studentData.getCurator() != null) { + dto.setCurator(TeacherDataDTO.from(studentData.getCurator())); + } + if (studentData.getDiplomaTopic() != null) { + dto.setDiplomaTopic(DiplomaTopicDTO.from(studentData.getDiplomaTopic())); + } + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/TaskDto.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/TaskDto.java new file mode 100644 index 0000000..feba773 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/TaskDto.java @@ -0,0 +1,33 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.mskobaro.tdms.business.entity.Task; +import ru.mskobaro.tdms.business.taskfields.TaskFields; + +import java.time.LocalDateTime; + +@NoArgsConstructor +@Getter +@Setter +public class TaskDto { + private Long id; + private Task.Type type; + private Task.Status status; + private TaskFields fields; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + + public static TaskDto from(Task task) { + TaskDto dto = new TaskDto(); + dto.setId(task.getId()); + dto.setType(task.getType()); + dto.setStatus(task.getStatus()); + dto.setFields(task.getFields()); + dto.setCreatedAt(task.getAuditInfo().getCreatedAt()); + dto.setUpdatedAt(task.getAuditInfo().getUpdatedAt()); + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/TeacherDataDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/TeacherDataDTO.java new file mode 100644 index 0000000..f79ebe1 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/TeacherDataDTO.java @@ -0,0 +1,27 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.mskobaro.tdms.business.entity.TeacherData; + +@Getter +@Setter +@NoArgsConstructor +public class TeacherDataDTO { + private Long id; + private ParticipantDTO participant; + private String degree; + private String createdAt; + private String updatedAt; + + public static TeacherDataDTO from(TeacherData teacherData) { + TeacherDataDTO dto = new TeacherDataDTO(); + dto.setId(teacherData.getId()); + dto.setParticipant(ParticipantDTO.fromEntity(teacherData.getParticipant())); + dto.setDegree(teacherData.getDegree()); + dto.setCreatedAt(teacherData.getAuditInfo().getCreatedAt().toString()); + dto.setUpdatedAt(teacherData.getAuditInfo().getUpdatedAt().toString()); + return dto; + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/UserDTO.java b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/UserDTO.java new file mode 100644 index 0000000..59df214 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/controller/payload/UserDTO.java @@ -0,0 +1,41 @@ +package ru.mskobaro.tdms.presentation.controller.payload; + + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import lombok.Builder; +import ru.mskobaro.tdms.business.entity.User; + +import java.time.LocalDateTime; +import java.time.ZonedDateTime; + + +@Builder +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public record UserDTO( + Long id, + String login, + ParticipantDTO participant, + LocalDateTime createdAt, + LocalDateTime updatedAt +) { + public static UserDTO fromEntity(User user) { + return UserDTO.builder() + .id(user.getId()) + .login(user.getLogin()) + .participant(ParticipantDTO.fromEntity(user.getParticipant())) + .createdAt(user.getAuditInfo().getCreatedAt()) + .updatedAt(user.getAuditInfo().getUpdatedAt()) + .build(); + } + + public static UserDTO fromEntity(User user, ParticipantDTO participant) { + return UserDTO.builder() + .id(user.getId()) + .login(user.getLogin()) + .participant(participant) + .createdAt(user.getAuditInfo().getCreatedAt()) + .updatedAt(user.getAuditInfo().getUpdatedAt()) + .build(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/presentation/exception/ApplicationExceptionHandler.java b/server/src/main/java/ru/mskobaro/tdms/presentation/exception/ApplicationExceptionHandler.java new file mode 100644 index 0000000..84cbeb3 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/presentation/exception/ApplicationExceptionHandler.java @@ -0,0 +1,73 @@ +package ru.mskobaro.tdms.presentation.exception; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.security.core.AuthenticationException; +import org.springframework.validation.BindException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.resource.NoResourceFoundException; +import ru.mskobaro.tdms.business.exception.AccessDeniedException; +import ru.mskobaro.tdms.business.exception.BusinessException; +import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO; + +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.springframework.http.HttpStatus.*; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO.ErrorCode.*; + +@RestControllerAdvice +@Slf4j +public class ApplicationExceptionHandler { + @ExceptionHandler(BindException.class) + @ResponseStatus(BAD_REQUEST) + public ErrorDTO handleMethodArgumentNotValidException(BindException e) { + log.debug("Validation error: {}", e.getMessage()); + String validationErrors = e.getAllErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.joining("\n")); + return new ErrorDTO(validationErrors, VALIDATION_ERROR); + } + + @ExceptionHandler(BusinessException.class) + public ErrorDTO handleBusinessException(BusinessException e, HttpServletResponse response) { + log.warn("Business error: {}", e.getMessage()); + response.setStatus(e.getErrorCode().getHttpStatus().value()); + return new ErrorDTO(e.getMessage(), e.getErrorCode()); + } + + @ExceptionHandler({org.springframework.security.access.AccessDeniedException.class, AccessDeniedException.class}) + @ResponseStatus(FORBIDDEN) + public ErrorDTO handleAccessDeniedException(RuntimeException e) { + log.warn("Access denied: {}", e.getMessage()); + return new ErrorDTO("Доступ запрещен", ACCESS_DENIED); + } + + @ExceptionHandler(AuthenticationException.class) + @ResponseStatus(UNAUTHORIZED) + public ErrorDTO handleAuthenticationException(AuthenticationException e) { + log.warn("Authentication error: {}", e.getMessage()); + return new ErrorDTO("Неверный логин или пароль", ACCESS_DENIED); + } + + @ExceptionHandler(NoResourceFoundException.class) + @ResponseStatus(NOT_FOUND) + public ErrorDTO handleNoResourceFoundException(NoResourceFoundException e) { + UUID uuid = UUID.randomUUID(); + String message = e.getMessage().substring(0, e.getMessage().length() - 1); + log.error("{} ({})", message, uuid); + return new ErrorDTO("Идентификатор ошибки: (" + uuid + ")\nРесурс не был наеден, обратитесь к администратору", ErrorDTO.ErrorCode.NOT_FOUND); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(INTERNAL_SERVER_ERROR) + public ErrorDTO handleUnexpectedException(Exception e) { + UUID uuid = UUID.randomUUID(); + log.error("Unexpected exception ({})", uuid, e); + return new ErrorDTO("Идентификатор ошибки: (" + uuid + ")\nПроизошла непредвиденная ошибка, обратитесь к администратору", INTERNAL_ERROR); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/system/config/SecurityConfig.java b/server/src/main/java/ru/mskobaro/tdms/system/config/SecurityConfig.java new file mode 100644 index 0000000..78d945e --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/system/config/SecurityConfig.java @@ -0,0 +1,142 @@ +package ru.mskobaro.tdms.system.config; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.context.SecurityContextHolderFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import ru.mskobaro.tdms.system.web.LoggingRequestFilter; + +import java.time.Duration; +import java.util.List; + +import static ru.mskobaro.tdms.business.service.RoleService.Authority.ADMIN; +import static ru.mskobaro.tdms.business.service.RoleService.Authority.SECRETARY; + + +@Slf4j +@Configuration +public class SecurityConfig { + @Autowired + private Environment environment; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, + AuthenticationManager authenticationManager, + CorsConfigurationSource cors + ) throws Exception { + return httpSecurity + .addFilterAfter(new LoggingRequestFilter(), SecurityContextHolderFilter.class) + .authorizeHttpRequests(this::configureHttpAuthorization) + // .csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) + .csrf(AbstractHttpConfigurer::disable) + .cors(a -> a.configurationSource(cors)) + .authenticationManager(authenticationManager) + .sessionManagement(cfg -> { + cfg.sessionCreationPolicy(SessionCreationPolicy.NEVER); + cfg.maximumSessions(1); + }) + .build(); + } + + @Bean + @Primary + public CorsConfigurationSource corsConfiguration( + @Value("${application.domain}") String domain, + @Value("${application.port}") String port, + @Value("${application.protocol}") String protocol + ) { + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.setAllowedMethods(List.of(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.OPTIONS.name())); + corsConfiguration.setAllowedHeaders(List.of("Authorization", "Content-Type")); + corsConfiguration.setAllowCredentials(true); + corsConfiguration.setMaxAge(Duration.ofDays(1)); + corsConfiguration.addAllowedOrigin(StringUtils.join(protocol, "://", domain, ":", port)); + + if (environment.matchesProfiles("dev")) { + corsConfiguration.addAllowedOrigin("http://localhost:8888"); + } + + log.info("CORS configuration: [headers: {}, methods: {}, origins: {}, credentials: {}, maxAge: {}]", + corsConfiguration.getAllowedHeaders(), corsConfiguration.getAllowedMethods(), corsConfiguration.getAllowedOrigins(), + corsConfiguration.getAllowCredentials(), corsConfiguration.getMaxAge()); + return request -> corsConfiguration; + } + + @Bean + public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder()); + provider.setUserDetailsService(userDetailsService); + return new ProviderManager(provider); + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + + private void configureHttpAuthorization( + AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry httpAuthorization + ) { + httpAuthorization.requestMatchers("/api/v1/sysinfo/**").permitAll(); + + httpAuthorization.requestMatchers("/api/v1/user/logout").authenticated(); + httpAuthorization.requestMatchers("/api/v1/user/login").permitAll(); + httpAuthorization.requestMatchers("/api/v1/user/current").permitAll(); + + httpAuthorization.requestMatchers("/api/v1/participant/get-all-participants").permitAll(); + httpAuthorization.requestMatchers("/api/v1/participant/get-possible-curators").permitAll(); + // Сложная логика авторизации, так что проверяем явно в ParticipantService + httpAuthorization.requestMatchers("/api/v1/participant/save").authenticated(); + httpAuthorization.requestMatchers("/api/v1/participant/delete").hasAuthority(ADMIN.getAuthority()); + + httpAuthorization.requestMatchers("/api/v1/group/get-all-groups").permitAll(); + httpAuthorization.requestMatchers("/api/v1/group/save").hasAnyAuthority(ADMIN.getAuthority(), SECRETARY.getAuthority()); + httpAuthorization.requestMatchers("/api/v1/group/delete").hasAnyAuthority(ADMIN.getAuthority()); + + httpAuthorization.requestMatchers("/api/v1/student/by-partic-id").permitAll(); + httpAuthorization.requestMatchers("/api/v1/student/all-without-group").permitAll(); + + httpAuthorization.requestMatchers("/api/v1/teacher-data/get-all").permitAll(); + httpAuthorization.requestMatchers("/api/v1/teacher-data/by-partic-id").permitAll(); + + httpAuthorization.requestMatchers("/api/v1/prep-direction/get-all").permitAll(); + httpAuthorization.requestMatchers("/api/v1/prep-direction/save").hasAnyAuthority(ADMIN.getAuthority(), SECRETARY.getAuthority()); + + httpAuthorization.requestMatchers("/api/v1/defence/all").permitAll(); + + httpAuthorization.requestMatchers("/api/v1/diploma-topic/all").permitAll(); + httpAuthorization.requestMatchers("/api/v1/diploma-topic/all-for-student").permitAll(); + httpAuthorization.requestMatchers("/api/v1/diploma-topic/save").hasAnyAuthority(ADMIN.getAuthority(), SECRETARY.getAuthority()); + + httpAuthorization.requestMatchers("/api/**").denyAll(); + + httpAuthorization.requestMatchers("/**").permitAll(); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/system/config/WebConfig.java b/server/src/main/java/ru/mskobaro/tdms/system/config/WebConfig.java new file mode 100644 index 0000000..d27e8fb --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/system/config/WebConfig.java @@ -0,0 +1,28 @@ +package ru.mskobaro.tdms.system.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.resource.PathResourceResolver; + +import java.io.IOException; +import org.springframework.core.io.Resource; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/**") + .addResourceLocations("classpath:/static/") + .resourceChain(true) + .addResolver(new PathResourceResolver() { + @Override + protected Resource getResource(String resourcePath, Resource location) throws IOException { + Resource requestedResource = location.createRelative(resourcePath); + return requestedResource.exists() && requestedResource.isReadable() + ? requestedResource + : location.createRelative("index.html"); + } + }); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/mskobaro/tdms/system/web/AccessDeniedExceptionHandler.java b/server/src/main/java/ru/mskobaro/tdms/system/web/AccessDeniedExceptionHandler.java new file mode 100644 index 0000000..4a85bb8 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/system/web/AccessDeniedExceptionHandler.java @@ -0,0 +1,20 @@ +package ru.mskobaro.tdms.system.web; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerExceptionResolver; + +@Component +public class AccessDeniedExceptionHandler implements AccessDeniedHandler { + @Autowired + private HandlerExceptionResolver handlerExceptionResolver; + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) { + handlerExceptionResolver.resolveException(request, response, null, new ru.mskobaro.tdms.business.exception.AccessDeniedException()); + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/system/web/LoggingRequestFilter.java b/server/src/main/java/ru/mskobaro/tdms/system/web/LoggingRequestFilter.java new file mode 100644 index 0000000..01397de --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/system/web/LoggingRequestFilter.java @@ -0,0 +1,35 @@ +package ru.mskobaro.tdms.system.web; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.UUID; + +@Slf4j +public class LoggingRequestFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + UUID uuid = UUID.randomUUID(); + long startTime = System.currentTimeMillis(); + String username = SecurityContextHolder.getContext().getAuthentication() != null ? + SecurityContextHolder.getContext().getAuthentication().getName() : "anonymousUser"; + HttpSession session = request.getSession(false); + log.info("Request received: [{}] {} user: {}, session: {}, remote ip: {} [{}]", + request.getMethod(), request.getRequestURI(), username, + session == null ? "no" : session.getId(), request.getRemoteAddr(), uuid); + try { + filterChain.doFilter(request, response); + } finally { + long duration = System.currentTimeMillis() - startTime; + log.info("Response with {} status duration: {} ms [{}]", response.getStatus(), duration, uuid); + } + } +} diff --git a/server/src/main/java/ru/mskobaro/tdms/system/web/LoggingSessionListener.java b/server/src/main/java/ru/mskobaro/tdms/system/web/LoggingSessionListener.java new file mode 100644 index 0000000..8421553 --- /dev/null +++ b/server/src/main/java/ru/mskobaro/tdms/system/web/LoggingSessionListener.java @@ -0,0 +1,23 @@ +package ru.mskobaro.tdms.system.web; + +import jakarta.servlet.http.HttpSessionEvent; +import jakarta.servlet.http.HttpSessionListener; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +@Component +@Slf4j + +public class LoggingSessionListener implements HttpSessionListener { + @Override + public void sessionCreated(HttpSessionEvent se) { + log.debug("Session created: {}, user {}", se.getSession().getId(), + SecurityContextHolder.getContext().getAuthentication().getName()); + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) { + log.debug("Session destroyed: {}", se.getSession().getId()); + } +} diff --git a/server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/server/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..4535a23 --- /dev/null +++ b/server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,53 @@ +{ + "properties": [ + { + "name": "db.url", + "type": "java.lang.String", + "description": "Database url." + }, + { + "name": "db.user", + "type": "java.lang.String", + "description": "Database user." + }, + { + "name": "db.password", + "type": "java.lang.String", + "description": "Database password." + }, + { + "name": "db.schema", + "type": "java.lang.String", + "description": "Database schema." + }, + { + "name": "application.name", + "type": "java.lang.String", + "description": "Service name." + }, + { + "name": "application.version", + "type": "java.lang.String", + "description": "Service version." + }, + { + "name": "application.type", + "type": "java.lang.String", + "description": "Service build type." + }, + { + "name": "application.domain", + "type": "java.lang.String", + "description": "Service domain." + }, + { + "name": "application.port", + "type": "java.lang.Integer", + "description": "Service port." + }, + { + "name": "application.protocol", + "type": "java.lang.String", + "description": "Service protocol." + } + ] } \ No newline at end of file diff --git a/server/src/main/resources/application-dev.yml b/server/src/main/resources/application-dev.yml new file mode 100644 index 0000000..8fd4241 --- /dev/null +++ b/server/src/main/resources/application-dev.yml @@ -0,0 +1,5 @@ +application: + type: development + port: 8080 + domain: localhost + protocol: http diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml new file mode 100644 index 0000000..2649fb3 --- /dev/null +++ b/server/src/main/resources/application.yml @@ -0,0 +1,42 @@ +db: + url: jdbc:postgresql://localhost:5400/tdms + schema: public + user: root + password: root +application: + name: @name@ + version: @version@ + type: production + port: 443 + domain: tdms.tu-bryansk.ru + protocol: https + +spring: + application: + name: ${application.name} + datasource: + url: ${db.url} + username: ${db.user} + password: ${db.password} + hikari: + schema: ${db.schema} + jpa: + open-in-view: false + hibernate.ddl-auto: validate + flyway: + url: ${db.url} + user: ${db.user} + password: ${db.password} + schemas: ${db.schema} + banner: + location: banner.txt +server: + port: ${application.port} + address: ${application.domain} + compression: + enabled: true +management: + endpoints: + web: + exposure: + exclude: "*" \ No newline at end of file diff --git a/server/src/main/resources/banner.txt b/server/src/main/resources/banner.txt new file mode 100644 index 0000000..d9160d5 --- /dev/null +++ b/server/src/main/resources/banner.txt @@ -0,0 +1,5 @@ + __________ __ ________ ____ ____ ___ + /_ __/ __ \/ |/ / ___/ _ __ / __ \ / __ \ < / + / / / / / / /|_/ /\__ \ | | / / / / / / / / / / / / + / / / /_/ / / / /___/ / | |/ /_/ /_/ /_/ /_/ /_ / / +/_/ /_____/_/ /_//____/ |___/(_)____/(_)____/(_)_/ \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V00000__Initial_schema.sql b/server/src/main/resources/db/migration/V00000__Initial_schema.sql new file mode 100644 index 0000000..22f5225 --- /dev/null +++ b/server/src/main/resources/db/migration/V00000__Initial_schema.sql @@ -0,0 +1,280 @@ +CREATE TABLE defense +( + id BIGSERIAL PRIMARY KEY, + defense_date DATE, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE defense_best_student_works +( + defense_id BIGINT, + student_data_id BIGINT +); + +CREATE TABLE defense_commission +( + defense_id BIGINT, + commission_member_data_id BIGINT +); + +CREATE TABLE diploma_topic +( + id BIGSERIAL PRIMARY KEY, + name TEXT, + teacher_id BIGINT, + direction_of_preparation_id BIGINT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE direction_of_preparation +( + id BIGSERIAL PRIMARY KEY, + name TEXT, + code TEXT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE "group" +( + id BIGSERIAL PRIMARY KEY, + name TEXT, + defense_id BIGINT, + direction_of_preparation_id BIGINT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE participant +( + id BIGSERIAL PRIMARY KEY, + first_name TEXT, + last_name TEXT, + middle_name TEXT, + email TEXT, + number_phone TEXT, + deleted BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE participant_role +( + partic_id BIGINT NOT NULL, + role_id BIGINT NOT NULL +); + +CREATE TABLE role +( + id BIGINT PRIMARY KEY, + name TEXT, + authority TEXT +); + +/* todo */ +CREATE TABLE student_data +( + id BIGSERIAL PRIMARY KEY, + partic_id BIGINT, + study_form_id BIGINT, + + curator_id BIGINT, + protection_order INTEGER, + protection_day INTEGER, + + mark_comment INTEGER, + mark_practice INTEGER, + + predefnese_comment TEXT, + normal_control BOOLEAN, + anti_plagiarism INTEGER, + record_book_returned BOOLEAN, + work TEXT, + diploma_topic_id BIGINT, + adviser_teacher_partic_id BIGINT, + group_id BIGINT, + marks_3 BIGINT, + marks_4 BIGINT, + marks_5 BIGINT, + commission_mark BIGINT, + estimated BOOLEAN, + diploma_with_honors BOOLEAN, + magistracy_recommendation BOOLEAN, + magistracy_wanted BOOLEAN, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +/* not implemented */ +create table questionnaire +( + id BIGSERIAL PRIMARY KEY, + student_data_id BIGINT, + data TEXT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +create table study_form +( + id BIGSERIAL PRIMARY KEY, + name TEXT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +/* todo */ +create table stud_comment +( + id BIGSERIAL PRIMARY KEY, + comment TEXT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +/* todo */ +create table student_data_comment +( + id BIGSERIAL PRIMARY KEY, + student_data_id BIGINT, + stud_comment_id BIGINT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE teacher_data +( + id BIGSERIAL PRIMARY KEY, + participant_id BIGINT, + degree TEXT NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE commission_member_data +( + id BIGSERIAL PRIMARY KEY, + partic_id BIGINT, + work_place TEXT, + work_position TEXT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE "user" +( + id BIGSERIAL PRIMARY KEY, + login TEXT, + password TEXT, + partic_id BIGINT, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE task +( + id BIGSERIAL PRIMARY KEY, + type TEXT, + status TEXT, + fields jsonb, + created_at TIMESTAMP WITHOUT TIME ZONE, + updated_at TIMESTAMP WITHOUT TIME ZONE +); + +ALTER TABLE defense_commission + ADD CONSTRAINT FK_DEFCOM_ON_DEFNESE FOREIGN KEY (defense_id) REFERENCES defense (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE defense_commission + ADD CONSTRAINT FK_DEFCOM_ON_COMMEMD FOREIGN KEY (commission_member_data_id) REFERENCES commission_member_data (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE "group" + ADD CONSTRAINT FK_GROUP_ON_defnese FOREIGN KEY (defense_id) REFERENCES defense (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE "group" + ADD CONSTRAINT FK_GROUP_ON_DOP FOREIGN KEY (direction_of_preparation_id) REFERENCES direction_of_preparation (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE participant_role + ADD CONSTRAINT FK_PARROL_ON_PARTICIPANT FOREIGN KEY (partic_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE participant_role + ADD CONSTRAINT FK_PARROL_ON_ROLE FOREIGN KEY (role_id) REFERENCES role (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE student_data + ADD CONSTRAINT UC_STUDENT_DATA_PARTIC UNIQUE (partic_id); + +ALTER TABLE student_data + ADD CONSTRAINT FK_STUDENT_DATA_ON_ADVISER_TEACHER_PARTIC FOREIGN KEY (adviser_teacher_partic_id) REFERENCES participant (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE student_data + ADD CONSTRAINT FK_STUDENT_DATA_ON_DIPLOMA_TOPIC FOREIGN KEY (diploma_topic_id) REFERENCES diploma_topic (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE student_data + ADD CONSTRAINT FK_STUDENT_DATA_ON_GROUP FOREIGN KEY (group_id) REFERENCES "group" (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE student_data + ADD CONSTRAINT FK_STUDENT_DATA_ON_PARTIC FOREIGN KEY (partic_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE participant + ADD CONSTRAINT UC_PARTICIPANT_EMAIL UNIQUE (email); + +ALTER TABLE participant + ADD CONSTRAINT UC_PARTICIPANT_NUMBER_PHONE UNIQUE (number_phone); + +ALTER TABLE "user" + ADD CONSTRAINT UC_USER_LOGIN UNIQUE (login); + +ALTER TABLE "user" + ADD CONSTRAINT UC_USER_PARTIC UNIQUE (partic_id); + +ALTER TABLE "user" + ADD CONSTRAINT FK_USER_ON_PARTIC FOREIGN KEY (partic_id) REFERENCES participant (id); + +ALTER TABLE teacher_data + ADD CONSTRAINT UC_TEACHER_DATA_PARTIC UNIQUE (participant_id); + +ALTER TABLE teacher_data + ADD CONSTRAINT FK_TEACHER_DATA_ON_PARTIC FOREIGN KEY (participant_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE commission_member_data + ADD CONSTRAINT UC_COMMISION_MEMBER_DATA_PARTIC UNIQUE (partic_id); + +ALTER TABLE commission_member_data + ADD CONSTRAINT FK_COMMISION_MEMBER_DATA_ON_PARTIC FOREIGN KEY (partic_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE direction_of_preparation + ADD CONSTRAINT UC_DIRECTION_OF_PREPARATION_NAME UNIQUE (name); + +ALTER TABLE direction_of_preparation + ADD CONSTRAINT UC_DIRECTION_OF_PREPARATION_CODE UNIQUE (code); + +ALTER TABLE diploma_topic + ADD CONSTRAINT UC_DIPLOMA_TOPIC_NAME FOREIGN KEY (direction_of_preparation_id) REFERENCES direction_of_preparation (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE defense_best_student_works + ADD CONSTRAINT FK_DEFENSE_BEST_STUDENT_WORKS_ON_DEFENSE FOREIGN KEY (defense_id) REFERENCES defense (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE defense_best_student_works + ADD CONSTRAINT FK_DEFENSE_BEST_STUDENT_WORKS_ON_STUDENT_DATA FOREIGN KEY (student_data_id) REFERENCES student_data (id) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE stud_comment + ADD CONSTRAINT UC_STUD_COMMENT_COMMENT UNIQUE (comment); + +ALTER TABLE student_data + ADD CONSTRAINT UC_STUDENT_DATA_DIPLOMA_TOPIC FOREIGN KEY (diploma_topic_id) REFERENCES diploma_topic (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE student_data + ADD CONSTRAINT UC_STUDENT_DATA_STUDY_FORM FOREIGN KEY (study_form_id) REFERENCES study_form (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE student_data + ADD CONSTRAINT UC_STUDENT_DATA_CURATOR FOREIGN KEY (curator_id) REFERENCES teacher_data (id) ON DELETE SET NULL ON UPDATE CASCADE; + +ALTER TABLE study_form + ADD CONSTRAINT UC_STUDY_FORM_NAME UNIQUE (name); + +alter table questionnaire + add constraint UC_QUESTIONNAIRE_STUDENT_DATA_ID unique (student_data_id); + +alter table questionnaire + add constraint FK_QUESTIONNAIRE_STUDENT_DATA foreign key (student_data_id) references student_data (id) on delete cascade on update cascade; \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V00500__Insert_default_roles.sql b/server/src/main/resources/db/migration/V00500__Insert_default_roles.sql new file mode 100644 index 0000000..8c143c4 --- /dev/null +++ b/server/src/main/resources/db/migration/V00500__Insert_default_roles.sql @@ -0,0 +1,7 @@ +insert into role (id, name, authority) +values (1, 'Преподаватель', 'ROLE_TEACHER'), + (2, 'Студент', 'ROLE_STUDENT'), + (3, 'Член комиссии ГЭК', 'ROLE_COMMISSION_MEMBER'), + (4, 'Администратор', 'ROLE_ADMINISTRATOR'), + (5, 'Секретарь', 'ROLE_SECRETARY'), + (6, 'Проверяющий на плагиат', 'ROLE_PLAGIARISM_CHECKER'); diff --git a/server/src/main/resources/db/migration/V00510__Insert_system_administrator.sql b/server/src/main/resources/db/migration/V00510__Insert_system_administrator.sql new file mode 100644 index 0000000..e3e769a --- /dev/null +++ b/server/src/main/resources/db/migration/V00510__Insert_system_administrator.sql @@ -0,0 +1,21 @@ +insert into participant (id, first_name, email, number_phone, created_at, updated_at) +values (1, + 'Администратор', + 'admin@tdms.tu-bryansk.ru', + '+74832580058', + now(), + now()); + +insert into "user" (id, login, password, partic_id, created_at, updated_at) +values (1, + 'admin', + '{noop}Admin000', + 1, + now(), + now()); + +insert into participant_role (partic_id, role_id) +values (1, 4); + +select setval('participant_id_seq', (select max(id) from participant)); +select setval('user_id_seq', (select max(id) from "user")); diff --git a/server/src/main/resources/db/test-data/V00070__Create__defence_table.sql b/server/src/main/resources/db/test-data/V00070__Create__defence_table.sql new file mode 100644 index 0000000..c0bb458 --- /dev/null +++ b/server/src/main/resources/db/test-data/V00070__Create__defence_table.sql @@ -0,0 +1,12 @@ +create table defense +( + id bigserial primary key, + defence_date timestamptz, + + created_at timestamptz not null, + updated_at timestamptz +); + +-- COMMENTS +comment on table defense is 'Таблица для хранения данных о защитах'; +comment on column defense.defence_date is 'Дата защиты'; \ No newline at end of file diff --git a/server/src/main/resources/db/test-data/diploma_topic.sql b/server/src/main/resources/db/test-data/diploma_topic.sql new file mode 100644 index 0000000..0294670 --- /dev/null +++ b/server/src/main/resources/db/test-data/diploma_topic.sql @@ -0,0 +1,40 @@ +INSERT INTO diploma_topic (name) +VALUES ('Мобильное приложение для заказа автозапчастей на платформе ABCP'), + ('Подсистема уведомления пользователей для программного комплекса "РискПроф. Учебный центр"'), + ('Веб-приложение "Таск-менеджер"'), + ('Интернет-магазин электроники'), + ('Информационная система для студентов БГТУ в Telegram'), + ('Автоматизированная система тестирования сотрудников для определения их компетенций: модуль подготовки тестов и учета результатов'), + ('Инструментарий для разработки динамической экосистемы игрового пространства. Подсистема управления поведением внутриигровых агентов'), + ('Корпоративное веб-приложение по управлению задачами'), + ('Подсистемы визуализации и аналитики для онлайн-сервиса поддержки scrum-доски'), + ('Интерактивная система обучения приемам работы с криптовалютой'), + ('Автоматизированная система учета деятельности салона по продаже автомобилей'), + ('Обучающее мобильное android-приложение'), + ('Автоматизированная система учета зуботехнической CAD/CAM лаборатории'), + ('Автоматизированная система тестирования сотрудников для определения их компетенций: модуль регистрации и управления кабинетами'), + ('Система мониторинга успеваемости обучающихся и посещаемости занятий образовательного учреждения'), + ('Мобильное приложение для организации работы репетитора'), + ('Модуль "Редактор рисков" для программного комплекса управления профессиональными рисками "1С. ЕОС ПБ"'), + ('Онлайн-сервис по продаже товаров (на примере ООО «ЭПК-2»'), + ('Веб-сервис по обучению Microsoft Excel'), + ('Инструментарий для разработки динамической экосистемы игрового пространства. Подсистема генерации наполнения помещений'), + ('Подсистема интеграции с федеральной государственной информационной системой по охране труда для программного комплекса «РискПроф. Учебный центр»'), + ('Разработка приложения голосовой авторизации на основе нейросетевого подхода'), + ('Автоматизированная система учета деятельности спортивного клуба'), + ('Модуль "Специальная оценка условий труда" для программного комплекса управления профессиональными рисками "1С. ЕОС ПБ"'), + ('Интернет-магазин по продаже спортивной обуви'), + ('Приложение интерактивной исторической карты'), + ('Миграция системы учёта с 1С:УТ на 1С:КА'), + ('Программный комплекс для автоматизированного проведения опросов и тестирования пользователей'), + ('Автоматизированное тестирование программного комплекса «Федеральная государственная информационная система специальной оценки условий труда»'), + ('Модуль "Единая информационно-справочная подсистема" для федеральной государственной информационной системы по охране труда'), + ('Подсистема сопровождения обучения по охране труда для программного комплекса «РискПроф. Учебный центр»'), + ('Программное обеспечение системы обучения и тестирования в мессенджере Telegram'), + ('Онлайн-калькулятор индексов влияния в играх взвешенного голосования'), + ('Telegram-бот с функциями обработки изображений'), + ('Подсистема администрирования автоматизированной системы для проведения платежей и перевода денежных средств'), + ('Подсистема администрирования для программного комплекса распределения студентов по руководителям выпускных квалификационных работ'), + ('Библиотека контроля качества данных в базах и хранилищах данных для программного комплекса MetaControl'), + ('Инструментарий для разработки динамической экосистемы игрового пространства. Подсистема генерации карты помещений'), + ('Программная система решения транспортной задачи методом генетического алгоритма для торговой сети'); diff --git a/server/src/main/resources/db/test-data/group.sql b/server/src/main/resources/db/test-data/group.sql new file mode 100644 index 0000000..0b0c6a4 --- /dev/null +++ b/server/src/main/resources/db/test-data/group.sql @@ -0,0 +1,3 @@ +INSERT INTO "group" (name, curator_teacher_id, created_at, updated_at) +VALUES ('ИВТ-1', 40, now(), now()), + ('ИВТ-2', 40, now(), now()); diff --git a/server/src/main/resources/db/test-data/student.sql b/server/src/main/resources/db/test-data/student.sql new file mode 100644 index 0000000..840c618 --- /dev/null +++ b/server/src/main/resources/db/test-data/student.sql @@ -0,0 +1,98 @@ +do +$$ + begin + INSERT INTO studentData (form, + protection_order, + magistracy, + digital_format_present, + mark_comment, + mark_practice, + predefence_comment, + normal_control, + anti_plagiarism, + note, + record_book_returned, + work, + user_id, + diploma_topic_id, + mentor_user_id, + group_id, + created_at) + VALUES (true, 1100, 'ПРИ, ИВТ или другой вуз', true, 5, 5, 'ок', 'Подписано', 7862, 'Акт о внедрении', true, + 'нет', 1, 1, 40, 3, now()), + (true, 1110, 'Да, но не уверен, в БГТУ ли', true, 5, 5, 'ок', 'Подписано', 8196, + 'Заявка, Акт о внедрении', true, 'ООО "ЦИРОБЗ"', 2, 2, 40, 3, now()), + (true, 3500, 'Нет', true, 3, 3, + 'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 7141, 'Иниц', + true, 'нет', 3, 3, 41, 3, now()), + (true, 1800, 'Да, но не уверен, в БГТУ ли', true, 4, 5, 'Усилить работу', 'Подписано', 5381, 'Иниц', + true, 'нет', 4, 4, 42, 3, now()), + (true, 2100, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 8146, 'Заявка, Акт о внедрении', true, 'нет', 5, + 5, 43, 3, now()), + (true, 1100, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 7965, 'Иниц', true, 'нет', 6, 6, 41, 3, now()), + (true, 1200, 'нет', true, 5, 4, 'Усилить работу', 'Подписано', 8583, Null, true, + 'Газ Энерго Комплект (ГЭК)', 7, 7, 44, 3, now()), + (true, 1700, 'Нет', true, 5, 5, + 'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 6647, Null, + true, 'нет', 8, 8, 45, 3, now()), + (true, 3300, 'ИВТ, ПРИ или другой вуз', true, 5, 5, 'Усилить работу', 'Подписано', 6741, + 'Заявка, Акт о внедрении', true, 'нет', 9, 9, 46, 3, now()), + (true, 1200, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7645, 'Заявка, Акт о внедрении', true, 'нет', 10, 10, + 40, 3, now()), + (true, 3700, 'нет', true, 3, 3, + 'Критически низкий уровень. Допущен под ответственность руководителя ВКР', '1', 3093, 'Иниц', true, + 'нет', 11, 11, 45, 3, now()), + (true, 1900, 'Нет', true, 5, 5, 'ок', 'Подписано', 8175, 'Заявка, Акт о внедрении', true, 'нет', 12, 12, + 42, 3, now()), + (true, 3200, 'ИВТ', true, 5, 5, 'Усилить работу', 'Подписано', 7805, 'Акт', true, 'нет', 13, 13, 46, 3, + now()), + (true, 1200, 'ИВТ, ПРИ', true, 4, 4, 'ок', 'рек', 7590, 'Иниц', true, 'нет', 14, 14, 45, 3, now()), + (true, 3900, 'нет', true, 5, 5, 'Усилить работу', 'Подписано', 6463, 'Заявка, Акт о внедрении', true, + 'нет', 15, 15, 40, 3, now()), + (true, 2200, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7441, Null, true, 'ООО "ЦИРОБЗ"', 16, 16, 44, 3, + now()), + (true, 1130, 'Нет', true, 5, 4, + 'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 7319, + 'Заявка, Акт о внедрении', true, 'нет', 17, 17, 40, 3, now()), + (true, 2400, 'ИВТ', true, 4, 5, 'ок', 'Подписано', 6436, Null, true, 'нет', 18, 18, 45, 3, now()), + (true, 1600, 'ИВТ', true, 5, 5, 'Усилить работу', 'Подписано', 6227, 'Исслед', true, 'АО "БЭМЗ"', 19, 19, + 47, 3, now()), + (true, 1400, 'Нет', true, 5, 5, 'ок', 'Подписано', 8935, 'Иниц', true, 'ООО "ЦИРОБЗ"', 20, 20, 44, 4, + now()), + (true, 2900, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7971, 'Заявка, Акт о внедрении', true, 'нет', 21, 21, + 40, 4, now()), + (true, 2600, 'ИВТ, ПРИ', true, 3, 3, 'ок', 'Подписано', 7284, Null, true, 'нет', 22, 22, 48, 4, now()), + (true, 3100, 'ИВТ, ПРИ', true, 5, 5, 'Усилить работу', 'Подписано', 4966, 'Заявка, Акт о внедрении', + true, 'нет', 23, 23, 45, 4, now()), + (true, 3110, 'Нет', true, 5, 5, 'ок', 'Подписано', 7174, Null, true, 'нет', 24, 24, 40, 4, now()), + (true, 3800, 'Нет', true, 5, 5, 'ок', 'Подписано', 7233, Null, true, 'нет', 25, 25, 45, 4, now()), + (true, 3300, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7133, Null, true, 'нет', 26, 26, 43, 4, now()), + (true, 3130, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 8083, 'Заявка, Акт о внедрении', true, 'нет', 27, 27, + 49, 4, now()), + (true, 3400, 'Нет', true, 5, 4, + 'Критически низкий уровень. Допущен под ответственность руководителя ВКР', 'Подписано', 7968, 'Иниц', + true, 'нет', 28, 28, 50, 4, now()), + (true, 3120, 'ИВТ или ПРИ', true, 5, 5, 'ок', 'Подписано', 7940, 'Исслед', true, 'ООО "ЦИРОБЗ"', 29, 29, + 40, 4, now()), + (true, 2800, 'Нет (возможно)', true, 4, 4, 'Усилить работу', 'рек', 6775, 'Заявка, Акт о внедрении', + true, 'нет', 30, 30, 40, 4, now()), + (true, 2100, 'Нет (возможно)', true, 5, 5, 'ок', 'Подписано', 7637, 'Заявка, Акт о внедрении', true, + 'нет', 31, 31, 40, 4, now()), + (true, 2700, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 8544, 'Заявка, Акт о внедрении', true, 'нет', 32, 32, + 45, 4, now()), + (true, 2130, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 7166, 'Заявка, Акт о внедрении', true, 'нет', 33, + 33, 51, 4, now()), + (true, 2110, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 6075, 'Заявка, Акт о внедрении', true, 'нет', 34, 34, + 52, 4, now()), + (true, 3100, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7057, 'Заявка, Акт о внедрении', true, 'нет', 35, 35, + 50, 4, now()), + (true, 2120, 'В БГТУ на другой кафедре (38.04.01 Экономика или 27.04.05. Инноватика)', true, 5, 5, 'ок', + 'Подписано', 7057, 'Заявка, Акт о внедрении', true, 'нет', 36, 36, 51, 4, now()), + (true, 2500, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 6583, 'Заявка, Акт о внедрении', true, 'нет', 37, 37, + 53, 4, now()), + (true, 1300, 'ИВТ, ПРИ', true, 5, 5, 'ок', 'Подписано', 8444, 'Заявка, Акт о внедрении', true, 'нет', 38, + 38, 44, 4, now()), + (true, 3600, 'ИВТ', true, 5, 5, 'ок', 'Подписано', 7631, 'Заявка, Акт о внедрении', true, 'нет', 39, 39, + 45, 4, now()); + end +$$; \ No newline at end of file diff --git a/server/src/main/resources/db/test-data/user.sql b/server/src/main/resources/db/test-data/user.sql new file mode 100644 index 0000000..7516226 --- /dev/null +++ b/server/src/main/resources/db/test-data/user.sql @@ -0,0 +1,82 @@ +INSERT INTO "user" (login, password, full_name, mail, number_phone, created_at) +VALUES ('akulenko_mikhail', '{noop}1', 'Акуленко Михаил Вячеславович', 'akulenko.mikhail@example.com', + '+79110000001', NOW()), + ('borovikov_artem', '{noop}1', 'Боровиков Артём Викторович', 'borovikov.artem@example.com', '+79110000002', + NOW()), + ('bykonya_alexey', '{noop}1', 'Быконя Алексей Николаевич', 'bykonya.alexey@example.com', '+79110000003', + NOW()), + ('ermakov_alexander', '{noop}1', 'Ермаков Александр Сергеевич', 'ermakov.alexander@example.com', + '+79110000004', NOW()), + ('zgursky_evgeny', '{noop}1', 'Згурский Евгений Олегович', 'zgursky.evgeny@example.com', '+79110000005', + NOW()), + ('ibishov_tural', '{noop}1', 'Ибишов Турал Садай оглы', 'ibishov.tural@example.com', '+79110000006', NOW()), + ('ignatenko_vladimir', '{noop}1', 'Игнатенко Владимир Алексеевич', 'ignatenko.vladimir@example.com', + '+79110000007', NOW()), + ('lazukin_danila', '{noop}1', 'Лазукин Данила Дмитриевич', 'lazukin.danila@example.com', '+79110000008', + NOW()), + ('mitiaev_danila', '{noop}1', 'Митяев Данила Алексеевич', 'mitiaev.danila@example.com', '+79110000009', + NOW()), + ('neshkov_daniil', '{noop}1', 'Нешков Даниил Владимирович', 'neshkov.daniil@example.com', '+79110000010', + NOW()), + ('petrov_pavel', '{noop}1', 'Петров Павел Сергеевич', 'petrov.pavel@example.com', '+79110000011', NOW()), + ('sazonov_andrey', '{noop}1', 'Сазонов Андрей Андреевич', 'sazonov.andrey@example.com', '+79110000012', + NOW()), + ('solokhin_maxim', '{noop}1', 'Солохин Максим Николаевич', 'solokhin.maxim@example.com', '+79110000013', + NOW()), + ('sochinsky_artem', '{noop}1', 'Сочинский Артем Александрович', 'sochinsky.artem@example.com', + '+79110000014', NOW()), + ('trisvyatsky_kirill', '{noop}1', 'Трисвятский Кирилл Андреевич', 'trisvyatsky.kirill@example.com', + '+79110000015', NOW()), + ('turov_alexander', '{noop}1', 'Туров Александр Сергеевич', 'turov.alexander@example.com', '+79110000016', + NOW()), + ('shevtsova_alexandra', '{noop}1', 'Шевцова Александра Валерьевна', 'shevtsova.alexandra@example.com', + '+79110000017', NOW()), + ('kibalyuk_artem', '{noop}1', 'Кибалюк Артем Сергеевич', 'kibalyuk.artem@example.com', '+79110000018', NOW()), + ('shulindin_artem', '{noop}1', 'Шулындин Артём Андреевич', 'shulindin.artem@example.com', '+79110000019', + NOW()), + ('belyaev_egor', '{noop}1', 'Беляев Егор Андреевич', 'belyaev.egor@example.com', '+79110000020', NOW()), + ('berezhnoy_igor', '{noop}1', 'Бережной Игорь Александрович', 'berezhnoy.igor@example.com', '+79110000021', + NOW()), + ('bogun_pavel', '{noop}1', 'Богун Павел Сергеевич', 'bogun.pavel@example.com', '+79110000022', NOW()), + ('vaseykin_nikita', '{noop}1', 'Васейкин Никита Павлович', 'vaseykin.nikita@example.com', '+79110000023', + NOW()), + ('gomonov_nikita', '{noop}1', 'Гомонов Никита Алексеевич', 'gomonov.nikita@example.com', '+79110000024', + NOW()), + ('druyan_oleg', '{noop}1', 'Друян Олег Викторович', 'druyan.oleg@example.com', '+79110000025', NOW()), + ('ivanov_kirill', '{noop}1', 'Иванов Кирилл Эдуардович', 'ivanov.kirill@example.com', '+79110000026', NOW()), + ('ivanova_veronika', '{noop}1', 'Иванова Вероника Евгеньевна', 'ivanova.veronika@example.com', + '+79110000027', NOW()), + ('izotov_ivan', '{noop}1', 'Изотов Иван Алексеевич', 'izotov.ivan@example.com', '+79110000028', NOW()), + ('isakov_zahar', '{noop}1', 'Исаков Захар Александрович', 'isakov.zahar@example.com', '+79110000029', NOW()), + ('iskritsky_daniil', '{noop}1', 'Искрицкий Даниил Павлович', 'iskritsky.daniil@example.com', '+79110000030', + NOW()), + ('linko_daria', '{noop}1', 'Линько Дарья Андреевна', 'linko.daria@example.com', '+79110000031', NOW()), + ('logutov_kirill', '{noop}1', 'Логутов Кирилл Александрович', 'logutov.kirill@example.com', '+79110000032', + NOW()), + ('nekrassov_sergey', '{noop}1', 'Некрасов Сергей Игоревич', 'nekrassov.sergey@example.com', '+79110000033', + NOW()), + ('sinyagin_ilya', '{noop}1', 'Синягин Илья Александрович', 'sinyagin.ilya@example.com', '+79110000034', + NOW()), + ('sopriko_daniil', '{noop}1', 'Соприко Даниил Сергеевич', 'sopriko.daniil@example.com', '+79110000035', + NOW()), + ('turovsky_ivan', '{noop}1', 'Туровский Иван Алексеевич', 'turovsky.ivan@example.com', '+79110000036', NOW()), + ('frantsev_sergey', '{noop}1', 'Францев Сергей Дмитриевич', 'frantsev.sergey@example.com', '+79110000037', + NOW()), + ('chepurnoy_maxim', '{noop}1', 'Чепурной Максим Романович', 'chepurnoy.maxim@example.com', '+79110000038', + NOW()), + ('schemelinin_dmitry', '{noop}1', 'Щемелинин Дмитрий Михайлович', 'schemelinin.dmitry@example.com', + '+79110000039', NOW()), + ('bulatitsky_d_i_1', '{noop}1', 'Булатицкий Д. И.', 'bulatitsky.d.i.1@example.com', '+79110000040', NOW()), + ('kopeliovich_d_i_1', '{noop}1', 'Копелиович Д. И.', 'kopeliovich.d.i.1@example.com', '+79110000041', NOW()), + ('dergachev_k_v', '{noop}1', 'Дергачев К. В.', 'dergachev.k.v@example.com', '+79110000042', NOW()), + ('trubakov_e_o', '{noop}1', 'Трубаков Е. О.', 'trubakov.e.o@example.com', '+79110000043', NOW()), + ('radchenko_a_o', '{noop}1', 'Радченко А. О.', 'radchenko.a.o@example.com', '+79110000044', NOW()), + ('zimin_s_n_1', '{noop}1', 'Зимин С. Н.', 'zimin.s.n.1@example.com', '+79110000045', NOW()), + ('koptenok_e_v', '{noop}1', 'Коптенок Е. В.', 'koptenok.e.v@example.com', '+79110000046', NOW()), + ('mikhaleva_o_a', '{noop}1', 'Михалева О. А.', 'mikhaleva.o.a@example.com', '+79110000047', NOW()), + ('gulakov_k_v', '{noop}1', 'Гулаков К. В.', 'gulakov.k.v@example.com', '+79110000048', NOW()), + ('titarev_d_v', '{noop}1', 'Титарёв Д. В.', 'titarev.d.v@example.com', '+79110000049', NOW()), + ('izrailev_v_ya_1', '{noop}1', 'Израилев В. Я.', 'izrailev.v.ya.1@example.com', '+79110000050', NOW()), + ('podvesovsky_a_g_1', '{noop}1', 'Подвесовский А. Г.', 'podvesovsky.a.g.1@example.com', '+79110000051', NOW()), + ('trubakov_a_o', '{noop}1', 'Трубаков А. О.', 'trubakov.a.o@example.com', '+79110000059', NOW()), + ('lageriev_d_g', '{noop}1', 'Лагерев Д. Г.', 'lageriev.d.g@example.com', '+79110000052', NOW()); diff --git a/server/src/main/resources/db/test-data/user_role.sql b/server/src/main/resources/db/test-data/user_role.sql new file mode 100644 index 0000000..4b00ae0 --- /dev/null +++ b/server/src/main/resources/db/test-data/user_role.sql @@ -0,0 +1,58 @@ +do +$$ + declare + teacher_id bigint := 1; + student_id bigint := 2; + commission_member_id bigint := 3; + administrator_id bigint := 4; + secretary_id bigint := 5; + begin + INSERT INTO user_role (user_id, role_id) + VALUES (1, student_id), + (2, student_id), + (3, student_id), + (4, student_id), + (5, student_id), + (6, student_id), + (7, student_id), + (8, student_id), + (9, student_id), + (10, student_id), + (11, student_id), + (12, student_id), + (13, student_id), + (14, student_id), + (15, student_id), + (16, student_id), + (17, student_id), + (18, student_id), + (19, student_id), + (20, student_id), + (21, student_id), + (22, student_id), + (23, student_id), + (24, student_id), + (25, student_id), + (26, student_id), + (27, student_id), + (28, student_id), + (29, student_id), + (30, student_id), + (31, student_id), + (32, student_id), + (33, student_id), + (34, student_id), + (35, student_id), + (36, student_id), + (37, teacher_id), + (37, administrator_id), + (38, commission_member_id), + (39, teacher_id), + (40, teacher_id), + (41, teacher_id), + (42, teacher_id), + (43, teacher_id), + (44, secretary_id), + (45, secretary_id); + end +$$; \ No newline at end of file diff --git a/server/src/main/resources/logback.xml b/server/src/main/resources/logback.xml new file mode 100644 index 0000000..539813a --- /dev/null +++ b/server/src/main/resources/logback.xml @@ -0,0 +1,22 @@ + + + + %d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n + + + + + logs/app.log + false + + %d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n + + + + + + + + + + diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..efe22aa --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,26 @@ +node + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/web/babel.config.json b/web/babel.config.json new file mode 100644 index 0000000..8fc8463 --- /dev/null +++ b/web/babel.config.json @@ -0,0 +1,12 @@ +{ + "presets": [ + ["@babel/preset-env", {"modules": false}], + "@babel/preset-react", + "@babel/preset-typescript" + ], + "plugins": [ + ["@babel/plugin-proposal-decorators", { "legacy": true }], + ["@babel/plugin-proposal-class-properties"], + ["@babel/plugin-transform-typescript"] + ] +} diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..346a708 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,7045 @@ +{ + "name": "web", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "1.0.0", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-brands-svg-icons": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", + "@types/lodash": "^4.17.15", + "axios": "^1.7.7", + "bootstrap": "^5.3.3", + "lodash": "^4.17.21", + "mobx": "^6.13.1", + "mobx-react": "^9.1.1", + "mobx-state-router": "^6.0.1", + "react": "^18.2.0", + "react-bootstrap": "^2.10.4", + "react-dom": "^18.2.0", + "uuid": "^11.0.5" + }, + "devDependencies": { + "@babel/plugin-proposal-decorators": "^7.25.7", + "@babel/preset-env": "^7.25.8", + "@babel/preset-react": "^7.25.7", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@types/webpack": "^5.28.5", + "copy-webpack-plugin": "^13.0.0", + "css-loader": "^7.1.2", + "html-webpack-plugin": "^5.6.2", + "style-loader": "^4.0.0", + "ts-loader": "^9.5.1", + "typescript": "^5.6.3", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.1.0" + } + }, + "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, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", + "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", + "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.8", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.8", + "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/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", + "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", + "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", + "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/traverse": "^7.25.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", + "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", + "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", + "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", + "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-wrap-function": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", + "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", + "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", + "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", + "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", + "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", + "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", + "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/plugin-transform-optional-chaining": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", + "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.7.tgz", + "integrity": "sha512-q1mqqqH0e1lhmsEQHV5U8OmdueBC2y0RFr2oUzZoFRtN3MvPmt2fsFRcNQAoGLTSNdHBFUYGnlgcRFhkBbKjPw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-syntax-decorators": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.7.tgz", + "integrity": "sha512-oXduHo642ZhstLVYTe2z2GSJIruU0c/W3/Ghr6A5yGMsVrvdnxO1z+3pbTcT7f3/Clnt+1z8D/w1r1f1SHaCHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", + "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", + "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", + "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", + "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", + "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", + "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", + "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", + "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", + "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", + "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", + "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/traverse": "^7.25.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", + "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/template": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", + "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", + "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", + "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", + "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", + "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", + "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", + "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", + "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", + "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", + "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", + "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", + "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", + "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", + "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", + "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", + "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", + "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", + "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", + "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", + "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-transform-parameters": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", + "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", + "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", + "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", + "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", + "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", + "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", + "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.7.tgz", + "integrity": "sha512-r0QY7NVU8OnrwE+w2IWiRom0wwsTbjx4+xH2RTd7AVdof3uurXOF+/mXHQDRk+2jIvWgSaCHKMgggfvM4dyUGA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.7.tgz", + "integrity": "sha512-vILAg5nwGlR9EXE8JIOX4NHXd49lrYbN8hnjffDtoULwpL9hUx/N55nqh2qd0q6FyNDfjl9V79ecKGvFbcSA0Q==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-syntax-jsx": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.7.tgz", + "integrity": "sha512-5yd3lH1PWxzW6IZj+p+Y4OLQzz0/LzlOG8vGqonHfVR3euf1vyzyMUJk9Ac+m97BH46mFc/98t9PmYLyvgL3qg==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.7.tgz", + "integrity": "sha512-6YTHJ7yjjgYqGc8S+CbEXhLICODk0Tn92j+vNJo07HFk9t3bjFgAKxPLFhHwF2NjmQVSI1zBRfBWUeVBa2osfA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", + "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", + "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", + "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", + "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", + "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", + "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", + "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", + "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", + "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", + "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", + "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", + "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.8", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.25.7", + "@babel/plugin-syntax-import-attributes": "^7.25.7", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.8", + "@babel/plugin-transform-async-to-generator": "^7.25.7", + "@babel/plugin-transform-block-scoped-functions": "^7.25.7", + "@babel/plugin-transform-block-scoping": "^7.25.7", + "@babel/plugin-transform-class-properties": "^7.25.7", + "@babel/plugin-transform-class-static-block": "^7.25.8", + "@babel/plugin-transform-classes": "^7.25.7", + "@babel/plugin-transform-computed-properties": "^7.25.7", + "@babel/plugin-transform-destructuring": "^7.25.7", + "@babel/plugin-transform-dotall-regex": "^7.25.7", + "@babel/plugin-transform-duplicate-keys": "^7.25.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-dynamic-import": "^7.25.8", + "@babel/plugin-transform-exponentiation-operator": "^7.25.7", + "@babel/plugin-transform-export-namespace-from": "^7.25.8", + "@babel/plugin-transform-for-of": "^7.25.7", + "@babel/plugin-transform-function-name": "^7.25.7", + "@babel/plugin-transform-json-strings": "^7.25.8", + "@babel/plugin-transform-literals": "^7.25.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", + "@babel/plugin-transform-member-expression-literals": "^7.25.7", + "@babel/plugin-transform-modules-amd": "^7.25.7", + "@babel/plugin-transform-modules-commonjs": "^7.25.7", + "@babel/plugin-transform-modules-systemjs": "^7.25.7", + "@babel/plugin-transform-modules-umd": "^7.25.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-new-target": "^7.25.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", + "@babel/plugin-transform-numeric-separator": "^7.25.8", + "@babel/plugin-transform-object-rest-spread": "^7.25.8", + "@babel/plugin-transform-object-super": "^7.25.7", + "@babel/plugin-transform-optional-catch-binding": "^7.25.8", + "@babel/plugin-transform-optional-chaining": "^7.25.8", + "@babel/plugin-transform-parameters": "^7.25.7", + "@babel/plugin-transform-private-methods": "^7.25.7", + "@babel/plugin-transform-private-property-in-object": "^7.25.8", + "@babel/plugin-transform-property-literals": "^7.25.7", + "@babel/plugin-transform-regenerator": "^7.25.7", + "@babel/plugin-transform-reserved-words": "^7.25.7", + "@babel/plugin-transform-shorthand-properties": "^7.25.7", + "@babel/plugin-transform-spread": "^7.25.7", + "@babel/plugin-transform-sticky-regex": "^7.25.7", + "@babel/plugin-transform-template-literals": "^7.25.7", + "@babel/plugin-transform-typeof-symbol": "^7.25.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.7", + "@babel/plugin-transform-unicode-property-regex": "^7.25.7", + "@babel/plugin-transform-unicode-regex": "^7.25.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.38.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.7.tgz", + "integrity": "sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-transform-react-display-name": "^7.25.7", + "@babel/plugin-transform-react-jsx": "^7.25.7", + "@babel/plugin-transform-react-jsx-development": "^7.25.7", + "@babel/plugin-transform-react-pure-annotations": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", + "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.6.0.tgz", + "integrity": "sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz", + "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", + "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, + "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==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@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==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@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==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@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==", + "dev": true + }, + "node_modules/@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==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", + "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", + "dev": true, + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.6.tgz", + "integrity": "sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-force/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@react-force/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-HdGqlVbNifHN0DL0kybtfuz3oXKk5B8nTkiZwMtMZGrYPOjw0WmHSLT0ptlyXkPUzIrJoRnazL//L3jHZghaCQ==", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.8.0.tgz", + "integrity": "sha512-xJEOXUOTmT4FngTmhdjKFRrVVF0hwCLNPdatLCHkyS4dkiSK12cEu1Y0fjxktjJrdst9jJIc5J6ihMJCoWEN/g==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.13.tgz", + "integrity": "sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz", + "integrity": "sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@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 + }, + "node_modules/@types/lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.7.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz", + "integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" + }, + "node_modules/@types/webpack": { + "version": "5.28.5", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.5.tgz", + "integrity": "sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "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, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "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, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.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" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/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==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "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==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "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, + "peer": true + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-webpack-plugin": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.0.tgz", + "integrity": "sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-parent": "^6.0.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2", + "tinyglobby": "^0.2.12" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/node_modules/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==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js-compat": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "dev": true, + "dependencies": { + "browserslist": "^4.23.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.41", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz", + "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/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, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "dev": true + }, + "node_modules/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==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "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/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "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/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.2.tgz", + "integrity": "sha512-q7xp/FO9RGBVoTKNItkdX1jKLscLFkgn/dLVFNYbHVbfHLBk6DYW5nsQ8kCzIWcgKP/kUBocetjvav6lD8YfCQ==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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", + "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/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "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/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/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==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz", + "integrity": "sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==", + "dev": true, + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "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==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/mobx": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.5.tgz", + "integrity": "sha512-/HTWzW2s8J1Gqt+WmUj5Y0mddZk+LInejADc79NJadrWla3rHzmRHki/mnEUH1AvOmbNTZ1BRbKxr8DSgfdjMA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.1.1.tgz", + "integrity": "sha512-gVV7AdSrAAxqXOJ2bAbGa5TkPqvITSzaPiiEkzpW4rRsMhSec7C2NBCJYILADHKp2tzOAIETGRsIY0UaCV5aEw==", + "dependencies": { + "mobx-react-lite": "^4.0.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/mobx-react-lite": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.0.7.tgz", + "integrity": "sha512-RjwdseshK9Mg8On5tyJZHtGD+J78ZnCnRaxeQDSiciKVQDUbfZcXhmld0VMxAwvcTnPEHZySGGewm467Fcpreg==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/mobx-state-router": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mobx-state-router/-/mobx-state-router-6.0.1.tgz", + "integrity": "sha512-tb2FCzb4Owxh6MQ+FkFC6ntJfM0TcOy2UG/3YBcRm/6hLhr42XG+ijOoQydf9C66DY7ihYVzf8LhSHvr8ie4fA==", + "dependencies": { + "@react-force/utils": "^2.3.0", + "debug": "^4.3.3", + "history": "^4.10.1", + "path-to-regexp": "^6.2.0", + "query-string": "^7.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "mobx": ">=6", + "mobx-react-lite": ">=3", + "react": ">=16" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "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==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/p-retry": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "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/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==", + "dev": true + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-bootstrap": { + "version": "2.10.5", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.5.tgz", + "integrity": "sha512-XueAOEn64RRkZ0s6yzUTdpFtdUXs5L5491QU//8ZcODKJNDLt/r01tNyriZccjgRImH1REynUc9pqjiRMpDLWQ==", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.9", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/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==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/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==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "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==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexpu-core": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", + "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "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", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "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-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-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "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/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/scheduler": { + "version": "0.23.2", + "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/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs/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==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", + "dev": true, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.27.0" + } + }, + "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/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==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "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, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webpack": { + "version": "5.95.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", + "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.6.0", + "mime-types": "^2.1.31", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.1.0.tgz", + "integrity": "sha512-aQpaN81X6tXie1FoOB7xlMfCsN19pSvRAeYUHOdFWOlhpQ/LlbfTqYwwmEDFV0h8GGuqmCmKmT+pxcUV/Nt2gQ==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "express": "^4.19.2", + "graceful-fs": "^4.2.6", + "html-entities": "^2.4.0", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..bc379ea --- /dev/null +++ b/web/package.json @@ -0,0 +1,44 @@ +{ + "name": "web", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "webpack --mode production", + "dev": "webpack-dev-server --mode development" + }, + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-brands-svg-icons": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", + "@types/lodash": "^4.17.15", + "axios": "^1.7.7", + "bootstrap": "^5.3.3", + "lodash": "^4.17.21", + "mobx": "^6.13.1", + "mobx-react": "^9.1.1", + "mobx-state-router": "^6.0.1", + "react": "^18.2.0", + "react-bootstrap": "^2.10.4", + "react-dom": "^18.2.0", + "uuid": "^11.0.5" + }, + "devDependencies": { + "@babel/plugin-proposal-decorators": "^7.25.7", + "@babel/preset-env": "^7.25.8", + "@babel/preset-react": "^7.25.7", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@types/webpack": "^5.28.5", + "copy-webpack-plugin": "^13.0.0", + "css-loader": "^7.1.2", + "html-webpack-plugin": "^5.6.2", + "style-loader": "^4.0.0", + "ts-loader": "^9.5.1", + "typescript": "^5.6.3", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^5.1.0" + } +} diff --git a/web/pom.xml b/web/pom.xml new file mode 100644 index 0000000..1ae56ab --- /dev/null +++ b/web/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + + ru.mskobaro + tdms + 0.0.1 + + + web + 0.0.1 + pom + + TDMS::WEB + + + + + com.github.eirslett + frontend-maven-plugin + 1.15.0 + + + install nodejs and npm + + install-node-and-npm + + generate-resources + + v20.12.0 + 10.5.0 + + + + install dependencies + + npm + + generate-resources + + install + + + + build frontend + + npm + + generate-resources + + run build + + + + + v20.12.0 + ./ + + + + org.apache.maven.plugins + maven-clean-plugin + 3.3.2 + + + + ./dist + + ** + + false + + + ./node + + ** + + false + + + + + + + diff --git a/web/src/Application.tsx b/web/src/Application.tsx new file mode 100644 index 0000000..30bc287 --- /dev/null +++ b/web/src/Application.tsx @@ -0,0 +1,34 @@ +import ReactDOM from 'react-dom/client' +import {RouterContext, RouterView, ViewMap} from "mobx-state-router"; +import {initApp} from "./utils/init"; +import React from "react"; +import 'bootstrap/dist/css/bootstrap.min.css'; +import './index.css' +import {RootStoreContext} from './utils/context'; +import {Home} from "./components/layout/Home"; +import {GroupListPage} from "./components/group/GroupListPage"; +import {Error} from "./components/layout/Error"; +import {ParticipantListPage} from "./components/participant/ParticipantListPage"; +import {DefenceListPage} from "./components/defence/DefenceListPage"; +import {PreparationDirectionListPage} from "./components/dictionary/PreparationDirectionList"; +import {DiplomaTopicListPage} from "./components/dictionary/DiplomaTopicList"; + +const viewMap: ViewMap = { + home: , + participantList: , + groupList: , + defenceList: , + themeList: , + preparationDirectionList: , + error: , +} + +const rootStore = initApp(); + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + +); diff --git a/web/src/components/NotificationContainer.tsx b/web/src/components/NotificationContainer.tsx new file mode 100644 index 0000000..91b96f5 --- /dev/null +++ b/web/src/components/NotificationContainer.tsx @@ -0,0 +1,88 @@ +import {ComponentContext} from "../utils/ComponentContext"; +import {observer} from "mobx-react"; +import {Card, CardBody, CardHeader, CardText, CardTitle} from "react-bootstrap"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {action, makeObservable} from "mobx"; +import {Notification, NotificationService, NotificationType} from "../services/NotificationService"; + +@observer +export class NotificationContainer extends ComponentContext { + forEachNotificationRender(notifications: Notification[], type: NotificationType) { + return notifications.map(notification => ( + + )); + } + + render() { + return
+ {this.forEachNotificationRender(NotificationService.errors, NotificationType.ERROR)} + {this.forEachNotificationRender(NotificationService.successes, NotificationType.SUCCESS)} + {this.forEachNotificationRender(NotificationService.warnings, NotificationType.WARNING)} + {this.forEachNotificationRender(NotificationService.infos, NotificationType.INFO)} +
+ } +} + +@observer +class NotificationPopup extends ComponentContext<{ notification: Notification, type: NotificationType }> { + + constructor(props: { notification: Notification, type: NotificationType }) { + super(props); + makeObservable(this); + } + + @action.bound + close() { + NotificationService.close(this.props.notification.uuid); + } + + get cardClassName() { + switch (this.props.type) { + case NotificationType.ERROR: + return 'text-bg-danger'; + case NotificationType.WARNING: + return 'text-bg-warning'; + case NotificationType.INFO: + return 'text-bg-info'; + case NotificationType.SUCCESS: + return 'text-bg-success'; + } + } + + render() { + const hasTitle = !!this.props.notification.title && this.props.notification.title.length > 0; + + const title = this.props.notification.title?.split('\n').map((item, key) => + {item}
); + const message = this.props.notification.message?.split('\n').map((item, key) => + {item}
); + const closeIcon = + + ; + + return + { + hasTitle && + + + + {title} + + {closeIcon} + + + } + + + + {message ?? ''} + + { + !hasTitle && + closeIcon + } + + + + } +} \ No newline at end of file diff --git a/web/src/components/controls/ReactiveControls.css b/web/src/components/controls/ReactiveControls.css new file mode 100644 index 0000000..43b8304 --- /dev/null +++ b/web/src/components/controls/ReactiveControls.css @@ -0,0 +1,25 @@ +.l-no-bg label::after { + background-color: rgba(0, 0, 0, 0) !important; +} + +.btn-group-lg > .btn, .btn-lg { + --bs-btn-border-radius: var(--bs-border-radius); +} + +.no-reaction { + transition: none !important; + background-color: rgba(255,255,255,0) !important; + color: inherit !important; + border: 0 !important; + box-shadow: none !important; +} + +.no-reaction:hover, +.no-reaction:focus, +.no-reaction:active { + transition: none !important; + background-color: rgba(255,255,255,0) !important; + color: inherit !important; + border: 0 !important; + box-shadow: none !important; +} \ No newline at end of file diff --git a/web/src/components/controls/ReactiveControls.tsx b/web/src/components/controls/ReactiveControls.tsx new file mode 100644 index 0000000..f809c78 --- /dev/null +++ b/web/src/components/controls/ReactiveControls.tsx @@ -0,0 +1,451 @@ +import React, { + ChangeEvent, + Component +} from "react"; +import { + ReactiveValue +} from "../../utils/reactive/reactiveValue"; +import { + observer +} from "mobx-react"; +import { + action, + makeObservable, + observable, + runInAction +} from "mobx"; +import { + Badge, + Button, + ButtonGroup, + Dropdown, + DropdownItem, + DropdownMenu, + DropdownToggle, + FloatingLabel, + FormCheck, + FormControl, + FormSelect, + FormText +} from "react-bootstrap"; +import { + FontAwesomeIcon +} from "@fortawesome/react-fontawesome"; +import './ReactiveControls.css'; +import { + TableDescriptor +} from "../../utils/tables"; +import { + DataTable +} from "../data-tables/DataTable"; +import { + ModalState +} from "../../utils/modalState"; + +export interface ReactiveInputProps { + value: ReactiveValue; + label?: string; + disabled?: boolean; + className?: string; + validateless?: boolean; +} + +/* Boolean Input */ + +@observer +export class BooleanInput extends Component> { + constructor(props: any) { + super(props); + makeObservable(this); + if (this.props.value.value === undefined) { + this.props.value.setAuto(false); + } + this.props.value.setField(this.props.label); + } + + @action.bound + onChange(event: React.ChangeEvent) { + this.props.value.set(event.currentTarget.checked); + } + + render() { + return
+
+ {this.props.label} + +
+ +
+ } +} + +/* String Input */ + +@observer +export class StringInput extends Component> { + constructor(props: any) { + super(props); + makeObservable(this); + if (this.props.value.value === undefined) { + this.props.value.setAuto(''); + } + this.props.value.setField(this.props.label); + } + + @action.bound + onChange(event: React.ChangeEvent) { + this.props.value.set(event.currentTarget.value); + } + + render() { + const inputClassName = `${this.props.validateless ? '' : this.props.value.invalid ? 'bg-danger' : this.props.value.touched ? 'bg-success' : ''} bg-opacity-10`; + + return
+ + + + +
+ } +} + +/* Password Input */ + +@observer +export class PasswordInput extends Component> { + @observable showPassword = false; + + constructor(props: any) { + super(props); + makeObservable(this); + if (this.props.value.value === undefined) { + this.props.value.setAuto(''); + } + this.props.value.setField(this.props.label); + } + + @action.bound + onChange(event: React.ChangeEvent) { + this.props.value.set(event.currentTarget.value); + } + + @action.bound + toggleShowPassword() { + this.showPassword = !this.showPassword; + } + + render() { + return
+
+ + + + +
+ +
+ } +} + +/* Select Input */ + +export interface SelectInputValue { + value: string; + label: string; +} + +export interface SelectInputProps extends ReactiveInputProps { + possibleValues: SelectInputValue[]; + unselectedLabel?: string; + initial?: SelectInputValue; +} + +@observer +export class SelectInput extends Component { + constructor(props: SelectInputProps) { + super(props); + makeObservable(this); + runInAction(() => { + this.options = props.possibleValues; + this.value = props.value; + this.value.setField(this.props.label); + + if (this.value.value === undefined && this.props.unselectedLabel !== undefined) { + this.options.unshift({ + value: '__unselected__', + label: this.props.unselectedLabel + }); + this.value.setAuto(this.options[0]); + } else if (this.props.initial) { + this.value.set(this.props.initial); + } else { + this.value.set(this.options[0]); + } + }); + } + + @observable options: { + value: string, + label: string + }[]; + @observable value: ReactiveValue; + + @action.bound + onChange(event: ChangeEvent) { + this.value.set(this.options.find(option => option.value === event.currentTarget.value)); + } + + render() { + const inputClassName = `${this.props.validateless ? '' : this.props.value.invalid ? 'bg-danger' : this.props.value.touched ? 'bg-success' : ''} bg-opacity-10`; + + return
+ { + + + { + this.options.map(option => + ) + } + + + } + +
+ } +} + +/* Multiple select */ + +export interface MultipleSelectInputProps extends ReactiveInputProps { + possibleValues: SelectInputValue[]; + singleSelect?: boolean; + filtering?: boolean; +} + +@observer +export class DropdownSelectInput extends Component { + constructor(props: MultipleSelectInputProps) { + super(props); + makeObservable(this); + if (this.props.value.value === undefined) { + this.props.value.setAuto([]); + } + this.initField(this.props); + runInAction(() => { + this.options = props.possibleValues; + }); + } + + componentDidUpdate(prevProps: Readonly) { + if (this.value != prevProps.value) { + this.initField(this.props); + } + } + + @action.bound + initField(prps: MultipleSelectInputProps) { + this.value = prps.value; + this.value.setField(prps.label); + } + + @observable options: SelectInputValue[]; + @observable value: ReactiveValue; + @observable filter: string = ''; + + @action.bound + onCloseClick(event: React.MouseEvent) { + event.stopPropagation(); + const value = (event.target as HTMLElement).closest('span').getAttribute('data-value'); + this.value.set(this.value.value.filter(sel => sel.value !== value)); + } + + @action.bound + onAddClick(event: React.MouseEvent) { + const value = (event.target as HTMLElement).getAttribute('data-value'); + const option = this.options.find(opt => opt.value === value); + if (option) { + let contains = this.value.value.find(sel => sel.value === value); + if (contains) { + this.value.set(this.value.value.filter(sel => sel.value !== value)); + } else { + if (this.props.singleSelect) { + this.value.set([option]); + } else { + this.value.set([...this.value.value, option]); + } + } + } + } + + @action.bound + onFilterChange(event: React.ChangeEvent) { + this.filter = (event.target as HTMLInputElement).value; + if (this.filter === '') { + this.options = this.props.possibleValues; + } else { + this.options = this.props.possibleValues.filter(opt => opt.label.toLowerCase().includes(this.filter.toLowerCase())); + } + } + + render() { + const inputClassName = `${this.props.validateless ? '' : this.props.value.invalid ? 'bg-danger' : this.props.value.touched ? 'bg-success' : ''} bg-opacity-10`; + const inputDisabledBackgroundStyle = this.props.disabled ? {backgroundColor: 'rgb(233, 236, 239)'} : {}; + const inputDisabledColor = this.props.disabled ? '#000000' : 'rgb(33, 37, 41)'; + + return
+ { + + + + { + !this.props.disabled && + + } + + + + { + this.props.filtering && +
+ +
+ } + { + this.options?.map(option => + + {option.label} + ) + } +
+
+ } + +
+ } +} + +/* Table */ + +export class TableInputNewEntryState { + constructor() { + makeObservable(this); + } + + @observable newEntryCallback: (entry: T) => void; + + @action.bound + newEntry(entry: T) { + this.newEntryCallback && this.newEntryCallback(entry); + } +} + +export interface TableInputProps { + table: TableDescriptor; + searchNewEntryState: TableInputNewEntryState; + searchNewEntryModalState: ModalState; + disabled?: boolean; +} + +@observer +export class TableInput extends Component> { + @observable table: TableDescriptor = this.props.table; + @observable searchNewEntryState: TableInputNewEntryState = this.props.searchNewEntryState; + @observable searchNewEntryModalState: ModalState = this.props.searchNewEntryModalState; + @observable disabled: boolean = this.props.disabled; + + constructor(props: any) { + super(props); + makeObservable(this); + this.table.pageable = false; + this.searchNewEntryState.newEntryCallback = this.addNewEntry; + } + + @action.bound + addNewEntry(entry: T) { + this.table.data.push(entry); + } + + componentDidUpdate() { + runInAction(() => { + this.table = this.props.table; + this.searchNewEntryState = this.props.searchNewEntryState; + this.searchNewEntryModalState = this.props.searchNewEntryModalState; + this.disabled = this.props.disabled; + }); + } + + render() { + return + } +} diff --git a/web/src/components/data-tables/DataTable.css b/web/src/components/data-tables/DataTable.css new file mode 100644 index 0000000..0e82542 --- /dev/null +++ b/web/src/components/data-tables/DataTable.css @@ -0,0 +1,4 @@ +._table-header:hover { + background: #f5f5f5; +} + diff --git a/web/src/components/data-tables/DataTable.tsx b/web/src/components/data-tables/DataTable.tsx new file mode 100644 index 0000000..d29f4e5 --- /dev/null +++ b/web/src/components/data-tables/DataTable.tsx @@ -0,0 +1,334 @@ +import {ComponentContext} from "../../utils/ComponentContext"; +import {TableDescriptor} from "../../utils/tables"; +import {observer} from "mobx-react"; +import {action, computed, makeObservable, observable, runInAction} from "mobx"; +import {Button, ButtonGroup, FormSelect, FormText, Table} from "react-bootstrap"; +import _ from "lodash"; +import React, {ChangeEvent} from "react"; +import {ModalState} from "../../utils/modalState"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import './DataTable.css'; + +export interface DataTableProps { + tableDescriptor: TableDescriptor; + filterModalState?: ModalState; + name?: string; + headless?: boolean; + editable?: boolean; + editableModalState?: ModalState; + additionalButtons?: React.ReactNode; +} + +@observer +export class DataTable extends ComponentContext & { className?: string }> { + constructor(props: DataTableProps) { + super(props); + makeObservable(this); + } + + @observable descriptor = this.props.tableDescriptor; + @observable name = this.props.name; + @observable headless = this.props.headless; + @observable filterModalState = this.props.filterModalState; + @observable editable = this.props.editable; + @observable editableModalState = this.props.editableModalState; + @observable className = this.props.className; + @observable additionalButtons?: React.ReactNode = this.props.additionalButtons; + + componentDidUpdate() { + runInAction(() => { + this.descriptor = this.props.tableDescriptor; + this.name = this.props.name; + this.headless = this.props.headless; + this.filterModalState = this.props.filterModalState; + this.editable = this.props.editable; + this.editableModalState = this.props.editableModalState; + this.className = this.props.className; + this.additionalButtons = this.props.additionalButtons; + }); + } + + @computed + get isFirstPage() { + return this.descriptor.page === 0; + } + + @computed + get isLastPage() { + return this.descriptor.page === Math.floor(this.descriptor.data.length / this.descriptor.pageSize) + || this.descriptor.pageSize === this.descriptor.data.length; + } + + @action.bound + goFirstPage() { + this.descriptor.page = 0; + } + + @action.bound + goLastPage() { + this.descriptor.page = Math.floor(this.descriptor.data.length / this.descriptor.pageSize); + } + + @action.bound + goNextPage() { + this.descriptor.page++; + } + + @action.bound + goPrevPage() { + this.descriptor.page--; + } + + @action.bound + changePageSize(e: ChangeEvent) { + if (e.target.value === "\u221E") { + this.descriptor.pageSize = this.descriptor.data.length; + return; + } + + this.descriptor.page = 0; + this.descriptor.pageSize = _.toNumber(e.target.value); + } + + // not computed, since we want to show initial data, when no sorts applied + get filteredData() { + const filters = this.descriptor.filters.filter(filter => filter); + return this.descriptor.data.filter(row => ((filters && filters.length) > 0 ? filters.every(filter => filter(row)) : true)); + } + + render() { + const style = { + border: '1px solid #dee2e6', + borderTopLeftRadius: this.headless ? '0.25rem' : '0', + borderTopRightRadius: this.headless ? '0.25rem' : '0', + borderBottomLeftRadius: this.descriptor.pageable ? '0' : '0.25rem', + borderBottomRightRadius: this.descriptor.pageable ? '0' : '0.25rem', + } + + return
+ { + !this.headless && + this.header + } +
+ + + {this.tableHeader} + + + {this.body} + +
+
+ { + this.descriptor.pageable && + this.footer + } +
+ } + + @computed + get header() { + const style = { + borderTop: '1px solid #dee2e6', + borderTopLeftRadius: '0.25rem', + borderTopRightRadius: '0.25rem', + borderLeft: '1px solid #dee2e6', + borderRight: '1px solid #dee2e6', + } + + return
+
+ {this.name} +
+ { + this.additionalButtons && + this.additionalButtons + } + + { + this.descriptor.pageable && +
{`Записей на странице: ${this.descriptor.pageSize} `}
+ } +
+ { + <> + {`Всего записей: ${this.filteredData.length}`} + + { + this.descriptor.pageable && + <> + {` (${this.descriptor.page + 1}/${Math.ceil(this.filteredData.length / this.descriptor.pageSize)})`} + + } + + { + this.filterModalState && +
+ +
+ } + + + } +
+
+
+
+
+ } + + @computed + get tableHeader() { + return <> + + { + this.descriptor.columns.map((column, i) => { + const firstColumn = i === 0; + const lastColumn = i === this.descriptor.columns.length - 1; + const style = { + borderLeft: firstColumn ? 'none' : '1px solid var(--bs-table-border-color)', + borderRight: lastColumn ? 'none' : '1px solid var(--bs-table-border-color)', + }; + + return +
runInAction(() => { + const other = this.descriptor.columns + .filter(c => c.key !== column.key) + .map(c => c.sort); + column.sort.toggle(other); + })}> + {column.title} + + { +
+ {column.sort.apply ? column.sort.order === 'asc' ? '▲' : '▼' : '△▽'} + { + column.sort.apply && + column.sort.applyOrder + } +
+ } +
+
+ + } + ) + } + + + } + + @computed + get body() { + const firstColumnKey = this.descriptor.columns[0].key; + const sortingColumns = this.descriptor.columns.filter(column => column.sort.apply).sort((a, b) => a.sort.applyOrder - b.sort.applyOrder); + const masterComparator = (a: any /*row*/, b: any /*row*/) => { + for (const column of sortingColumns) { + const sortKey = column.key; + const result = column.sort.comparator(a[sortKey], b[sortKey]); + if (result !== 0) { + return column.sort.order === 'asc' ? result : -result; + } + } + return 0; + } + + let elements = this.filteredData + .sort(masterComparator) + .slice(this.descriptor.page * this.descriptor.pageSize, (this.descriptor.page + 1) * this.descriptor.pageSize) + .map((row, i) => { + const rowAny = row as any; + const lastRow = i === this.descriptor.pageSize - 1; + return + { + this.descriptor.columns.map(column => { + const firstColumn = column === this.descriptor.columns[0]; + const lastColumn = column === this.descriptor.columns[this.descriptor.columns.length - 1]; + const suffixElement = column.suffixElement ? column.suffixElement(row) : undefined; + const style = { + borderLeft: firstColumn ? 'none' : '1px solid var(--bs-table-border-color)', + borderRight: lastColumn ? 'none' : '1px solid var(--bs-table-border-color)', + borderBottom: lastRow && !this.editable ? 'none' : '1px solid var(--bs-table-border-color)', + } + return + {column.format(rowAny[column.key], row)} + { + suffixElement && + {suffixElement} + } + + }) + } + + }); + + if (this.editable) { + elements.push( + + + + ); + } + + return elements; + } + + @computed + get footer() { + const style = { + borderBottom: '1px solid #dee2e6', + borderBottomLeftRadius: '0.25rem', + borderBottomRightRadius: '0.25rem', + borderLeft: '1px solid #dee2e6', + borderRight: '1px solid #dee2e6', + width: '100%', + height: '40px', + } + + const buttonSizeStyle = { + width: '30px', + height: '30px', + padding: '0', + } + + return
+ + + + + + + + + + + + + + +
+ } +} \ No newline at end of file diff --git a/web/src/components/defence/DefenceListPage.tsx b/web/src/components/defence/DefenceListPage.tsx new file mode 100644 index 0000000..3a0a0a6 --- /dev/null +++ b/web/src/components/defence/DefenceListPage.tsx @@ -0,0 +1,74 @@ +import {observer} from "mobx-react"; +import {Page} from "../layout/Page"; +import {action, makeObservable, observable, reaction, runInAction} from "mobx"; +import {get} from "../../utils/request"; +import {Group} from "../../models/group"; +import React from "react"; +import {ThinkService} from "../../services/ThinkService"; +import {DataTable} from "../data-tables/DataTable"; +import {Column, TableDescriptor} from "../../utils/tables"; +import {Defence} from "../../models/defence"; +import {Participant} from "../../models/participant"; +import {datetimeConverter} from "../../utils/converters"; +import {StudentData} from "../../models/studentData"; + +class DefenceListPageState { + constructor() { + makeObservable(this); + this.updateDefences(); + reaction(() => this.defences, () => { + this.tableDescriptor = new TableDescriptor([ + new Column('commissionMembers', 'ГЭК', p => p ? p.length : 0), + new Column('groups', 'Группы', g => g && g.length ? g.map(g => g.name).join(', ') : 'Пусто'), + new Column('groups', 'Студентов', g => { + if (!g) return 'Пусто'; + const students: StudentData[] = []; + g.forEach(group => group.students.forEach(student => students.push(student))); + return students.length; + }), + new Column('createdAt', 'Дата создания', datetimeConverter), + new Column('updatedAt', 'Дата обновления', datetimeConverter), + ], this.defences, true + ); + }); + } + + @observable defences: Defence[] = []; + @observable tableDescriptor: TableDescriptor; + + @action.bound + updateDefences() { + ThinkService.think(); + get('/defence/all').then((defences) => { + runInAction(() => { + this.defences = defences; + }); + }).finally(() => { + ThinkService.completeAll(); + }); + } +} + +@observer +export class DefenceListPage extends Page { + constructor(props: any) { + super(props); + makeObservable(this); + this.fields = new DefenceListPageState(); + } + + @observable fields: DefenceListPageState; + + get page() { + return <> + { + <> + { + this.fields.tableDescriptor && + + } + + } + + } +} diff --git a/web/src/components/dictionary/DiplomaTopicList.tsx b/web/src/components/dictionary/DiplomaTopicList.tsx new file mode 100644 index 0000000..3703d08 --- /dev/null +++ b/web/src/components/dictionary/DiplomaTopicList.tsx @@ -0,0 +1,118 @@ +import {observer} from "mobx-react"; +import {Page} from "../layout/Page"; +import {action, makeObservable, observable, reaction, runInAction} from "mobx"; +import React from "react"; +import {DataTable} from "../data-tables/DataTable"; +import {Column, TableDescriptor} from "../../utils/tables"; +import {PreparationDirection} from "../../models/preparationDirection"; +import {get} from "../../utils/request"; +import {datetimeConverter} from "../../utils/converters"; +import {Button} from "react-bootstrap"; +import {ModalState} from "../../utils/modalState"; +import {UserService} from "../../services/UserService"; +import {DiplomaTopic} from "../../models/diplomaTopic"; +import {TeacherData} from "../../models/teacherData"; +import {fullName} from "../../models/participant"; +import DiplomaTopicModal from "./DiplomaTopicModal"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import _ from "lodash"; + +const getAllDiplomaTopics = () => { + return get("diploma-topic/all"); +} + +class DiplomaTopicPageState { + constructor() { + makeObservable(this); + this.updateDiplomaTopics(); + reaction(() => this.diplomaTopics, () => { + this.tableDescriptor = new TableDescriptor([ + new Column('name', 'Название', undefined, (dp) => { + return ; + }), + new Column('teacher', 'Преподаватель', (value) => { + return value ? fullName(value.participant) : "Не указан"; + }), + new Column('preparationDirection', 'Код направления подготовки', value => { + return value ? value.code : "Не указано"; + }), + new Column('preparationDirection', 'Название Направления подготовки', value => { + return value ? value.name : "Не указано"; + }), + new Column('createdAt', 'Дата создания', datetimeConverter), + new Column('updatedAt', 'Дата обновления', datetimeConverter), + ], this.diplomaTopics, true + ); + }, {fireImmediately: true}); + } + + @observable diplomaTopics: DiplomaTopic[] = []; + @observable selectedTopic: DiplomaTopic = undefined; + @observable tableDescriptor: TableDescriptor; + @observable diplomaTopicModalState = new ModalState(false, this.updateDiplomaTopics); + @observable diplomaTopicEditModalState = new ModalState(false, this.updateDiplomaTopics); + + @action.bound + updateDiplomaTopics() { + getAllDiplomaTopics().then(dt => { + runInAction(() => { + this.diplomaTopics = dt; + }) + }); + } + + @action.bound + onIconClicked(event: React.MouseEvent) { + const pd = this.diplomaTopics.find(pd => pd.id === _.toNumber(event.currentTarget.getAttribute('data-value'))); + if (pd) { + this.selectedTopic = pd; + this.diplomaTopicEditModalState.open(); + } + } +} + +@observer +export class DiplomaTopicListPage extends Page { + constructor(props: any) { + super(props); + makeObservable(this); + this.fields = new DiplomaTopicPageState(); + } + + @observable fields: DiplomaTopicPageState; + + get page() { + return <> + { + <> + { + this.fields.tableDescriptor && + } + /> + } + + { + this.fields.selectedTopic && + + } + + } + + } +} + +const AdditionalButtons = observer(({state}: {state: DiplomaTopicPageState}) => { + return ( +
+ {(UserService.isSecretary || UserService.isAdministrator) && ( + + )} +
+ ); +}); + + diff --git a/web/src/components/dictionary/DiplomaTopicModal.tsx b/web/src/components/dictionary/DiplomaTopicModal.tsx new file mode 100644 index 0000000..18d7366 --- /dev/null +++ b/web/src/components/dictionary/DiplomaTopicModal.tsx @@ -0,0 +1,180 @@ +import {action, makeObservable, observable, runInAction} from "mobx"; +import {ComponentContext} from "../../utils/ComponentContext"; +import {ModalState} from "../../utils/modalState"; +import {observer} from "mobx-react"; +import {Button, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle} from "react-bootstrap"; +import {DropdownSelectInput, SelectInputValue, StringInput} from "../controls/ReactiveControls"; +import {ReactiveValue} from "../../utils/reactive/reactiveValue"; +import {NotificationService} from "../../services/NotificationService"; +import {get, post} from "../../utils/request"; +import {Group} from "../../models/group"; +import {required, strLength, strPattern} from "../../utils/reactive/validators"; +import _ from "lodash"; +import {fullName, Participant} from "../../models/participant"; +import {datetimeConverter} from "../../utils/converters"; +import {StudentData} from "../../models/studentData"; +import {PreparationDirection} from "../../models/preparationDirection"; +import {DiplomaTopic} from "../../models/diplomaTopic"; +import {TeacherData} from "../../models/teacherData"; + +const getAllTeachers = () => { + return get("teacher-data/get-all"); +} + +const getAllPreparationDirections = () => { + return get("prep-direction/get-all"); +} + +class DiplomaTopicModalState { + constructor(modalState: ModalState, topic: DiplomaTopic) { + makeObservable(this); + this.modalState = modalState; + getAllTeachers().then(teachers => { + runInAction(() => { + this.allTeachers = teachers; + }); + }); + getAllPreparationDirections().then(preparationDirections => { + runInAction(() => { + this.allPreparationDirections = preparationDirections; + }); + }); + + if (topic) { + this.editMode = false; + this.adding = false; + this.name.setAuto(topic.name); + this.teacher.setAuto([{ + value: _.toString(topic.id), + label: topic.name, + }]); + this.preparationDirection.setAuto([{ + value: _.toString(topic.preparationDirection.id), + label: topic.preparationDirection.name, + }]); + this.topicId = topic.id; + } + } + + @observable modalState: ModalState; + @observable editMode = true; + @observable adding = true; + @observable topicId: number = undefined; + + @observable allTeachers: TeacherData[] = []; + @observable allPreparationDirections: PreparationDirection[] = []; + + @observable name = new ReactiveValue().setAuto("").addValidator(required); + @observable teacher = new ReactiveValue(); + @observable preparationDirection = new ReactiveValue(); + + anyInvalid(): boolean { + let invalid = this.name.invalid + || this.teacher.invalid + || this.preparationDirection.invalid; + + if (this.adding) { + invalid = invalid || !this.name.touched + } + + return invalid; + } + + touchAll() { + this.name.touch(); + this.teacher.touch(); + this.preparationDirection.touch(); + } + + @action.bound + create() { + if (this.anyInvalid()) { + this.touchAll(); + NotificationService.error("Пожалуйста, исправьте ошибки на форме"); + return; + } + + post("diploma-topic/save", { + id: this.topicId, + name: this.name.value, + teacher: this.teacher.value && this.teacher.value[0] + ? this.getTeacherById(_.toNumber(this.teacher.value[0].value)) + : undefined, + preparationDirection: this.preparationDirection.value && this.preparationDirection.value[0] + ? this.getPreparationDirectionById(_.toNumber(this.preparationDirection.value[0].value)) + : undefined, + }).then(() => { + if (this.adding) + NotificationService.success(`Тема ВКР ${this.name.value} успешно добавлена`); + else + NotificationService.success(`Тема ВКР ${this.name.value} успешно изменена`); + if (this.modalState) this.modalState.onApply(); + }); + + this.modalState.close(); + } + + getTeacherById(id: number): TeacherData | undefined { + return this.allTeachers.find(teacher => teacher.id === id); + } + + getPreparationDirectionById(id: number): PreparationDirection | undefined { + return this.allPreparationDirections.find(direction => direction.id === id); + } + + @action.bound + changeEditMode() { + this.editMode = !this.editMode; + } +} + +@observer +export default class DiplomaTopicModal extends ComponentContext<{ modalState: ModalState, topic?: DiplomaTopic}> { + constructor(props: any) { + super(props); + makeObservable(this); + runInAction(() => { + this.fields = new DiplomaTopicModalState(props.modalState, props.topic); + }); + } + + @observable fields: DiplomaTopicModalState; + + render() { + return ( + + + { + this.fields.editMode + ? "Редактирование темы ВКР" + : "Новая тема ВКР" + } + + + + { + return {label: fullName(value.participant), value: value.id?.toString() || ""}; + })} + value={this.fields.teacher} disabled={!this.fields.editMode}/> + { + return {label: value.name, value: value.id?.toString() || ""}; + })} + value={this.fields.preparationDirection} disabled={!this.fields.editMode}/> + + + { + !this.fields.editMode && + + } + { + this.fields.editMode && + + } + + + + ); + } +} \ No newline at end of file diff --git a/web/src/components/dictionary/PreparationDirectionList.tsx b/web/src/components/dictionary/PreparationDirectionList.tsx new file mode 100644 index 0000000..61cbf3b --- /dev/null +++ b/web/src/components/dictionary/PreparationDirectionList.tsx @@ -0,0 +1,101 @@ +import {observer} from "mobx-react"; +import {Page} from "../layout/Page"; +import {action, makeObservable, observable, reaction, runInAction} from "mobx"; +import React from "react"; +import {DataTable} from "../data-tables/DataTable"; +import {Column, TableDescriptor} from "../../utils/tables"; +import {PreparationDirection} from "../../models/preparationDirection"; +import {get} from "../../utils/request"; +import {datetimeConverter} from "../../utils/converters"; +import {Button} from "react-bootstrap"; +import {ModalState} from "../../utils/modalState"; +import PreparationDirectionModal from "./PreparationDirectionModal"; +import {UserService} from "../../services/UserService"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import _ from "lodash"; + +const getAllPreparationDirections = () => { + return get("prep-direction/get-all"); +} + +class PreparationDirectionPageState { + constructor() { + makeObservable(this); + getAllPreparationDirections().then(pd => { + runInAction(() => { + this.preparationDirections = pd; + }) + }); + + reaction(() => this.preparationDirections, () => { + this.tableDescriptor = new TableDescriptor([ + new Column('code', 'Код', undefined, (pd) => { + return ; + }), + new Column('name', 'Название'), + new Column('createdAt', 'Дата создания', datetimeConverter), + new Column('updatedAt', 'Дата обновления', datetimeConverter), + ], this.preparationDirections, true + ); + }, {fireImmediately: true}); + } + + @observable preparationDirections: PreparationDirection[] = []; + @observable tableDescriptor: TableDescriptor; + @observable preparationDirectionModalState = new ModalState(); + @observable preparationDirectionEditModalState = new ModalState(); + @observable selectedDirection: PreparationDirection; + + @action.bound + onIconClicked(event: React.MouseEvent) { + const pd = this.preparationDirections.find(pd => pd.id === _.toNumber(event.currentTarget.getAttribute('data-value'))); + if (pd) { + this.selectedDirection = pd; + this.preparationDirectionEditModalState.open(); + } + } +} + +@observer +export class PreparationDirectionListPage extends Page { + constructor(props: any) { + super(props); + makeObservable(this); + this.fields = new PreparationDirectionPageState(); + } + + @observable fields: PreparationDirectionPageState; + + get page() { + return <> + { + <> + { + this.fields.tableDescriptor && + }/> + } + + { + this.fields.selectedDirection && + + } + + } + + } +} + +const AdditionalButtons = observer(({state}: {state: PreparationDirectionPageState}) => { + return ( +
+ {(UserService.isSecretary || UserService.isAdministrator) && ( + + )} +
+ ); +}); + diff --git a/web/src/components/dictionary/PreparationDirectionModal.tsx b/web/src/components/dictionary/PreparationDirectionModal.tsx new file mode 100644 index 0000000..d4a1d80 --- /dev/null +++ b/web/src/components/dictionary/PreparationDirectionModal.tsx @@ -0,0 +1,140 @@ +import {action, makeObservable, observable, runInAction} from "mobx"; +import {ComponentContext} from "../../utils/ComponentContext"; +import {ModalState} from "../../utils/modalState"; +import {observer} from "mobx-react"; +import {Button, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle} from "react-bootstrap"; +import {DropdownSelectInput, SelectInputValue, StringInput} from "../controls/ReactiveControls"; +import {ReactiveValue} from "../../utils/reactive/reactiveValue"; +import {NotificationService} from "../../services/NotificationService"; +import {get, post} from "../../utils/request"; +import {Group} from "../../models/group"; +import {required, strLength, strPattern} from "../../utils/reactive/validators"; +import _ from "lodash"; +import {fullName, Participant} from "../../models/participant"; +import {datetimeConverter} from "../../utils/converters"; +import {StudentData} from "../../models/studentData"; +import {PreparationDirection} from "../../models/preparationDirection"; + +class PreparationDirectionModalState { + constructor(modalState: ModalState, direction?: PreparationDirection) { + makeObservable(this); + this.modalState = modalState; + if (direction) { + console.log('PreparationDirectionModalState: initializing with direction', direction); + this.name.setAuto(direction.name); + this.code.setAuto(direction.code); + this.id = direction.id; + this.editMode = true; + } else { + this.name.setAuto(""); + this.code.setAuto(""); + this.viewMode = false; + } + } + + @observable modalState: ModalState; + @observable name = new ReactiveValue().addValidator(required); + @observable code = new ReactiveValue().addValidator(required); + @observable editMode = false; + @observable viewMode = true; + @observable id: number = undefined; + + anyInvalid(): boolean { + let invalid = this.name.invalid || this.code.invalid; + if (!this.editMode) { + invalid = invalid || !this.name.touched || !this.code.touched; + } + return invalid; + } + + touchAll() { + this.name.touch(); + this.code.touch(); + } + + @action.bound + save() { + if (this.anyInvalid()) { + this.touchAll(); + NotificationService.error("Пожалуйста, исправьте ошибки на форме"); + return; + } + + post("prep-direction/save", { + id: this.id, + name: this.name.value, + code: this.code.value + }).then(() => { + if (this.editMode) { + NotificationService.success(`Направление подготовки ${this.code.value} успешно обновлено`); + } else { + NotificationService.success(`Направление подготовки ${this.code.value} успешно добавлено`); + } + }); + + this.modalState.close(); + if (this.editMode) { + this.toView(); + } + } + + @action.bound + toEdit() { + this.viewMode = false; + } + + @action.bound + toView() { + this.viewMode = true; + } +} + +@observer +export default class PreparationDirectionModal extends ComponentContext<{ modalState: ModalState, direction?: PreparationDirection }> { + constructor(props: any) { + super(props); + makeObservable(this); + runInAction(() => { + this.fields = new PreparationDirectionModalState(props.modalState, props.direction); + }); + } + + componentDidUpdate(prevProps: Readonly<{ modalState: ModalState; direction?: PreparationDirection }>) { + if (this.props.direction !== prevProps.direction) { + runInAction(() => { + this.fields = new PreparationDirectionModalState(this.props.modalState, this.props.direction); + }); + } + } + + @observable fields: PreparationDirectionModalState; + + render() { + return ( + + + { + this.fields.editMode + ? "Редактирование направления подготовки " + this.fields.code.value + : "Добавление направления подготовки" + } + + + + + + + { + !this.fields.viewMode && + + } + { + this.fields.editMode && this.fields.viewMode && + + } + + + + ); + } +} \ No newline at end of file diff --git a/web/src/components/group/GroupListPage.tsx b/web/src/components/group/GroupListPage.tsx new file mode 100644 index 0000000..0a8036f --- /dev/null +++ b/web/src/components/group/GroupListPage.tsx @@ -0,0 +1,92 @@ +import {observer} from "mobx-react"; +import {Page} from "../layout/Page"; +import {action, makeObservable, observable, reaction, runInAction} from "mobx"; +import {get} from "../../utils/request"; +import {Group} from "../../models/group"; +import React from "react"; +import {ModalState} from "../../utils/modalState"; +import {ThinkService} from "../../services/ThinkService"; +import {DataTable} from "../data-tables/DataTable"; +import {Column, TableDescriptor} from "../../utils/tables"; +import {datetimeConverter} from "../../utils/converters"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import _ from "lodash"; +import {StudentData} from "../../models/studentData"; +import GroupProfileModal from "./GroupProfileModal"; +import {GroupListFilterModal} from "./GroupTableFilterModal"; + +class GroupListPageState { + constructor() { + makeObservable(this); + this.updateGroups(); + reaction(() => this.groups, () => { + this.tableDescriptor = new TableDescriptor([ + new Column('name', 'Название', undefined, (group) => { + return ; + }), + new Column('students', 'Студентов', v => v ? v.length : 0), + new Column('createdAt', 'Дата создания', datetimeConverter), + new Column('updatedAt', 'Дата обновления', datetimeConverter), + ], this.groups, true, this.filters + ); + }, {fireImmediately: true}); + } + + @observable filterModalState = new ModalState(); + @observable groupModalState = new ModalState(); + @observable groups: Group[] = []; + @observable filters: ((group: Group) => boolean)[] = []; + + @observable selectedGroup: Group = null; + @observable tableDescriptor: TableDescriptor; + + @action.bound + updateGroups() { + ThinkService.think(); + get('/group/get-all-groups').then((groups) => { + runInAction(() => { + this.groups = groups; + }); + }).finally(() => { + ThinkService.completeAll(); + }); + } + + @action.bound + openModal(event: React.MouseEvent) { + const group = this.groups.find(g => g.id === _.toNumber(event.currentTarget.getAttribute('data-value'))); + if (group) { + this.selectedGroup = group; + this.groupModalState.open(); + } + } +} + +@observer +export class GroupListPage extends Page { + constructor(props: {}) { + super(props); + makeObservable(this); + this.fields = new GroupListPageState(); + } + + @observable fields: GroupListPageState; + + get page() { + return <> + { + <> + { + this.fields.tableDescriptor && + + } + { + this.fields.selectedGroup && + + } + + + } + + } +} diff --git a/web/src/components/group/GroupProfileModal.tsx b/web/src/components/group/GroupProfileModal.tsx new file mode 100644 index 0000000..004991d --- /dev/null +++ b/web/src/components/group/GroupProfileModal.tsx @@ -0,0 +1,230 @@ +import {action, computed, makeObservable, observable, runInAction} from "mobx"; +import {ComponentContext} from "../../utils/ComponentContext"; +import {ModalState} from "../../utils/modalState"; +import {observer} from "mobx-react"; +import {Button, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle} from "react-bootstrap"; +import {DropdownSelectInput, SelectInputValue, StringInput} from "../controls/ReactiveControls"; +import {ReactiveValue} from "../../utils/reactive/reactiveValue"; +import {NotificationService} from "../../services/NotificationService"; +import {get, post} from "../../utils/request"; +import {Group} from "../../models/group"; +import {required, strLength, strPattern} from "../../utils/reactive/validators"; +import _ from "lodash"; +import {fullName} from "../../models/participant"; +import {datetimeConverter} from "../../utils/converters"; +import {StudentData} from "../../models/studentData"; +import {UserService} from "../../services/UserService"; +import PreparationDirectionModal from "../dictionary/PreparationDirectionModal"; +import {PreparationDirection} from "../../models/preparationDirection"; + +const mapStudent = (student: StudentData) => { + return {value: _.toString(student.id), label: fullName(student.participant)}; +} + +const mapDirOfPrep = (prep: PreparationDirection) => { + return {value: _.toString(prep.id), label: prep.code} +} + +const getAllDirOfPrep = () => { + return get("prep-direction/get-all"); +} + +class GroupProfileState { + constructor(props: GroupProfileModalProps) { + makeObservable(this); + this.modalState = props.modalState; + this.viewMode = !props.creation; + + this.group = props.group ?? {name: ""}; + this.groupName.setAuto(this.group.name); + this.createdAt.setAuto(datetimeConverter(this.group.createdAt)); + this.updatedAt.setAuto(datetimeConverter(this.group.updatedAt)); + this.students.setAuto(this.group.students?.map(mapStudent)); + if (this.group.preparationDirection) { + this.directionOfPreparation.setAuto([mapDirOfPrep(this.group.preparationDirection)]); + } + + get('student/all-without-group').then((students) => { + runInAction(() => { + this.allStudents = students; + }); + }); + + getAllDirOfPrep().then(dop => { + runInAction(() => { + this.allDirOfPrep = dop; + }); + }); + } + + @observable viewMode: boolean; + @observable editMode: boolean = false; + @observable group: Group; + @observable modalState: ModalState; + + @observable allDirOfPrep: PreparationDirection[] = []; + @observable allStudents: StudentData[] = []; + + @observable groupName = new ReactiveValue().addValidator(required).addValidator(strLength(3, 50)) + .addInputRestriction(strPattern(/^[а-яА-ЯёЁ0-9_-]*$/, "Имя группы должно содержать только русские буквы, цифры и символы _-")); + @observable students = new ReactiveValue(); + @observable directionOfPreparation = new ReactiveValue().addValidator(required); + + @observable createdAt = new ReactiveValue(); + @observable updatedAt = new ReactiveValue(); + + @action.bound + closeModal() { + this.modalState.close(); + this.setViewMode(); + } + + anyInvalid(): boolean { + let invalid = this.groupName.invalid || this.students.invalid || this.directionOfPreparation.invalid; + + if (!this.editMode) { + invalid = invalid || !this.groupName.touched || !this.directionOfPreparation.touched; + } + + return invalid; + } + + touchAll() { + this.groupName.touch(); + this.students.touch(); + this.directionOfPreparation.touch(); + } + + @action.bound + save() { + if (this.anyInvalid()) { + this.touchAll(); + NotificationService.error("Пожалуйста, исправьте ошибки на форме"); + return; + } + + const studentIds: StudentData[] = this.students.value.map((s) => ({ + id: _.toNumber(s.value) + })); + + post("group/save", { + id: this.group.id ? this.group.id : undefined, + name: this.groupName.value, + students: studentIds, + preparationDirection: this.directionOfPreparation.value && this.directionOfPreparation.value[0] + ? {id: this.directionOfPreparation.value[0].value} + : undefined, + }).then(() => { + if (this.editMode) + NotificationService.success(`Профиль группы ${this.groupName.value} успешно обновлен`); + else + NotificationService.success(`Профиль группы ${this.groupName.value} успешно создан`); + }).finally(() => { + this.closeModal(); + }); + } + + @action.bound + setEditMode() { + this.editMode = true; + this.viewMode = false; + } + + @action.bound + setViewMode() { + this.editMode = false; + this.viewMode = true; + } + + @computed + get canManipulateGroups() { + return UserService.isSecretary || UserService.isAdministrator; + } + + @action.bound + deleteGroup() { + post(`group/delete?id=${this.group.id}`).then(() => { + NotificationService.success(`Группа ${this.groupName.value} удалена`); + }); + + this.closeModal(); + } +} + +interface GroupProfileModalProps { + creation?: boolean; + modalState: ModalState; + group?: Group; +} + +@observer +export default class GroupProfileModal extends ComponentContext { + constructor(props: GroupProfileModalProps) { + super(props); + makeObservable(this); + runInAction(() => { + this.fields = new GroupProfileState(this.props); + }); + } + + componentDidUpdate(prevProps: Readonly) { + if (prevProps.group !== this.props.group) { + runInAction(() => { + this.fields = new GroupProfileState(this.props); + }); + } + } + + @observable fields: GroupProfileState; + + render() { + return ( + + + + { + this.props.creation + ? `Создание группы` + : this.fields.editMode + ? `Редактирование профиля группы ${this.fields.groupName.value}` + : `Профиль группы ${this.fields.groupName.value}` + } + + + + + + + { + this.fields.viewMode && + <> + + + + } + + + { + this.props.creation && this.fields.canManipulateGroups && + + } + { + this.fields.editMode && this.fields.canManipulateGroups && + + } + { + !this.props.creation && !this.fields.editMode && this.props.group && this.fields.canManipulateGroups && + + } + { + !this.props.creation && this.props.group && this.fields.canManipulateGroups && !this.fields.editMode && + + } + + + + ); + } +} \ No newline at end of file diff --git a/web/src/components/group/GroupTableFilterModal.tsx b/web/src/components/group/GroupTableFilterModal.tsx new file mode 100644 index 0000000..b15821e --- /dev/null +++ b/web/src/components/group/GroupTableFilterModal.tsx @@ -0,0 +1,54 @@ +import {ModalState} from "../../utils/modalState"; +import {Group} from "../../models/group"; +import {observer} from "mobx-react"; +import React, {Component} from "react"; +import {action, makeObservable, observable, runInAction} from "mobx"; +import {ReactiveValue} from "../../utils/reactive/reactiveValue"; +import {Button, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle} from "react-bootstrap"; +import {StringInput} from "../controls/ReactiveControls"; + +export interface GroupListFilterProps { + modalState: ModalState; + filters: ((group: Group) => boolean)[]; +} + +@observer +export class GroupListFilterModal extends Component { + constructor(props: GroupListFilterProps) { + super(props); + makeObservable(this); + + runInAction(() => { + this.filters.push(this.nameFilter); + }); + } + + @observable filters = this.props.filters; + @observable modalState = this.props.modalState; + @observable nameField = new ReactiveValue().syncWithParam('name'); + + @observable nameFilter = (group: Group) => { + if (!this.nameField.value) return true; + return group.name?.includes(this.nameField.value) + }; + + @action.bound + reset() { + this.nameField.set(""); + } + + render() { + return + + Фильтр + + + + + + + + + + } +} diff --git a/web/src/components/layout/Error.tsx b/web/src/components/layout/Error.tsx new file mode 100644 index 0000000..7c341f0 --- /dev/null +++ b/web/src/components/layout/Error.tsx @@ -0,0 +1,9 @@ +import {observer} from "mobx-react"; +import {Page} from "./Page"; + +@observer +export class Error extends Page { + get page() { + return

Error

+ } +} diff --git a/web/src/components/layout/Footer.tsx b/web/src/components/layout/Footer.tsx new file mode 100644 index 0000000..ef03e5b --- /dev/null +++ b/web/src/components/layout/Footer.tsx @@ -0,0 +1,43 @@ +import {ComponentContext} from "../../utils/ComponentContext"; +import {observer} from "mobx-react"; +import {makeObservable} from "mobx"; +import {Container, Nav, Navbar, NavbarText, NavLink} from "react-bootstrap"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {findIconDefinition} from "@fortawesome/fontawesome-svg-core"; +import {ThinkService} from "../../services/ThinkService"; +import {SysInfoService} from "../../services/SysInfoService"; + +@observer +export class Footer extends ComponentContext { + + constructor(props: any) { + super(props); + makeObservable(this); + } + + render() { + return
+ + +
+ Thesis Defence Management System — + { + ThinkService.isThinking('updateVersion') && + + } + { + !ThinkService.isThinking('updateVersion') && + {SysInfoService.version} + } +
+ + +
+
+
+ } +} diff --git a/web/src/components/layout/Header.tsx b/web/src/components/layout/Header.tsx new file mode 100644 index 0000000..9e33dad --- /dev/null +++ b/web/src/components/layout/Header.tsx @@ -0,0 +1,118 @@ +import {Container, Nav, Navbar, NavDropdown} from "react-bootstrap"; +import {RouterLink} from "mobx-state-router"; +import {observer} from "mobx-react"; +import {post} from "../../utils/request"; +import {UserLoginModal} from "../user/UserLoginModal"; +import {ModalState} from "../../utils/modalState"; +import {action, makeObservable, observable} from "mobx"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {ComponentContext} from "../../utils/ComponentContext"; +import {NotificationService} from "../../services/NotificationService"; +import {ThinkService} from "../../services/ThinkService"; +import ParticipantProfileModal from "../participant/ParticipantProfileModal"; +import GroupProfileModal from "../group/GroupProfileModal"; +import {fullName} from "../../models/participant"; +import {UserService} from "../../services/UserService"; + +@observer +export class Header extends ComponentContext { + constructor(props: any) { + super(props); + makeObservable(this); + } + + @observable loginModalState = new ModalState(); + @observable addGroupModalState = new ModalState(); + @observable userRegistrationModalState = new ModalState(); + + render() { + let userThink = ThinkService.isThinking('updateCurrentUser'); + + return <> +
+ + + + TDMS + + + + + +
+ + + + + } +} + +@observer +class AuthenticatedItems extends ComponentContext { + constructor(props: any) { + super(props); + makeObservable(this); + } + + @observable userParticipantProfileModalState = new ModalState(); + + @action.bound + logout() { + post('user/logout').then(() => { + UserService.updateCurrentUser(); + this.routerStore.goTo('home').then(); + NotificationService.success('Вы успешно вышли из системы', 'Выход'); + }); + } + + render() { + return <> + Пользователь: + + Мой профиль + + Выйти + + + + + } +} diff --git a/web/src/components/layout/Home.tsx b/web/src/components/layout/Home.tsx new file mode 100644 index 0000000..4dd0d64 --- /dev/null +++ b/web/src/components/layout/Home.tsx @@ -0,0 +1,18 @@ +import {Page} from "./Page"; +import {observer} from "mobx-react"; +import {makeObservable} from "mobx"; + +@observer +export class Home extends Page { + + constructor(props: any) { + super(props); + makeObservable(this); + } + + get page() { + return <> +

home

+ + } +} diff --git a/web/src/components/layout/Page.tsx b/web/src/components/layout/Page.tsx new file mode 100644 index 0000000..0d5b6b8 --- /dev/null +++ b/web/src/components/layout/Page.tsx @@ -0,0 +1,41 @@ +import {ReactNode} from "react"; +import {Container} from "react-bootstrap"; +import {Header} from "./Header"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {ComponentContext} from "../../utils/ComponentContext"; +import {NotificationContainer} from "../NotificationContainer"; +import {Footer} from "./Footer"; +import {ThinkService} from "../../services/ThinkService"; + +export abstract class Page extends ComponentContext { + get page(): ReactNode { + throw new Error('This is not abstract method, ' + + 'because mobx cant handle abstract methods. ' + + 'Please override this method in child class. ' + + 'Do not call it directly.'); + } + + render() { + const thinking = ThinkService.isThinking(); + + return <> +
+ + + { + thinking && +
+ +
+ } + { + !thinking && + <> + {this.page} + + } +
+