diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8cd968c38187..7e405ef32689 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,8 @@ #### Server -- [ ] **Important**: I implemented the changes with a very good performance and prevented too many (unnecessary) database calls. +- [ ] **Important**: I implemented the changes with a [very good performance](https://docs.artemis.cit.tum.de/dev/guidelines/performance/) and prevented too many (unnecessary) and too complex database calls. +- [ ] I **strictly** followed the principle of **data economy** for all database calls. - [ ] I **strictly** followed the [server coding and design guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/server/). - [ ] I added multiple integration tests (Spring) related to the features (with a high test coverage). - [ ] I added pre-authorization annotations according to the [guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/server/#rest-endpoint-best-practices-for-authorization) and checked the course groups for all new REST Calls (security). @@ -21,7 +22,8 @@ #### Client -- [ ] **Important**: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data. +- [ ] **Important**: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging). +- [ ] I **strictly** followed the principle of **data economy** for all client-server REST calls. - [ ] I **strictly** followed the [client coding and design guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/client/). - [ ] Following the [theming guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/client-design/), I specified colors only in the theming variable files and checked that the changes look consistent in both the light and the dark theme. - [ ] I added multiple integration tests (Jest) related to the features (with a high test coverage), while following the [test guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/client-tests/). @@ -92,8 +94,8 @@ Prerequisites: #### Performance Review -- [ ] I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance -- [ ] I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance +- [ ] I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students. +- [ ] I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students. #### Code Review - [ ] Code Review 1 - [ ] Code Review 2 @@ -103,6 +105,9 @@ Prerequisites: #### Exam Mode Test - [ ] Test 1 - [ ] Test 2 +#### Performance Tests +- [ ] Test 1 +- [ ] Test 2 ### Test Coverage diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 89ee014c27e2..d4887bcca43e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,76 +1,133 @@ -# Contributor Covenant Code of Conduct + +# Artemis Code of Conduct ## Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to creating a positive environment -include: +Examples of behavior that contributes to a positive environment for our +community include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission +* Publishing others' private information, such as a physical or email address, + without their explicit permission * Other conduct which could reasonably be considered inappropriate in a - professional setting + professional setting -## Our Responsibilities +## Enforcement Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. ## Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project lead Stephan Krusche at krusche@tum.de. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. -[homepage]: https://www.contributor-covenant.org +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea3d5c107479..482bdb00df6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,43 @@ -# Contributing Guide for Artemis +# Contribution Guidelines for Artemis Read the [setup guide](https://docs.artemis.cit.tum.de/dev/setup.html) on how to set up your local development environment. -Before creating a pull request, please read the [guidelines to the development process](https://docs.artemis.cit.tum.de/dev/development-process/development-process.html) as well as the [coding and design guidelines](https://docs.artemis.cit.tum.de/dev/guidelines.html). +## Identity and Transparency + +To ensure a transparent and trustworthy environment, we have established different guidelines for members of our organization and external contributors. + +### For Members of Our Organization + +1. **Real Names Required**: As a member of our organization, you must use your full real name in your GitHub profile. This is a prerequisite for joining our organization. Using a real name is crucial for building trust within the team and the broader community. It fosters accountability and transparency, which are essential for collaborative work. When members use their real identities, it encourages open communication and strengthens professional relationships. Furthermore, it aligns with best practices in open-source communities, where transparency is key to ensuring the integrity and reliability of contributions. + +2. **Profile Picture**: Members are required to upload an authentic profile picture. Use a clear, professional image and avoid comic-like pictures, memojis, or other non-authentic picture styles. Using a professional and authentic profile picture is essential for establishing credibility and fostering trust within the community. It helps others easily identify and connect with you, which is crucial for effective collaboration. By using a real photo, you present yourself as a serious and committed contributor, which in turn encourages others to take your work and interactions seriously. Avoiding non-authentic images ensures that the focus remains on the substance of your contributions rather than on distractions or misunderstandings that might arise from informal or unprofessional visuals. + +3. **Direct Branching and PR Creation**: As a member, you are encourages to create branches and pull requests (PRs) directly within the repository. Please follow the internal branching and code review processes outlined in [guidelines to the development process](https://docs.artemis.cit.tum.de/dev/development-process/development-process.html) and [coding and design guidelines](https://docs.artemis.cit.tum.de/dev/guidelines.html). + +### For External Contributors + +1. **Identity Verification**: External contributions will only be considered if the contributor uses their real name and an authentic profile picture (see above). This ensures accountability and trustworthiness in all external contributions. + +2. **Forking the Repository**: External contributors fork the repository and work on changes in their own branches. + +3. **Submit a Pull Request**: Once your work is complete, submit a pull request for review. Ensure that your branch is up to date with the main branch before submitting. + +4. **Compliance**: Contributions from external contributors that do not adhere to these guidelines may not be accepted. + +### References and Best Practices + +- We align our guidelines with the [GitHub Acceptable Use Policies](https://docs.github.com/en/site-policy/acceptable-use-policies) which stress the importance of authenticity and transparency in user profiles. +- For more insights on contributing to open-source projects, we recommend reviewing the [Open Source Guides by GitHub](https://opensource.guide/). + +By following these guidelines, we foster a collaborative environment built on mutual trust and respect, essential for the success of our project. + +## Contribution Process + +1. **External contributors only**: Fork the Repository and create a branch. +2. **Create a feature branch**: Work on your changes in a separate branch. +3. **Submit a pull request**: Once your work is complete, submit a pull request for review. + +Thank you for your contributions and for helping us maintain a high standard of quality and trust in this project. + + diff --git a/README.md b/README.md index 30ec2419c16d..092d74dcdbb0 100644 --- a/README.md +++ b/README.md @@ -53,22 +53,68 @@ Artemis brings interactive learning to life with instant, individual feedback on ## Roadmap -The Artemis development team prioritizes the following issues in the future. We welcome feature requests from students, tutors, instructors, and administrators. We are happy to discuss any suggestions for improvements. +The Artemis development team prioritizes the following areas in the future. We welcome feature requests from students, tutors, instructors, and administrators. We are happy to discuss any suggestions for improvements. * **Short term**: Further improve the communication features with mobile apps for iOS and Android -* **Short term**: Improve the REST API of the server application +* **Short term**: Add the possibility to use Iris for questions on all exercise types and lectures (partly done) +* **Short term**: Provide GenAI based automatic feedback to modeling, text and programming exercise with Athena +* **Short term**: Improve the LTI integration with Moodle +* **Medium term**: Improve the REST API of the server application +* **Medium term**: Integrate an online IDE (e.g. Eclipse Theia) into Artemis for enhanced user experience * **Medium term**: Add more learning analytics features while preserving data privacy * **Medium term**: Improve the user experience, usability and navigation * **Medium term**: Add automatic generation of hints for programming exercises * **Medium term**: Add GenAI support for reviewing exercises for instructors -* **Medium term**: Add GenAI support for learning analytics -* **Medium term**: Add the possibility to use Iris for questions on all exercise types and lectures +* **Medium term**: Add GenAI support for learning analytics (partly done) * **Long term**: Explore the possibilities of microservices, Kubernetes based deployment, and micro frontends -* **Long term**: Integrated on online IDE (e.g. Eclipse Theia) into Artemis for enhanced user experience * **Long term**: Allow students to take notes on lecture slides and support the automatic updates of lecture slides * **Long term**: Develop an exchange platform for exercises -## Setup, guides, and contributing +## Contributing to This Project + +We welcome contributions from both members of our organization and external contributors. To maintain transparency and trust: + +- **Members**: Must use their full real names and upload a professional and authentic profile picture. Members can directly create branches and PRs in the repository. +- **External Contributors**: Must adhere to our identity guidelines, using real names and authentic profile pictures. Contributions will only be considered if these guidelines are followed. + +We adhere to best practices as recommended by [GitHub's Open Source Guides](https://opensource.guide/) and their [Acceptable Use Policies](https://docs.github.com/en/site-policy/acceptable-use-policies). Thank you for helping us create a respectful and professional environment for everyone involved. + +We follow a pull request contribution model. For detailed guidelines, please refer to our [CONTRIBUTING.md](./CONTRIBUTING.md). Once your pull request is ready to merge, notify the responsible feature maintainer on Slack: + +#### Maintainers + +The following members of the project management team are responsible for specific feature areas in Artemis. Contact them if you have questions or if you want to develop new features in this area. + +| Feature / Aspect | Responsible maintainer | +|--------------------------------|------------------------------------------------------------------------------------| +| Programming exercises | Stephan Krusche ([@krusche](https://github.com/krusche)) | +| Integrated code lifecycle | Stephan Krusche ([@krusche](https://github.com/krusche)) | +| Quiz exercises | Felix Dietrich ([@FelixTJDietrich](https://github.com/FelixTJDietrich)) | +| Modeling exercises (+ Apollon) | Stephan Krusche ([@krusche](https://github.com/krusche)) | +| Text exercises | Maximilian Sölch ([@maximiliansoelch](https://github.com/maximiliansoelch)) | +| File upload exercises | Maximilian Sölch ([@maximiliansoelch](https://github.com/maximiliansoelch)) | +| Exam mode | Stephan Krusche ([@krusche](https://github.com/krusche)) | +| Grading | Maximilian Sölch ([@maximiliansoelch](https://github.com/maximiliansoelch)) | +| Assessment | Maximilian Sölch ([@maximiliansoelch](https://github.com/maximiliansoelch)) | +| Communication | Ramona Beinstingel ([@rabeatwork](https://github.com/rabeatwork)) | +| Notifications | Ramona Beinstingel ([@rabeatwork](https://github.com/rabeatwork)) | +| Team Exercises | Stephan Krusche ([@krusche](https://github.com/krusche)) | +| Lectures | Maximilian Anzinger ([@maximiliananzinger](https://github.com/maximiliananzinger)) | +| Integrated Markdown Editor | Patrick Bassner ([@bassner](https://github.com/bassner)) | +| Plagiarism checks | Markus Paulsen ([@MarkusPaulsen](https://github.com/MarkusPaulsen)) | +| Learning analytics | Maximilian Anzinger ([@maximiliananzinger](https://github.com/maximiliananzinger)) | +| Adaptive learning | Maximilian Anzinger ([@maximiliananzinger](https://github.com/maximiliananzinger)) | +| Learning paths | Maximilian Anzinger ([@maximiliananzinger](https://github.com/maximiliananzinger)) | +| Tutorial Groups | Felix Dietrich ([@FelixTJDietrich](https://github.com/FelixTJDietrich)) | +| Iris | Patrick Bassner ([@bassner](https://github.com/bassner)) | +| Scalability | Matthias Linhuber ([@mtze](https://github.com/mtze)) | +| Usability | Ramona Beinstingel ([@rabeatwork](https://github.com/rabeatwork)) | +| Performance | Ramona Beinstingel ([@rabeatwork](https://github.com/rabeatwork)) | +| Infrastructure | Matthias Linhuber ([@mtze](https://github.com/mtze)) | +| Development process | Felix Dietrich ([@FelixTJDietrich](https://github.com/FelixTJDietrich)) | +| Mobile apps (iOS + Android) | Maximilian Sölch ([@maximiliansoelch](https://github.com/maximiliansoelch)) | + +## Setup and guidelines ### Development setup, coding, and design guidelines @@ -76,6 +122,7 @@ The Artemis development team prioritizes the following issues in the future. We * [Server coding and design guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/server/) * [Client coding and design guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/client/) * [Code Review Guidelines](https://docs.artemis.cit.tum.de/dev/development-process/#review) +* [Performance Guidelines](https://docs.artemis.cit.tum.de/dev/guidelines/performance/) ### Documentation @@ -96,46 +143,6 @@ Artemis uses these external tools for user management and the configuration of p If needed, you can configure self service [user registration](https://docs.artemis.cit.tum.de/admin/registration). -### Contributing - -Please read the guide on [how to contribute](CONTRIBUTING.md) to Artemis. - -Once your PR is ready to merge, notify the responsible feature maintainer on Slack: - -#### Maintainers - -The following members of the project management team are responsible for specific feature areas in Artemis. Contact them if you have questions or if you want to develop new features in this area. - -| Feature / Aspect | Maintainer | -|--------------------------------|-----------------------------------------------------------------------------------------------------| -| Programming exercises | [@krusche](https://github.com/krusche) | -| Integrated code lifecycle | [@krusche](https://github.com/krusche) | -| Quiz exercises | [@FelixTJDietrich](https://github.com/FelixTJDietrich) | -| Modeling exercises (+ Apollon) | [@krusche](https://github.com/krusche) | -| Text exercises | [@maximiliansoelch](https://github.com/maximiliansoelch) | -| File upload exercises | [@maximiliansoelch](https://github.com/maximiliansoelch) | -| Exam mode | [@krusche](https://github.com/krusche) | -| Grading | [@maximiliansoelch](https://github.com/maximiliansoelch) | -| Assessment | [@maximiliansoelch](https://github.com/maximiliansoelch) | -| Communication | [@rabeatwork](https://github.com/rabeatwork) | -| Notifications | [@rabeatwork](https://github.com/rabeatwork) | -| Team Exercises | [@krusche](https://github.com/krusche) | -| Lectures | [@maximiliananzinger](https://github.com/maximiliananzinger) | -| Integrated Markdown Editor | [@maximiliansoelch](https://github.com/maximiliansoelch) [@bassner](https://github.com/bassner) | -| Plagiarism checks | [@MarkusPaulsen](https://github.com/MarkusPaulsen) | -| Learning analytics | [@bassner](https://github.com/bassner) | -| Adaptive learning | [@bassner](https://github.com/bassner) [@maximiliananzinger](https://github.com/maximiliananzinger) | -| Learning paths | [@maximiliananzinger](https://github.com/maximiliananzinger) | -| Tutorial Groups | [@FelixTJDietrich](https://github.com/FelixTJDietrich) | -| Iris | [@bassner](https://github.com/bassner) | -| Scalability | [@mtze](https://github.com/mtze) | -| Usability | [@rabeatwork](https://github.com/rabeatwork) | -| Performance | [@rabeatwork](https://github.com/rabeatwork) | -| Infrastructure | [@mtze](https://github.com/mtze) | -| Development process | [@FelixTJDietrich](https://github.com/FelixTJDietrich) | -| Mobile apps (iOS + Android) | [@krusche](https://github.com/krusche) [@maximiliansoelch](https://github.com/maximiliansoelch) | - - ### Building for production To build and optimize the Artemis application for production, run: diff --git a/docs/.readthedocs.yaml b/docs/.readthedocs.yaml index 9522486c382b..4e14204d7703 100644 --- a/docs/.readthedocs.yaml +++ b/docs/.readthedocs.yaml @@ -4,7 +4,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.10" + python: "3.12" sphinx: fail_on_warning: true python: diff --git a/docs/README.md b/docs/README.md index aaf6ac4ae190..a806a39d5ef5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -60,11 +60,11 @@ RtD will build and deploy changes automatically. You can install Sphinx using `pip` or choose a system-wide installation instead. When using pip, consider using [Python virtual environments]. ```bash -pip install -r requirements.txt +pip install -r requirements.txt --break-system-packages ``` or ```bash -pip3 install -r requirements.txt +pip3 install -r requirements.txt --break-system-packages ``` The [Installing Sphinx] documentation explains more install options. For macOS, it is recommended to install it using homebrew: diff --git a/docs/dev/guidelines.rst b/docs/dev/guidelines.rst index b53f26746d0d..8574b339f28d 100644 --- a/docs/dev/guidelines.rst +++ b/docs/dev/guidelines.rst @@ -7,6 +7,7 @@ Coding and design guidelines :includehidden: :maxdepth: 3 + guidelines/performance guidelines/server guidelines/server-tests guidelines/client diff --git a/docs/dev/guidelines/database.rst b/docs/dev/guidelines/database.rst index 7898ab2caa33..0c7119159a9d 100644 --- a/docs/dev/guidelines/database.rst +++ b/docs/dev/guidelines/database.rst @@ -1,8 +1,6 @@ -********************** -Database Relationships -********************** - -WORK IN PROGRESS +******** +Database +******** 1. Retrieving and Building Objects ================================== diff --git a/docs/dev/guidelines/performance.rst b/docs/dev/guidelines/performance.rst new file mode 100644 index 000000000000..192f3dc34739 --- /dev/null +++ b/docs/dev/guidelines/performance.rst @@ -0,0 +1,219 @@ +*********** +Performance +*********** + +These guidelines focus on optimizing the performance of Spring Boot applications using Hibernate, with an emphasis on data economy, large-scale testing, paging, and general SQL database best practices. You can find more best practices in the `Database Guidelines `_ section. + +1. Data Economy +=============== + +**Database-Level Filtering** + +Ensure that all filtering is done at the database level rather than in memory. This approach minimizes data transfer to the application and reduces memory usage. + +Example: + +.. code-block:: java + + @Query(""" + SELECT e + FROM Exercise e + WHERE e.course.id = :courseId + AND e.releaseDate >= :releaseDate + """) + List findExercisesByCourseAndReleaseDate(@Param("courseId") Long courseId, @Param("releaseDate") ZonedDateTime releaseDate); + +**Projections and DTOs** + +When only a subset of fields is needed, use projections or Data Transfer Objects (DTOs) instead of fetching entire entities. This reduces the amount of data loaded and improves query performance. + +Example: + +.. code-block:: java + + @Query(""" + SELECT new com.example.dto.ExerciseDTO(e.id, e.title) + FROM Exercise e + WHERE e.course.id = :courseId + AND e.releaseDate >= :releaseDate + """) + List findExerciseDTOsByCourseAndReleaseDate(@Param("courseId") Long courseId, @Param("releaseDate") ZonedDateTime releaseDate); + +2. Large Scale Testing +======================= + +**Test with Realistic Data Loads** + +Given that courses can have up to 2,000 students, simulate this scale during testing to identify potential performance bottlenecks when handling large amounts of data. + +**Benchmarking** + +Perform load testing to ensure that the application can handle the expected volume of data efficiently. + +Example: + +Use tools like JMeter or Gatling to simulate concurrent users and large datasets. + +3. Paging +========= + +**Implement Paging for Large Results** + +For queries that return large datasets, implement pagination to avoid loading too much data into memory at once. + +Example: + +.. code-block:: java + + Page findByCourseId(Long courseId, Pageable pageable); + +**Caution with Collection Fetching and Pagination** + +Avoid combining `LEFT JOIN FETCH` with pagination, as this can cause performance issues or even fail due to the Cartesian Product problem. + +Example: + +Instead of: + +.. code-block:: java + + @Query(""" + SELECT c + FROM Course c + LEFT JOIN FETCH c.exercises + WHERE c.id = :courseId + """) + Page findCourseWithExercises(@Param("courseId") Long courseId, Pageable pageable); + +Do: + +.. code-block:: java + + @Query(""" + SELECT c + FROM Course c + WHERE c.id = :courseId + """) + Course findCourseById(@Param("courseId") Long courseId); + + // Fetch exercises in a separate query if needed + @Query(""" + SELECT e + FROM Exercise e + WHERE e.course.id = :courseId + """) + List findExercisesByCourseId(@Param("courseId") Long courseId); + +You can find out more on https://vladmihalcea.com/hibernate-query-fail-on-pagination-over-collection-fetch + +4. Avoiding the N+1 Issue +========================= + +**Eager Fetching and Left Join Fetch** + +The N+1 query issue occurs when lazy-loaded collections cause multiple queries to be executed — one for the parent entity and additional queries for each related entity. To avoid this issue, consider using eager fetching or `JOIN FETCH` for collections that are critical to performance. + +Example: + +.. code-block:: java + + @Query(""" + SELECT e + FROM Exercise e + JOIN FETCH e.submissions + WHERE e.course.id = :courseId + """) + List findExercisesWithSubmissions(@Param("courseId") Long courseId); + +In this example, the query fetches exercises along with their submissions in a single query, avoiding the N+1 problem. Be cautious, however, as fetching too many collections eagerly can lead to performance degradation due to large result sets. + + +5. Optimal Use of Left Join Fetch +================================= + +**Balance Between Queries** + +While reducing the number of queries by using `LEFT JOIN FETCH` is often beneficial, overusing this strategy can lead to performance issues, especially when fetching multiple `OneToMany` relationships. As a best practice, avoid fetching more than three `OneToMany` collections in a single query. + +Example: + +.. code-block:: java + + @Query(""" + SELECT c + FROM Course c + LEFT JOIN FETCH c.exercises e + LEFT JOIN FETCH e.participations + WHERE c.id = :courseId + """) + Course findCourseWithExercisesAndParticipations(@Param("courseId") Long courseId); + +This query efficiently fetches a course with its exercises and their submissions. However, if more collections are added to the fetch, consider splitting the query into multiple parts to prevent large result sets and excessive memory usage. + +**Selective Fetching** + +Use lazy loading by default, and override with `JOIN FETCH` only when necessary for performance-critical queries. This approach minimizes the risk of performance degradation due to large query results. + +Example: + +.. code-block:: java + + @Entity + public class Exercise { + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "exercise") + private List participations; + + // Other fields and methods + } + +By default, participations are lazily loaded. When you need to fetch them, use a specific `JOIN FETCH` query only in performance-sensitive situations. Alternatively, consider using ``@EntityGraph`` to define fetch plans for specific queries. + +6. General SQL Database Best Practices +====================================== + +**Indexing** + +Indexes are critical for query performance, especially on columns that are frequently used in `WHERE` clauses, `JOIN` conditions, or are sorted. Ensure that all key fields, such as `releaseDate` and `courseId`, are properly indexed. + +Example: + +Create an index on the `releaseDate` column to speed up queries filtering exercises by date: + +.. code-block:: sql + + CREATE INDEX idx_exercise_release_date ON exercise(release_date); + +**Normalization vs. Denormalization** + +While normalization reduces data redundancy, it can lead to complex queries with multiple joins. In scenarios where read performance is critical, consider denormalizing certain tables to reduce the number of joins. However, always balance this against potential issues such as data inconsistency and increased storage requirements. + +**Use of Foreign Keys** + +Maintain foreign key constraints to enforce data integrity. However, be aware of the potential performance impact on insert, update, and delete operations in high-load scenarios. Proper indexing can help mitigate these effects. + +Example: + +.. code-block:: sql + + ALTER TABLE submission ADD CONSTRAINT fk_exercise FOREIGN KEY (exercise_id) REFERENCES exercise(id); + +This foreign key ensures that submissions are always linked to a valid exercise, maintaining data integrity. + +**Query Optimization** + +Regularly review and optimize SQL queries to ensure they are performing efficiently. Use tools like `EXPLAIN` to analyze query execution plans and make adjustments where necessary. + +Example: + +.. code-block:: sql + + EXPLAIN SELECT * FROM exercise WHERE course_id = 1 AND release_date > '2024-01-01'; + +Use the `EXPLAIN` output to identify slow-running queries and optimize them by adding indexes, rewriting queries, or adjusting table structures. + +**Avoid Transactions** + +Transactions are generally very slow and should be avoided when possible. + +By following these best practices, you can build Spring Boot applications with Hibernate that are optimized for performance, even under the demands of large-scale data processing. diff --git a/docs/dev/guidelines/server.rst b/docs/dev/guidelines/server.rst index 9e5a8ce068de..a4e510f362b0 100644 --- a/docs/dev/guidelines/server.rst +++ b/docs/dev/guidelines/server.rst @@ -99,7 +99,7 @@ Avoid code duplication. If we cannot reuse a method elsewhere, then the method i 8. Comments =========== -Only write comments for complicated algorithms, to help other developers better understand them. We should only add a comment, if our code is not self-explanatory. +Always add JavaDoc and inline comments to help other developers better understand the code and the rationale behind it. ChatGPT can be a great help. It can generate comments for you, but you should always check them and adjust them to your needs. Prefer more extensive comments and documentation and avoid useless and non sense documentation. Comments should always be in English. 9. Utility ========== @@ -121,10 +121,10 @@ It gets activated when a particular jar file is detected on the classpath. The s * RestControllers should be stateless. * RestControllers are by default singletons. -* RestControllers should not execute business logic but rely on delegation. -* RestControllers should deal with the HTTP layer of the application. +* RestControllers should not execute business logic but rely on delegation to ``@Service`` classes. +* RestControllers should deal with the HTTP layer of the application, handle access control, input data validation, output data cleanup (if necessary) and error handling. * RestControllers should be oriented around a use-case/business-capability. -* RestControllers should return DTOs that are as small as possible +* RestControllers must always return DTOs that are as small as possible (please focus on data economy to improve performance and follow data privacy principles). Route naming conventions: diff --git a/docs/requirements.txt b/docs/requirements.txt index 980c9ef308b6..d7f6f239f035 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -Sphinx==7.2.6 +Sphinx==7.4.7 sphinx-rtd-theme==2.0.0 -sphinx-autobuild==2024.2.4 +sphinx-autobuild==2024.04.16 sphinxcontrib-bibtex==2.6.2 diff --git a/package-lock.json b/package-lock.json index bc5fa5d014c4..4ac4c8f4a4b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,18 +10,18 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@angular/animations": "18.2.0", - "@angular/cdk": "18.2.0", - "@angular/common": "18.2.0", - "@angular/compiler": "18.2.0", - "@angular/core": "18.2.0", - "@angular/forms": "18.2.0", - "@angular/localize": "18.2.0", - "@angular/material": "18.2.0", - "@angular/platform-browser": "18.2.0", - "@angular/platform-browser-dynamic": "18.2.0", - "@angular/router": "18.2.0", - "@angular/service-worker": "18.2.0", + "@angular/animations": "18.2.1", + "@angular/cdk": "18.2.1", + "@angular/common": "18.2.1", + "@angular/compiler": "18.2.1", + "@angular/core": "18.2.1", + "@angular/forms": "18.2.1", + "@angular/localize": "18.2.1", + "@angular/material": "18.2.1", + "@angular/platform-browser": "18.2.1", + "@angular/platform-browser-dynamic": "18.2.1", + "@angular/router": "18.2.1", + "@angular/service-worker": "18.2.1", "@ctrl/ngx-emoji-mart": "9.2.0", "@danielmoncada/angular-datetime-picker": "18.1.0", "@fingerprintjs/fingerprintjs": "4.4.3", @@ -38,7 +38,7 @@ "@swimlane/ngx-charts": "20.5.0", "@swimlane/ngx-graph": "8.4.0", "@vscode/codicons": "0.0.36", - "ace-builds": "1.35.5", + "ace-builds": "1.36.0", "bootstrap": "5.3.3", "brace": "0.11.1", "compare-versions": "6.1.1", @@ -57,7 +57,7 @@ "jszip": "3.10.1", "lodash-es": "4.17.21", "mobile-drag-drop": "3.0.0-rc.0", - "monaco-editor": "0.50.0", + "monaco-editor": "0.51.0", "ngx-infinite-scroll": "18.0.0", "ngx-webstorage": "18.0.0", "papaparse": "5.4.1", @@ -66,12 +66,12 @@ "showdown": "2.1.0", "showdown-highlight": "3.1.0", "showdown-katex": "0.6.0", - "simple-statistics": "7.8.3", + "simple-statistics": "7.8.4", "smoothscroll-polyfill": "0.4.4", "sockjs-client": "1.6.1", "split.js": "1.6.5", "ts-cacheable": "1.0.10", - "tslib": "2.6.3", + "tslib": "2.7.0", "uuid": "10.0.0", "webstomp-client": "1.2.6", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", @@ -79,22 +79,22 @@ }, "devDependencies": { "@angular-builders/jest": "18.0.0", - "@angular-devkit/build-angular": "18.2.0", + "@angular-devkit/build-angular": "18.2.1", "@angular-eslint/builder": "18.3.0", "@angular-eslint/eslint-plugin": "18.3.0", "@angular-eslint/eslint-plugin-template": "18.3.0", "@angular-eslint/schematics": "18.3.0", "@angular-eslint/template-parser": "18.3.0", - "@angular/cli": "18.2.0", - "@angular/compiler-cli": "18.2.0", - "@angular/language-service": "18.2.0", + "@angular/cli": "18.2.1", + "@angular/compiler-cli": "18.2.1", + "@angular/language-service": "18.2.1", "@sentry/types": "8.26.0", "@types/crypto-js": "4.2.2", "@types/d3-shape": "3.1.6", "@types/dompurify": "3.0.5", "@types/jest": "29.5.12", "@types/lodash-es": "4.17.12", - "@types/node": "22.4.2", + "@types/node": "22.5.0", "@types/papaparse": "5.3.14", "@types/showdown": "2.0.6", "@types/smoothscroll-polyfill": "0.3.4", @@ -102,7 +102,7 @@ "@types/uuid": "10.0.0", "@typescript-eslint/eslint-plugin": "8.2.0", "@typescript-eslint/parser": "8.2.0", - "eslint": "9.9.0", + "eslint": "9.9.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", "eslint-plugin-jest": "28.8.0", @@ -121,7 +121,7 @@ "ng-mocks": "14.13.0", "prettier": "3.3.3", "sass": "1.77.8", - "ts-jest": "29.2.4", + "ts-jest": "29.2.5", "typescript": "5.5.4", "weak-napi": "2.0.2" }, @@ -211,13 +211,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1802.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.0.tgz", - "integrity": "sha512-s1atTSL98XLUUxfWEQAnhFaOFIJZDLWjSqec+Sb+f4iZFj+hOFejzJxPVnHMIJudOzn0Lqjk3t987KG/zNEGdg==", + "version": "0.1802.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.1.tgz", + "integrity": "sha512-XTnJfCBMDQl3xF4w/eNrq821gbj2Ig1cqbzpRflhz4pqrANTAfHfPoIC7piWEZ60FNlHapzb6fvh6tJUGXG9og==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", + "@angular-devkit/core": "18.2.1", "rxjs": "7.8.1" }, "engines": { @@ -227,17 +227,17 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.0.tgz", - "integrity": "sha512-V0XKT7xt6d6duXqmDAQEQgEJNXuWAekpHEDxafvBT0MTxcEhu0ozQVwI4oAekiKOz+APIcAZyMzvw3Tlzog5cw==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.1.tgz", + "integrity": "sha512-ANsTWKjIlEvJ6s276TbwnDhkoHhQDfsNiRFUDRGBZu94UNR78ImQZSyKYGHJOeQQH6jpBtraA1rvW5WKozAtlw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.0", - "@angular-devkit/build-webpack": "0.1802.0", - "@angular-devkit/core": "18.2.0", - "@angular/build": "18.2.0", + "@angular-devkit/architect": "0.1802.1", + "@angular-devkit/build-webpack": "0.1802.1", + "@angular-devkit/core": "18.2.1", + "@angular/build": "18.2.1", "@babel/core": "7.25.2", "@babel/generator": "7.25.0", "@babel/helper-annotate-as-pure": "7.24.7", @@ -248,7 +248,7 @@ "@babel/preset-env": "7.25.3", "@babel/runtime": "7.25.0", "@discoveryjs/json-ext": "0.6.1", - "@ngtools/webpack": "18.2.0", + "@ngtools/webpack": "18.2.1", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -280,7 +280,7 @@ "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.77.8", + "sass": "1.77.6", "sass-loader": "16.0.0", "semver": "7.6.3", "source-map-loader": "5.0.0", @@ -355,14 +355,39 @@ } } }, + "node_modules/@angular-devkit/build-angular/node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1802.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.0.tgz", - "integrity": "sha512-bU7AxlI/avnlOLrgE195cokrOA1ayT6JjRv8Hxzh1bIOa1jE87HsyjxvQhOLcdEb97zwHqMqbntcgUNBgsegJQ==", + "version": "0.1802.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.1.tgz", + "integrity": "sha512-xOP9Hxkj/mWYdMTa/8uNxFTv7z+3UiGdt4VAO7vetV5qkU/S9rRq8FEKviCc2llXfwkhInSgeeHpWKdATa+YIQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/architect": "0.1802.1", "rxjs": "7.8.1" }, "engines": { @@ -376,9 +401,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.0.tgz", - "integrity": "sha512-8SOopyUKUMqAq2rj32XkTIfr79Y274k4uglxxRtzHYoWwHlLdG0KrA+wCcsh/FU9PyR4dA+5dcDAApn358ZF+Q==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.1.tgz", + "integrity": "sha512-fSuGj6CxiTFR+yjuVcaWqaVb5Wts39CSBYRO1BlsOlbuWFZ2NKC/BAb5bdxpB31heCBJi7e3XbPvcMMJIcnKlA==", "dev": true, "license": "MIT", "dependencies": { @@ -404,13 +429,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.0.tgz", - "integrity": "sha512-WWKwz2RKMVI6T25JFwOSSfRLB+anNzabVIRwf85R/YMIo34BUk777f2JU/7cCKlxSpQu39TqIfMQZAyzAD1z0A==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.1.tgz", + "integrity": "sha512-2t/q0Jcv7yqhAzEdNgsxoGSCmPgD4qfnVOJ7EJw3LNIA+kX1CmtN4FESUS0i49kN4AyNJFAI5O2pV8iJiliKaw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", + "@angular-devkit/core": "18.2.1", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -523,9 +548,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.0.tgz", - "integrity": "sha512-BFAfqDDjsa0Q91F4s33pFcBG+ydFDurEmwYGG1gmO7UXZJI6ZbRVdULaZHz75M+Bf3hJkzVB05pesvfbK+Fg/g==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.1.tgz", + "integrity": "sha512-jit452yuE6DMVV09E6RAjgapgw64mMVH31ccpPvMDekzPsTuP3KNKtgRFU/k2DFhYJvyczM1AqqlgccE/JGaRw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -534,18 +559,18 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0" + "@angular/core": "18.2.1" } }, "node_modules/@angular/build": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.0.tgz", - "integrity": "sha512-LvNJ2VOEVy3N1tGzt+xnKyweRBuUE1NsnuEEWAb9Y+V1cyRgj0s7FX2+IQZZQhP+W/pXfbsKaByOAbJ5KWV85Q==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.1.tgz", + "integrity": "sha512-HwzjB+I31cAtjTTbbS2NbayzfcWthaKaofJlSmZIst3PN+GwLZ8DU0DRpd/xu5AXkk+DoAIWd+lzUIaqngz6ow==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/architect": "0.1802.1", "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", @@ -565,7 +590,7 @@ "picomatch": "4.0.2", "piscina": "4.6.1", "rollup": "4.20.0", - "sass": "1.77.8", + "sass": "1.77.6", "semver": "7.6.3", "vite": "5.4.0", "watchpack": "2.4.1" @@ -606,10 +631,28 @@ } } }, + "node_modules/@angular/build/node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@angular/cdk": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.0.tgz", - "integrity": "sha512-hjuUWNhxU48WB2i1j4NLwnPTaCnucRElfp7DBX5Io0rY5Lwl3HXafvyi7/A1D0Ah+YsJpktKOWPDGv8r7vxymg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.1.tgz", + "integrity": "sha512-6y4MmpEPXze6igUHkLsBUPkxw32F8+rmW0xVXZchkSyGlFgqfh53ueXoryWb0qL4s5enkNY6AzXnKAqHfPNkVQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -624,18 +667,18 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.0.tgz", - "integrity": "sha512-hA60QIA7Dh8LQxM42wqd7WrhwQjbjB8oIRH5Slgbiv8iocAo76scp1/qyZo2SSzjfkB7jxUiao5L4ckiJ/hvZg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.1.tgz", + "integrity": "sha512-SomUFDHanY4o7k3XBGf1eFt4z1h05IGJHfcbl2vxoc0lY59VN13m/pZsD2AtpqtJTzLQT02XQOUP4rmBbGoQ+Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.0", - "@angular-devkit/core": "18.2.0", - "@angular-devkit/schematics": "18.2.0", + "@angular-devkit/architect": "0.1802.1", + "@angular-devkit/core": "18.2.1", + "@angular-devkit/schematics": "18.2.1", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.0", + "@schematics/angular": "18.2.1", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -658,9 +701,9 @@ } }, "node_modules/@angular/common": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.0.tgz", - "integrity": "sha512-DELx/QYNqqjmiM+kE5PoVmyG4gPw5WB1bDDeg3hEuBCK3eS2KosgQH0/MQo3OSBZHOcAMFjfHMGqKgxndmYixQ==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.1.tgz", + "integrity": "sha512-N0ZJO1/iU9UhprplZRPvBcdRgA/i6l6Ng5gXs5ymHBJ0lxsB+mDVCmC4jISjR9gAWc426xXwLaOpuP5Gv3f/yg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -669,14 +712,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0", + "@angular/core": "18.2.1", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.0.tgz", - "integrity": "sha512-RmGwQ7jRzotUKKMk0CwxTcIXFr5mRxSbJf9o5S3ujuIOo1lYop8SQZvjq67a5BuoYyD+1pX6iMmxZqlbKoihBQ==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.1.tgz", + "integrity": "sha512-5e9ygKEcsBoV6xpaGKVrtsLxLETlrM0oB7twl4qG/xuKYqCLj8cRQMcAKSqDfTPzWMOAQc7pHdk+uFVo/8dWHA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -685,7 +728,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0" + "@angular/core": "18.2.1" }, "peerDependenciesMeta": { "@angular/core": { @@ -694,9 +737,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.0.tgz", - "integrity": "sha512-pPBFjMqNTNettrleLtEc6a/ysOZjG3F0Z5lyKYePcyNQmn2rpa9XmD2y6PhmzTmIhxeXrogWA84Xgg/vK5wBNw==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.1.tgz", + "integrity": "sha512-D+Qba0r6RfHfffzrebGYp54h05AxpkagLjit/GczKNgWSP1gIgZxSfi88D+GvFmeWvZxWN1ecAQ+yqft9hJqWg==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -717,14 +760,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.0", + "@angular/compiler": "18.2.1", "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.0.tgz", - "integrity": "sha512-7+4wXfeAk1TnG3MGho2gpBI5XHxeSRWzLK2rC5qyyRbmMV+GrIgf1HqFjQ4S02rydkQvGpjqQHtO1PYJnyn4bg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.1.tgz", + "integrity": "sha512-9KrSpJ65UlJZNXrE18NszcfOwb5LZgG+LYi5Doe7amt218R1bzb3trvuAm0ZzMaoKh4ugtUCkzEOd4FALPEX6w==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -738,9 +781,9 @@ } }, "node_modules/@angular/forms": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.0.tgz", - "integrity": "sha512-G+4BjNCUo4cRwg9NwisGLbtG/1AbIJNOO3RUejPJJbCcAkYMSzmDWSQ+LQEGW4vC/1xaDKO8AT71DI/I09bOxA==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.1.tgz", + "integrity": "sha512-T7z8KUuj2PoPxrMrAruQVJha+x4a9Y6IrKYtArgOQQlTwCEJuqpVYuOk5l3fwWpHE9bVEjvgkAMI1D5YXA/U6w==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -749,16 +792,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0", + "@angular/common": "18.2.1", + "@angular/core": "18.2.1", + "@angular/platform-browser": "18.2.1", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.2.0.tgz", - "integrity": "sha512-brl5061YqfNnT7yZNMWmsgv6ve6p9+kfhX6mZWOGICcY2SYVtCNVHdqzwWTTwY7MvTVfycHxiAf9PEmc5lD4/g==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.2.1.tgz", + "integrity": "sha512-JI4oox9ELNdDVg0uJqCwgyFoK4XrowV14wSoNpGhpTLModRg3eDS6q+8cKn27cjTQRZvpReyYSTfiZMB8j4eqQ==", "dev": true, "license": "MIT", "engines": { @@ -766,9 +809,9 @@ } }, "node_modules/@angular/localize": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.0.tgz", - "integrity": "sha512-ul8yGmimiHkhUU87isDCst0790jTBHP1zPBMI2q7QHv7iDzSN5brV8zUMcRypxhh4mQOCnq2LtP84o5ybn4Sig==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.1.tgz", + "integrity": "sha512-nNdB6ehXCSBpQ75sTh6Gcwy2rgExfZEkGcPARJLpjqQlHO+Mk3b1y3ka6XT9M2qQYUeyukncTFUMEZWwHICsOA==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -785,21 +828,21 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.0", - "@angular/compiler-cli": "18.2.0" + "@angular/compiler": "18.2.1", + "@angular/compiler-cli": "18.2.1" } }, "node_modules/@angular/material": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.0.tgz", - "integrity": "sha512-lOXk8pAVP4Mr0/Q6YrNnVYQVTnR8aEG5D9QSEWjs+607gONuh/9n7ERBdzX7xO9D0vYyXq+Vil32zcF41/Q8Cg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.1.tgz", + "integrity": "sha512-DBSJGqLttT9vYpLGWTuuRoOKd1mNelS0jnNo7jNZyMpjcGfuhNzmPtYiBkXfNsAl7YoXoUmX8+4uh1JZspQGqA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^18.0.0 || ^19.0.0", - "@angular/cdk": "18.2.0", + "@angular/cdk": "18.2.1", "@angular/common": "^18.0.0 || ^19.0.0", "@angular/core": "^18.0.0 || ^19.0.0", "@angular/forms": "^18.0.0 || ^19.0.0", @@ -808,9 +851,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.0.tgz", - "integrity": "sha512-yhj281TuPz5a8CehwucwIVl29Oqte9KS4X/VQkMV++GpYQE2KKKcoff4FXSdF5RUcUYkK2li4IvawIqPmUSagg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.1.tgz", + "integrity": "sha512-hQABX7QotGmCIR3EhCBCDh5ZTvQao+JkuK5CCw2G1PkRfJMBwEpjNqnyhz41hZhWiGlucp9jgbeypppW+mIQEw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -819,9 +862,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.2.0", - "@angular/common": "18.2.0", - "@angular/core": "18.2.0" + "@angular/animations": "18.2.1", + "@angular/common": "18.2.1", + "@angular/core": "18.2.1" }, "peerDependenciesMeta": { "@angular/animations": { @@ -830,9 +873,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.0.tgz", - "integrity": "sha512-izfaXKNC/kqOEzJG8eTnFu39VLI3vv3dTKoYOdEKRB7MTI0t0x66oZmABnHcihtkTSvXs/is+7lA5HmH2ZuIEQ==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.1.tgz", + "integrity": "sha512-tYJHtshbaKrtnRA15k3vrveSVBqkVUGhINvGugFA2vMtdTOfhfPw+hhzYrcwJibgU49rHogCfI9mkIbpNRYntA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -841,16 +884,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/compiler": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0" + "@angular/common": "18.2.1", + "@angular/compiler": "18.2.1", + "@angular/core": "18.2.1", + "@angular/platform-browser": "18.2.1" } }, "node_modules/@angular/router": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.0.tgz", - "integrity": "sha512-6/462hvC3HSwbps8VItECHpkdkiFqRmTKdn1Trik+FjnvdupYrKB6kBsveM3eP+gZD4zyMBMKzBWB9N/xA1clw==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.1.tgz", + "integrity": "sha512-gVyqW6fYnG7oq1DlZSXJMQ2Py2dJQB7g6XVtRcYB1gR4aeowx5N9ws7PjqAi0ih91ASq2MmP4OlSSWLq+eaMGg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -859,16 +902,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0", + "@angular/common": "18.2.1", + "@angular/core": "18.2.1", + "@angular/platform-browser": "18.2.1", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.0.tgz", - "integrity": "sha512-ngcALrgqMuAeIo5dgou6eBzdtgLvmVg5zwmZuTyrnNPZENEaKTj7u5pm9++gl62797sUWlMbL+fa/BOhntGs5A==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.1.tgz", + "integrity": "sha512-Is4arGy+4HjyvALmR/GsWI4SwXYVJ1IkauAgxPsQKvWLNHdX7a/CEgEEVQGXq96H46QX9O2OcW69PnPatmJIXg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -880,8 +923,8 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0" + "@angular/common": "18.2.1", + "@angular/core": "18.2.1" } }, "node_modules/@babel/code-frame": { @@ -898,9 +941,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", - "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1000,9 +1043,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", - "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", + "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1011,7 +1054,7 @@ "@babel/helper-optimise-call-expression": "^7.24.7", "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.0", + "@babel/traverse": "^7.25.4", "semver": "^6.3.1" }, "engines": { @@ -1270,12 +1313,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", + "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.25.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -1646,13 +1689,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", + "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1764,14 +1807,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", + "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1799,17 +1842,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", - "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", + "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-compilation-targets": "^7.25.2", "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.0", + "@babel/traverse": "^7.25.4", "globals": "^11.1.0" }, "engines": { @@ -2280,14 +2323,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", + "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2517,14 +2560,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", + "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2680,16 +2723,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", - "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", + "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", + "@babel/generator": "^7.25.4", + "@babel/parser": "^7.25.4", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", + "@babel/types": "^7.25.4", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2697,10 +2740,25 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.25.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", + "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.4", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", + "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -3240,9 +3298,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", - "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3364,9 +3422,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", - "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz", + "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==", "dev": true, "license": "MIT", "engines": { @@ -5006,9 +5064,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.0.tgz", - "integrity": "sha512-a6hbkYzh/KUlI52huiU4vztqIuxzyddg6kJGcelUJx3Ju6MJeziu+XmJ6wqFRvfH89zmJeaSADKsGFQaBHtJLg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.1.tgz", + "integrity": "sha512-v86U3jOoy5R9ZWe9Q0LbHRx/IBw1lbn0ldBU+gIIepREyVvb9CcH/vAyIb2Fw1zaYvvfG1OyzdrHyW8iGXjdnQ==", "dev": true, "license": "MIT", "engines": { @@ -5653,14 +5711,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.0.tgz", - "integrity": "sha512-XePvx2ZnxCcAQw5lHVMUrJvm8MXqAWGcMyJDAuQUqNZrPCk3GpCaplWx2n+nPkinYVX2Q2v/DqtvWStQwgU4nA==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.1.tgz", + "integrity": "sha512-bBV7I+MCbdQmBPUFF4ECg37VReM0+AdQsxgwkjBBSYExmkErkDoDgKquwL/tH7stDCc5IfTd0g9BMeosRgDMug==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", - "@angular-devkit/schematics": "18.2.0", + "@angular-devkit/core": "18.2.1", + "@angular-devkit/schematics": "18.2.1", "jsonc-parser": "3.3.1" }, "engines": { @@ -6398,9 +6456,9 @@ } }, "node_modules/@types/node": { - "version": "22.4.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.2.tgz", - "integrity": "sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==", + "version": "22.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", + "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", "dev": true, "license": "MIT", "dependencies": { @@ -6448,9 +6506,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", + "version": "18.3.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", + "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -7019,9 +7077,9 @@ } }, "node_modules/ace-builds": { - "version": "1.35.5", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.35.5.tgz", - "integrity": "sha512-yh3V5BLHlN6gwbmk5sV00WRRvdEggJGJ3AIHhOOGHlgDWNWCSvOnHPO7Chb+AqaxxHuvpxOdXd7ZQesaiuJQZQ==", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.36.0.tgz", + "integrity": "sha512-7to4F86V5N13EY4M9LWaGo2Wmr9iWe5CrYpc28F+/OyYCf7yd+xBV5x9v/GB73EBGGoYd89m6JjeIUjkL6Yw+w==", "license": "BSD-3-Clause" }, "node_modules/acorn": { @@ -7301,9 +7359,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, @@ -8654,9 +8712,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz", - "integrity": "sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==", + "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, "license": "MIT", "dependencies": { @@ -9714,9 +9772,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.8.tgz", - "integrity": "sha512-4Nx0gP2tPNBLTrFxBMHpkQbtn2hidPVr/+/FTtcCiBYTucqc70zRyVZiOLj17Ui3wTO7SQ1/N+hkHYzJjBzt6A==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", "license": "ISC" }, "node_modules/emittery": { @@ -9974,17 +10032,17 @@ } }, "node_modules/eslint": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", - "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz", + "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.17.1", + "@eslint/config-array": "^0.18.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.9.0", + "@eslint/js": "9.9.1", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -12115,9 +12173,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "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, "license": "MIT", "dependencies": { @@ -15567,9 +15625,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -15898,9 +15956,9 @@ "license": "MIT" }, "node_modules/monaco-editor": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.50.0.tgz", - "integrity": "sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA==", + "version": "0.51.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.51.0.tgz", + "integrity": "sha512-xaGwVV1fq343cM7aOYB6lVE4Ugf0UyimdD/x5PWcWBMKENwectaEu77FAN7c5sFiyumqeJdX1RPTh1ocioyDjw==", "license": "MIT" }, "node_modules/moo-color": { @@ -17212,9 +17270,9 @@ } }, "node_modules/postcss": { - "version": "8.4.40", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz", - "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "funding": [ { @@ -18764,12 +18822,12 @@ } }, "node_modules/simple-statistics": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.3.tgz", - "integrity": "sha512-JFvMY00t6SBGtwMuJ+nqgsx9ylkMiJ5JlK9bkj8AdvniIe5615wWQYkKHXe84XtSuc40G/tlrPu0A5/NlJvv8A==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.4.tgz", + "integrity": "sha512-KHC7X+4Dji2rFgnPU7FxPPp4GxPz9hvQCHx2x6JbjLYNKuSMHcoNZ54gF0xBBMOAvNtWmfCHcfC4MD2T89ffEA==", "license": "ISC", "engines": { - "node": "*" + "node": ">= 18" } }, "node_modules/sisteransi": { @@ -19012,9 +19070,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true, "license": "CC0-1.0" }, @@ -19833,21 +19891,21 @@ } }, "node_modules/ts-jest": { - "version": "29.2.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", - "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", "dev": true, "license": "MIT", "dependencies": { - "bs-logger": "0.x", + "bs-logger": "^0.2.6", "ejs": "^3.1.10", - "fast-json-stable-stringify": "2.x", + "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" @@ -19951,9 +20009,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD" }, "node_modules/tsutils": { @@ -21022,9 +21080,9 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.3.0.tgz", - "integrity": "sha512-xD2qnNew+F6KwOGZR7kWdbIou/ud7cVqLEXeK1q0nHcNsX/u7ul/fSdlOTX4ntSL5FNFy7ZJJXbf0piF591JYw==", + "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, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1eda30265f5f..da3d4a60a893 100644 --- a/package.json +++ b/package.json @@ -13,18 +13,18 @@ "node_modules" ], "dependencies": { - "@angular/animations": "18.2.0", - "@angular/cdk": "18.2.0", - "@angular/common": "18.2.0", - "@angular/compiler": "18.2.0", - "@angular/core": "18.2.0", - "@angular/forms": "18.2.0", - "@angular/localize": "18.2.0", - "@angular/material": "18.2.0", - "@angular/platform-browser": "18.2.0", - "@angular/platform-browser-dynamic": "18.2.0", - "@angular/router": "18.2.0", - "@angular/service-worker": "18.2.0", + "@angular/animations": "18.2.1", + "@angular/cdk": "18.2.1", + "@angular/common": "18.2.1", + "@angular/compiler": "18.2.1", + "@angular/core": "18.2.1", + "@angular/forms": "18.2.1", + "@angular/localize": "18.2.1", + "@angular/material": "18.2.1", + "@angular/platform-browser": "18.2.1", + "@angular/platform-browser-dynamic": "18.2.1", + "@angular/router": "18.2.1", + "@angular/service-worker": "18.2.1", "@ctrl/ngx-emoji-mart": "9.2.0", "@danielmoncada/angular-datetime-picker": "18.1.0", "@fingerprintjs/fingerprintjs": "4.4.3", @@ -41,7 +41,7 @@ "@swimlane/ngx-charts": "20.5.0", "@swimlane/ngx-graph": "8.4.0", "@vscode/codicons": "0.0.36", - "ace-builds": "1.35.5", + "ace-builds": "1.36.0", "bootstrap": "5.3.3", "brace": "0.11.1", "compare-versions": "6.1.1", @@ -60,7 +60,7 @@ "jszip": "3.10.1", "lodash-es": "4.17.21", "mobile-drag-drop": "3.0.0-rc.0", - "monaco-editor": "0.50.0", + "monaco-editor": "0.51.0", "ngx-infinite-scroll": "18.0.0", "ngx-webstorage": "18.0.0", "papaparse": "5.4.1", @@ -69,12 +69,12 @@ "showdown": "2.1.0", "showdown-highlight": "3.1.0", "showdown-katex": "0.6.0", - "simple-statistics": "7.8.3", + "simple-statistics": "7.8.4", "smoothscroll-polyfill": "0.4.4", "sockjs-client": "1.6.1", "split.js": "1.6.5", "ts-cacheable": "1.0.10", - "tslib": "2.6.3", + "tslib": "2.7.0", "uuid": "10.0.0", "webstomp-client": "1.2.6", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", @@ -91,7 +91,6 @@ "@typescript-eslint/utils": { "eslint": "^9.9.0" }, - "axios": "1.7.4", "braces": "3.0.3", "critters": "0.0.24", "debug": "4.3.6", @@ -103,36 +102,35 @@ }, "jsdom": "24.1.1", "katex": "0.16.11", - "postcss": "8.4.40", + "postcss": "8.4.41", "semver": "7.6.3", "showdown-katex": { "showdown": "2.1.0" }, "tough-cookie": "4.1.4", - "undici": "6.19.5", - "webpack-dev-middleware": "7.3.0", + "webpack-dev-middleware": "7.4.2", "word-wrap": "1.2.5", "ws": "8.18.0", "yargs-parser": "21.1.1" }, "devDependencies": { "@angular-builders/jest": "18.0.0", - "@angular-devkit/build-angular": "18.2.0", + "@angular-devkit/build-angular": "18.2.1", "@angular-eslint/builder": "18.3.0", "@angular-eslint/eslint-plugin": "18.3.0", "@angular-eslint/eslint-plugin-template": "18.3.0", "@angular-eslint/schematics": "18.3.0", "@angular-eslint/template-parser": "18.3.0", - "@angular/cli": "18.2.0", - "@angular/compiler-cli": "18.2.0", - "@angular/language-service": "18.2.0", + "@angular/cli": "18.2.1", + "@angular/compiler-cli": "18.2.1", + "@angular/language-service": "18.2.1", "@sentry/types": "8.26.0", "@types/crypto-js": "4.2.2", "@types/d3-shape": "3.1.6", "@types/dompurify": "3.0.5", "@types/jest": "29.5.12", "@types/lodash-es": "4.17.12", - "@types/node": "22.4.2", + "@types/node": "22.5.0", "@types/papaparse": "5.3.14", "@types/showdown": "2.0.6", "@types/smoothscroll-polyfill": "0.3.4", @@ -140,7 +138,7 @@ "@types/uuid": "10.0.0", "@typescript-eslint/eslint-plugin": "8.2.0", "@typescript-eslint/parser": "8.2.0", - "eslint": "9.9.0", + "eslint": "9.9.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", "eslint-plugin-jest": "28.8.0", @@ -159,7 +157,7 @@ "ng-mocks": "14.13.0", "prettier": "3.3.3", "sass": "1.77.8", - "ts-jest": "29.2.4", + "ts-jest": "29.2.5", "typescript": "5.5.4", "weak-napi": "2.0.2" }, diff --git a/src/main/webapp/app/admin/legal/legal-document-update.component.html b/src/main/webapp/app/admin/legal/legal-document-update.component.html index d581d9dcf664..7ab8f31edb25 100644 --- a/src/main/webapp/app/admin/legal/legal-document-update.component.html +++ b/src/main/webapp/app/admin/legal/legal-document-update.component.html @@ -3,15 +3,14 @@

-
@if (!unsavedChanges && !isSaving) { @@ -34,13 +33,7 @@

}

- diff --git a/src/main/webapp/app/admin/legal/legal-document-update.component.ts b/src/main/webapp/app/admin/legal/legal-document-update.component.ts index 11151a4410c1..cbe5aafc7cd5 100644 --- a/src/main/webapp/app/admin/legal/legal-document-update.component.ts +++ b/src/main/webapp/app/admin/legal/legal-document-update.component.ts @@ -1,14 +1,14 @@ import { AfterContentChecked, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; import { faBan, faCheckCircle, faCircleNotch, faExclamationTriangle, faSave } from '@fortawesome/free-solid-svg-icons'; import { LegalDocumentService } from 'app/shared/service/legal-document.service'; -import { MarkdownEditorComponent, MarkdownEditorHeight } from 'app/shared/markdown-editor/markdown-editor.component'; +import { MarkdownEditorHeight } from 'app/shared/markdown-editor/markdown-editor.component'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { UnsavedChangesWarningComponent } from 'app/admin/legal/unsaved-changes-warning/unsaved-changes-warning.component'; import { LegalDocument, LegalDocumentLanguage, LegalDocumentType } from 'app/entities/legal-document.model'; import { ActivatedRoute } from '@angular/router'; import { Observable, tap } from 'rxjs'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; -import { ArtemisMarkdownService } from 'app/shared/markdown.service'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; @Component({ selector: 'jhi-privacy-statement-update-component', @@ -35,12 +35,12 @@ export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked legalDocumentType: LegalDocumentType = LegalDocumentType.PRIVACY_STATEMENT; unsavedChanges = false; isSaving = false; - @ViewChild(MarkdownEditorComponent, { static: false }) markdownEditor: MarkdownEditorComponent; + @ViewChild(MarkdownEditorMonacoComponent, { static: false }) markdownEditor: MarkdownEditorMonacoComponent; + currentContentTrimmed = ''; currentLanguage = this.DEFAULT_LANGUAGE; unsavedChangesWarning: NgbModalRef; titleKey: string; - private languageChangeInPreview: boolean; constructor( private legalDocumentService: LegalDocumentService, @@ -48,7 +48,6 @@ export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked private route: ActivatedRoute, private languageHelper: JhiLanguageHelper, private changeDetectorRef: ChangeDetectorRef, - private markdownService: ArtemisMarkdownService, ) {} ngOnInit() { @@ -84,7 +83,7 @@ export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked updateLegalDocument() { this.isSaving = true; - this.legalDocument.text = this.markdownEditor.markdown!; + this.legalDocument.text = this.currentContentTrimmed; if (this.legalDocumentType === LegalDocumentType.PRIVACY_STATEMENT) { this.legalDocumentService.updatePrivacyStatement(this.legalDocument).subscribe((statement) => { this.setUpdatedDocument(statement); @@ -102,29 +101,27 @@ export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked this.isSaving = false; } - checkUnsavedChanges(content: string) { + onContentChanged(content: string) { + this.currentContentTrimmed = content.trim(); this.unsavedChanges = content !== this.legalDocument.text; } - onLanguageChange(legalDocumentLanguage: any) { + onLanguageChange(legalDocumentLanguage: LegalDocumentLanguage) { if (this.unsavedChanges) { this.showWarning(legalDocumentLanguage); } else { - this.markdownEditor.markdown = ''; this.currentLanguage = legalDocumentLanguage; this.getLegalDocumentForUpdate(this.legalDocumentType, legalDocumentLanguage).subscribe((document) => { this.legalDocument = document; + this.markdownEditor.markdown = this.legalDocument.text; + // Ensure the new text is parsed and displayed in the preview + this.markdownEditor.parseMarkdown(); this.unsavedChanges = false; - // if we are currently in preview mode, we need to update the preview - if (this.markdownEditor.previewMode) { - this.markdownEditor.previewTextAsHtml = this.markdownService.safeHtmlForMarkdown(this.legalDocument.text); - this.languageChangeInPreview = true; - } }); } } - showWarning(legalDocumentLanguage: any) { + showWarning(legalDocumentLanguage: LegalDocumentLanguage) { this.unsavedChangesWarning = this.modalService.open(UnsavedChangesWarningComponent, { size: 'lg', backdrop: 'static' }); if (this.legalDocumentType === LegalDocumentType.PRIVACY_STATEMENT) { this.unsavedChangesWarning.componentInstance.textMessage = 'artemisApp.legal.privacyStatement.unsavedChangesWarning'; @@ -151,16 +148,4 @@ export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked ngAfterContentChecked() { this.changeDetectorRef.detectChanges(); } - - /** - * If the language is changed while we are in the preview mode, we must trigger a change event, so the ace editor updates its content. - * We must do this when the editor is visible because otherwise the editor will only be updated if you click on it once. - */ - updateTextIfLanguageChangedInPreview() { - if (this.languageChangeInPreview) { - // we have to trigger a change event, so the ace editor updates its content - this.markdownEditor.aceEditorContainer.getEditor().session._emit('change', { start: { row: 0, column: 0 }, end: { row: 0, column: 0 }, action: 'insert', lines: [] }); - this.languageChangeInPreview = false; - } - } } diff --git a/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.html b/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.html index 6e026c952a34..32fb9e3112d8 100644 --- a/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.html +++ b/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.html @@ -8,7 +8,7 @@
@@ -227,14 +229,14 @@

@@ -304,7 +300,7 @@
{{ 'artemisApp.userManagement.filter.authority.title' | artemis
-
{{ 'artemisApp.userManagement.filter.origin.title' | artemisTranslate: { num: this.filters.originFilter.size } }}
+
@@ -321,16 +317,18 @@
{{ 'artemisApp.userManagement.filter.origin.title' | artemisTra }
  • - +
  • -
    {{ 'artemisApp.userManagement.filter.registrationNumber.title' | artemisTranslate: { num: this.filters.registrationNumberFilter.size } }}
    +
    @@ -360,16 +358,14 @@
    {{ 'artemisApp.userManagement.filter.registrationNumber.title' (click)="this.toggleRegistrationNumberFilter()" [checked]="this.filters.registrationNumberFilter.size === 0" /> - +
    -
    {{ 'artemisApp.userManagement.filter.status.title' | artemisTranslate: { num: this.filters.statusFilter.size } }}
    +
    @@ -386,9 +382,7 @@
    {{ 'artemisApp.userManagement.filter.status.title' | artemisTra }
  • - +
  • @@ -396,9 +390,7 @@
    {{ 'artemisApp.userManagement.filter.status.title' | artemisTra } diff --git a/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.html b/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.html index 0997b352dbbd..97bd1ae26257 100644 --- a/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.html +++ b/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.html @@ -1,25 +1,20 @@ @if (isLoading) {
    - {{ 'loading' | artemisTranslate }} +
    } @if (!isLoading && complaint) { -

    - {{ complaint.complaintType === ComplaintType.MORE_FEEDBACK ? ('artemisApp.moreFeedback.review' | artemisTranslate) : ('artemisApp.complaint.review' | artemisTranslate) }} -

    +

    } @if (!isLoading && complaint) {
    @if (handled) { -
    - {{ - complaint.complaintType === ComplaintType.MORE_FEEDBACK - ? ('artemisApp.moreFeedback.alreadyHandled' | artemisTranslate) - : ('artemisApp.complaint.complaintAlreadyHandled' | artemisTranslate) - }} -
    +
    }
    @if (showLockDuration) { @@ -35,21 +30,13 @@

    } @if (lockedByCurrentUser) { - + }

    - {{ - complaint.complaintType === ComplaintType.MORE_FEEDBACK - ? ('artemisApp.moreFeedback.title' | artemisTranslate) - : ('artemisApp.complaint.title' | artemisTranslate) - }} - + @if (handled) { @if (complaint?.accepted) { @@ -71,13 +58,9 @@

    @if (handled || isAllowedToRespond) {
    -

    - {{ - complaint.complaintType === ComplaintType.MORE_FEEDBACK - ? ('artemisApp.moreFeedbackResponse.title' | artemisTranslate) - : ('artemisApp.complaintResponse.title' | artemisTranslate) - }} -

    +

    diff --git a/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.html b/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.html index 0651cc2ad7e5..ff642a21befc 100644 --- a/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.html +++ b/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.html @@ -3,10 +3,10 @@

    @if (complaintType === ComplaintType.COMPLAINT) { - {{ 'artemisApp.complaint.listOfComplaints.title' | artemisTranslate }} + } @if (complaintType === ComplaintType.MORE_FEEDBACK) { - {{ 'artemisApp.moreFeedback.list.title' | artemisTranslate }} + }

    @@ -15,16 +15,16 @@

    @if (!allComplaintsForTutorLoaded && complaintType === ComplaintType.COMPLAINT) { - {{ 'artemisApp.complaint.listOfComplaints.loadAllComplaintsExplanation' | artemisTranslate }} + } @if (!allComplaintsForTutorLoaded && complaintType === ComplaintType.MORE_FEEDBACK) { - {{ 'artemisApp.moreFeedback.list.loadAllRequestsExplanation' | artemisTranslate }} + } @if (allComplaintsForTutorLoaded && complaintType === ComplaintType.COMPLAINT) { - {{ 'artemisApp.complaint.listOfComplaints.allComplaintsLoaded' | artemisTranslate }} + } @if (allComplaintsForTutorLoaded && complaintType === ComplaintType.MORE_FEEDBACK) { - {{ 'artemisApp.moreFeedback.list.allRequestsLoaded' | artemisTranslate }} + } @if (!allComplaintsForTutorLoaded) { } @@ -59,13 +59,12 @@

    />

    @@ -80,48 +79,48 @@

    - {{ 'artemisApp.complaint.listOfComplaints.exercise' | artemisTranslate }} + - {{ 'artemisApp.complaint.listOfComplaints.submissionId' | artemisTranslate }} + - {{ 'artemisApp.complaint.listOfComplaints.assessorName' | artemisTranslate }} + @if (course?.isAtLeastInstructor) { - {{ 'artemisApp.complaint.listOfComplaints.studentLogin' | artemisTranslate }} + - {{ 'artemisApp.complaint.listOfComplaints.studentName' | artemisTranslate }} + - {{ 'artemisApp.complaint.listOfComplaints.reviewerName' | artemisTranslate }} + } - {{ 'artemisApp.complaint.listOfComplaints.dateAndTime' | artemisTranslate }} + - {{ 'artemisApp.complaint.listOfComplaints.responseTime' | artemisTranslate }} + - {{ 'artemisApp.complaint.listOfComplaints.status' | artemisTranslate }} + - {{ 'artemisApp.locks.lockStatus' | artemisTranslate }} + - {{ 'artemisApp.complaint.listOfComplaints.actions' | artemisTranslate }} + @@ -166,16 +165,16 @@

    @if (complaint.accepted === undefined) { - {{ 'artemisApp.complaint.listOfComplaints.noReply' | artemisTranslate }} + } @if (complaint.accepted === true && complaintType === ComplaintType.COMPLAINT) { - {{ 'artemisApp.complaint.listOfComplaints.accepted' | artemisTranslate }} + } @if (complaint.accepted === true && complaintType === ComplaintType.MORE_FEEDBACK) { - {{ 'artemisApp.moreFeedback.accepted' | artemisTranslate }} + } @if (complaint.accepted === false) { - {{ 'artemisApp.complaint.listOfComplaints.rejected' | artemisTranslate }} + } @@ -185,10 +184,10 @@

    @@ -199,10 +198,10 @@

    @if (complaintType === ComplaintType.COMPLAINT) { - {{ 'artemisApp.exerciseAssessmentDashboard.noComplaints' | artemisTranslate }} + } @if (complaintType === ComplaintType.MORE_FEEDBACK) { - {{ 'artemisApp.exerciseAssessmentDashboard.noMoreFeedbackRequests' | artemisTranslate }} + }

    diff --git a/src/main/webapp/app/complaints/request/complaint-request.component.html b/src/main/webapp/app/complaints/request/complaint-request.component.html index 5c3b8f448f1c..2f86094c7345 100644 --- a/src/main/webapp/app/complaints/request/complaint-request.component.html +++ b/src/main/webapp/app/complaints/request/complaint-request.component.html @@ -7,18 +7,13 @@ }} {{ complaint.submittedTime | artemisTimeAgo }} @if (complaint.accepted === true) { - - {{ - complaint.complaintType === ComplaintType.COMPLAINT - ? ('artemisApp.complaint.acceptedLong' | artemisTranslate) - : ('artemisApp.moreFeedback.acceptedLong' | artemisTranslate) - }} - + } @if (complaint.accepted === false) { - - {{ 'artemisApp.complaint.rejectedLong' | artemisTranslate }} - + }

    - +
    - +

    [ngModel]="hideOptional" (ngModelChange)="triggerOptionalExercises()" /> - + } @@ -120,33 +116,33 @@

    - {{ 'artemisApp.assessmentDashboard.exerciseType' | artemisTranslate }} + - {{ 'artemisApp.assessmentDashboard.exercise' | artemisTranslate }} + @if (!isTestRun) { - {{ 'artemisApp.assessmentDashboard.yourStatus' | artemisTranslate }} + } - {{ 'artemisApp.assessmentDashboard.exerciseAverageRating' | artemisTranslate }} + @if (!isExamMode) { - {{ 'artemisApp.assessmentDashboard.exerciseDueDate' | artemisTranslate }} + } @if (!isExamMode) { - {{ 'artemisApp.assessmentDashboard.assessmentsDueDate' | artemisTranslate }} + } - {{ 'artemisApp.assessmentDashboard.actions' | artemisTranslate }} + @@ -241,7 +237,7 @@

    @if (course && course.isAtLeastInstructor && tutorIssues.length > 0) {
    -

    {{ 'artemisApp.assessmentDashboard.tutorPerformanceIssues.title' | artemisTranslate }}

    +

    @for (issue of tutorIssues; track issue) {
      @if (issue.averageTutorValue < issue.allowedRange.lowerBound) { @@ -278,10 +274,10 @@

      {{ 'artemisApp.assessmentDashboard.tutorPerformanceIssues.title' | artemisTr }
      @if (!isExamMode) { -

      {{ 'artemisApp.assessmentDashboard.tutorLeaderboard.courseTitle' | artemisTranslate }}

      +

      } @if (isExamMode) { -

      {{ 'artemisApp.assessmentDashboard.tutorLeaderboard.examTitle' | artemisTranslate }}

      +

      }
      diff --git a/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.html b/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.html index 988ad7835bce..1bae6b366f55 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.html +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.html @@ -3,7 +3,7 @@ - {{ 'artemisApp.examManagement.gradingSystem' | artemisTranslate }} +

    } @@ -13,9 +11,7 @@
    - - {{ 'artemisApp.learningPath.graph.legend.matchStart.title' | artemisTranslate }} - +
    } @@ -23,9 +19,7 @@
    - - {{ 'artemisApp.learningPath.graph.legend.matchEnd.title' | artemisTranslate }} - +
    } @@ -33,9 +27,7 @@
    - - {{ 'artemisApp.learningPath.graph.legend.learningObject.title' | artemisTranslate }} - +
    } @@ -43,9 +35,7 @@
    - - {{ 'artemisApp.learningPath.graph.legend.completedLearningObject.title' | artemisTranslate }} - +
    } diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html index 396b2735560b..864bcc2920b0 100644 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html +++ b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html @@ -1,7 +1,7 @@ @if (isLoading) {
    - {{ 'loading' | artemisTranslate }} +
    } @else { diff --git a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html b/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html index 061c611f6b93..c8a10304da15 100644 --- a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html +++ b/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html @@ -1,7 +1,7 @@ @if (isLoading) {
    - {{ 'loading' | artemisTranslate }} +
    } diff --git a/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.html b/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.html index 8c267652beff..b7252be6dc5d 100644 --- a/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.html +++ b/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.html @@ -16,28 +16,28 @@