diff --git a/.ci/E2E-tests/cleanup.sh b/.ci/E2E-tests/cleanup.sh index 49750033d2f1..59956fa455e5 100755 --- a/.ci/E2E-tests/cleanup.sh +++ b/.ci/E2E-tests/cleanup.sh @@ -12,12 +12,8 @@ docker container stop $(docker ps -a -q) || true docker container rm $(docker ps -a -q) || true docker volume rm $(docker volume ls -q) || true -docker compose -f ./docker/cypress-E2E-tests-mysql.yml down -v -docker compose -f ./docker/cypress-E2E-tests-postgres.yml down -v docker compose -f ./docker/playwright-E2E-tests-mysql.yml down -v -docker compose -f ./docker/cypress-E2E-tests-local.yml down -v docker compose -f ./docker/playwright-E2E-tests-multi-node.yml down -v -docker compose -f ./docker/cypress-E2E-tests-multi-node.yml down -v # show all running docker containers and volumes after the cleanup to detect issues echo "SHOW RUNNING Docker containers and volumes:" diff --git a/.ci/E2E-tests/execute.sh b/.ci/E2E-tests/execute.sh index 18fbd4ebfa7d..e98ca13b5c28 100755 --- a/.ci/E2E-tests/execute.sh +++ b/.ci/E2E-tests/execute.sh @@ -6,11 +6,8 @@ DB="mysql" echo "CONFIGURATION:" echo "$CONFIGURATION" -echo "Test framework:" -echo "$TEST_FRAMEWORK" -if [ "$TEST_FRAMEWORK" = "playwright" ]; then - if [ "$CONFIGURATION" = "mysql" ]; then +if [ "$CONFIGURATION" = "mysql" ]; then COMPOSE_FILE="playwright-E2E-tests-mysql.yml" elif [ "$CONFIGURATION" = "postgres" ]; then COMPOSE_FILE="playwright-E2E-tests-postgres.yml" @@ -22,21 +19,6 @@ if [ "$TEST_FRAMEWORK" = "playwright" ]; then else echo "Invalid configuration. Please choose among mysql, postgres, mysql-localci or multi-node." exit 1 - fi -else - if [ "$CONFIGURATION" = "mysql" ]; then - COMPOSE_FILE="cypress-E2E-tests-mysql.yml" - elif [ "$CONFIGURATION" = "postgres" ]; then - COMPOSE_FILE="cypress-E2E-tests-postgres.yml" - DB="postgres" - elif [ "$CONFIGURATION" = "local" ]; then - COMPOSE_FILE="cypress-E2E-tests-local.yml" - elif [ "$CONFIGURATION" = "multi-node" ]; then - COMPOSE_FILE="cypress-E2E-tests-multi-node.yml" - else - echo "Invalid configuration. Please choose among mysql, postgres, local or multi-node." - exit 1 - fi fi echo "Compose file:" @@ -51,8 +33,7 @@ export HOST_HOSTNAME=$(hostname) cd docker #just pull everything else than artemis-app as we build it later either way -if [ "$TEST_FRAMEWORK" = "playwright" ]; then - if [ "$CONFIGURATION" = "multi-node" ]; then +if [ "$CONFIGURATION" = "multi-node" ]; then echo "Building for playwright (multi-node)" docker compose -f $COMPOSE_FILE pull artemis-playwright $DB nginx docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app-node-1 artemis-app-node-2 artemis-app-node-3 @@ -62,20 +43,8 @@ if [ "$TEST_FRAMEWORK" = "playwright" ]; then docker compose -f $COMPOSE_FILE pull artemis-playwright $DB nginx docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app docker compose -f $COMPOSE_FILE up --exit-code-from artemis-playwright - fi -else - if [ "$CONFIGURATION" = "multi-node" ]; then - echo "Building for cypress (multi-node)" - docker compose -f $COMPOSE_FILE pull artemis-cypress $DB nginx - docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app-node-1 artemis-app-node-2 artemis-app-node-3 - docker compose -f $COMPOSE_FILE up --exit-code-from artemis-cypress - else - echo "Building for cypress" - docker compose -f $COMPOSE_FILE pull artemis-cypress $DB nginx - docker compose -f $COMPOSE_FILE build --build-arg WAR_FILE_STAGE=external_builder --no-cache --pull artemis-app - docker compose -f $COMPOSE_FILE up --exit-code-from artemis-cypress - fi fi + exitCode=$? cd .. echo "Container exit code: $exitCode" diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8cd968c38187..2296ed26a829 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,9 @@ 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 +106,9 @@ Prerequisites: #### Exam Mode Test - [ ] Test 1 - [ ] Test 2 +#### Performance Tests +- [ ] Test 1 +- [ ] Test 2 ### Test Coverage diff --git a/.github/labeler.yml b/.github/labeler.yml index 5dd34c477214..d9295deb1372 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -10,9 +10,9 @@ tests: - changed-files: - any-glob-to-any-file: src/test/**/* -cypress: +playwright: - changed-files: - - any-glob-to-any-file: src/test/cypress/**/* + - any-glob-to-any-file: src/test/playwright/**/* database: - changed-files: diff --git a/.github/workflows/analysis-of-endpoint-connections.yml b/.github/workflows/analysis-of-endpoint-connections.yml index 25849bddd012..f5f2dd4edb71 100644 --- a/.github/workflows/analysis-of-endpoint-connections.yml +++ b/.github/workflows/analysis-of-endpoint-connections.yml @@ -1,3 +1,5 @@ +name: Analysis of Endpoint Connections + on: workflow_dispatch: pull_request: @@ -15,7 +17,7 @@ env: java: 21 jobs: - analysis-of-endpoint-connections: + Parse-rest-calls-and-endpoints: timeout-minutes: 10 runs-on: ubuntu-latest steps: @@ -24,39 +26,70 @@ jobs: with: fetch-depth: 0 - - name: Get list of modified files - run: | - git diff --name-only origin/${{ github.event.pull_request.base.ref }} HEAD > modified_files.txt + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '${{ env.java }}' + distribution: 'temurin' + cache: 'gradle' - # Analyze the client sided REST-API calls - - name: Set up Node.js + - name: Set up node.js uses: actions/setup-node@v4 with: node-version: '${{ env.node }}' - - name: Install and compile TypeScript + - name: Parse client sided REST-API calls run: | - cd supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/ npm install - tsc -p tsconfig.analysisOfEndpointConnections.json - - - name: Run analysis-of-endpoint-connections-client - run: | + tsc -p supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/tsconfig.analysisOfEndpointConnections.json node supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/AnalysisOfEndpointConnectionsClient.js - - name: Upload JSON file + - name: Parse server sided Endpoints + run: ./gradlew :supporting_scripts:analysis-of-endpoint-connections:runEndpointParser + + - name: Upload parsing results uses: actions/upload-artifact@v4 with: - name: rest-calls-json - path: supporting_scripts/analysis-of-endpoint-connections/restCalls.json + name: REST API Parsing Results + path: | + supporting_scripts/analysis-of-endpoint-connections/endpoints.json + supporting_scripts/analysis-of-endpoint-connections/restCalls.json + + Analysis-of-endpoint-connections: + needs: Parse-rest-calls-and-endpoints + timeout-minutes: 10 + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 - # Analyze the server sided endpoints - - name: Set up JDK 21 + - name: Set up JDK uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '${{ env.java }}' + cache: 'gradle' - - name: Run analysis-of-endpoint-connections - run: | - ./gradlew :supporting_scripts:analysis-of-endpoint-connections:run --args="$(cat modified_files.txt)" + - name: Download JSON files + uses: actions/download-artifact@v4 + with: + name: REST API Parsing Results + path: supporting_scripts/analysis-of-endpoint-connections/ + + - name: Analyze endpoints + run: + ./gradlew :supporting_scripts:analysis-of-endpoint-connections:runEndpointAnalysis + + - name: Analyze rest calls + run: + ./gradlew :supporting_scripts:analysis-of-endpoint-connections:runRestCallAnalysis + + - name: Upload analysis results + uses: actions/upload-artifact@v4 + with: + name: Endpoint and REST Call Analysis Results + path: | + supporting_scripts/analysis-of-endpoint-connections/endpointAnalysisResult.json + supporting_scripts/analysis-of-endpoint-connections/restCallAnalysisResult.json diff --git a/.gitignore b/.gitignore index 13cfd9a24518..8f71a8ae13d5 100644 --- a/.gitignore +++ b/.gitignore @@ -188,13 +188,6 @@ data-exports/ /docker/.docker-data/artemis-postgres-data/* !/docker/.docker-data/artemis-postgres-data/.gitkeep -###################### -# Cypress -###################### -/src/test/cypress/screenshots/ -/src/test/cypress/videos/ -/src/test/cypress/build - ###################### # Playwright ###################### 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..bbfc8945e870 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: @@ -159,7 +166,7 @@ Refer to [Using JHipster in production](http://www.jhipster.tech/production) for The following command can automate the deployment to a server. The example shows the deployment to the main Artemis test server (which runs a virtual machine): ```shell -./artemis-server-cli deploy username@artemistest.ase.in.tum.de -w build/libs/Artemis-7.5.1.war +./artemis-server-cli deploy username@artemistest.ase.in.tum.de -w build/libs/Artemis-7.5.2.war ``` ## Architecture diff --git a/angular.json b/angular.json index a2c893ff59f6..e5543ff2ce60 100644 --- a/angular.json +++ b/angular.json @@ -21,8 +21,6 @@ "builder": "@angular-devkit/build-angular:application", "options": { "allowedCommonJsDependencies": [ - "brace", - "brace/mode/java", "clone-deep", "crypto-js", "crypto", diff --git a/build.gradle b/build.gradle index ae88a8170eda..e53437485601 100644 --- a/build.gradle +++ b/build.gradle @@ -27,12 +27,12 @@ plugins { id "com.github.ben-manes.versions" version "0.51.0" id "com.github.andygoossens.modernizer" version "${modernizer_plugin_version}" id "com.gorylenko.gradle-git-properties" version "2.4.2" - id "org.owasp.dependencycheck" version "10.0.3" + id "org.owasp.dependencycheck" version "10.0.4" id "com.adarshr.test-logger" version "4.0.0" } group = "de.tum.in.www1.artemis" -version = "7.5.1" +version = "7.5.2" description = "Interactive Learning with Individual Feedback" java { @@ -284,12 +284,12 @@ dependencies { implementation "org.apache.sshd:sshd-sftp:${sshd_version}" // https://mvnrepository.com/artifact/net.sourceforge.plantuml/plantuml - implementation "net.sourceforge.plantuml:plantuml:1.2024.6" + implementation "net.sourceforge.plantuml:plantuml:1.2024.5" implementation "org.jasypt:jasypt:1.9.3" implementation "me.xdrop:fuzzywuzzy:1.4.0" implementation("org.yaml:snakeyaml") { version { - strictly "2.2" + strictly "2.3" // needed to reduce the number of vulnerabilities, also see https://mvnrepository.com/artifact/org.yaml/snakeyaml } } @@ -322,7 +322,7 @@ dependencies { implementation "com.github.docker-java:docker-java-transport-httpclient5:${docker_java_version}" // use newest version of commons-compress to avoid security issues through outdated dependencies - implementation "org.apache.commons:commons-compress:1.27.0" + implementation "org.apache.commons:commons-compress:1.27.1" // import JHipster dependencies BOM @@ -330,7 +330,7 @@ dependencies { implementation "tech.jhipster:jhipster-framework:${jhipster_dependencies_version}" implementation "org.springframework.boot:spring-boot-starter-cache:${spring_boot_version}" - implementation "io.micrometer:micrometer-registry-prometheus:1.12.6" + implementation "io.micrometer:micrometer-registry-prometheus:1.13.3" implementation "net.logstash.logback:logstash-logback-encoder:8.0" // Defines low-level streaming API, and includes JSON-specific implementations @@ -349,13 +349,19 @@ dependencies { implementation "com.hazelcast:hazelcast:${hazelcast_version}" implementation "com.hazelcast:hazelcast-spring:${hazelcast_version}" implementation "com.hazelcast:hazelcast-hibernate53:5.2.0" + implementation "javax.cache:cache-api:1.1.1" implementation "org.hibernate.orm:hibernate-core:${hibernate_version}" + implementation "com.zaxxer:HikariCP:5.1.0" + implementation "org.apache.commons:commons-text:1.12.0" implementation "org.apache.commons:commons-math3:3.6.1" + implementation "javax.transaction:javax.transaction-api:1.3" + implementation "org.liquibase:liquibase-core:${liquibase_version}" + implementation "org.springframework.boot:spring-boot-starter-validation:${spring_boot_version}" implementation "org.springframework.boot:spring-boot-loader-tools:${spring_boot_version}" implementation "org.springframework.boot:spring-boot-starter-mail:${spring_boot_version}" @@ -373,8 +379,8 @@ dependencies { implementation "org.springframework.boot:spring-boot-starter-oauth2-resource-server:${spring_boot_version}" implementation "org.springframework.boot:spring-boot-starter-oauth2-client:${spring_boot_version}" - implementation "org.springframework.ldap:spring-ldap-core:3.2.4" - implementation "org.springframework.data:spring-data-ldap:3.3.2" + implementation "org.springframework.ldap:spring-ldap-core:3.2.6" + implementation "org.springframework.data:spring-data-ldap:3.3.3" implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.3") { // NOTE: these modules contain security vulnerabilities and are not needed @@ -385,9 +391,9 @@ dependencies { implementation "org.springframework.cloud:spring-cloud-commons:4.1.4" implementation "io.netty:netty-all:4.1.112.Final" - implementation "io.projectreactor.netty:reactor-netty:1.1.21" - implementation "org.springframework:spring-messaging:6.1.11" - implementation "org.springframework.retry:spring-retry:2.0.7" + implementation "io.projectreactor.netty:reactor-netty:1.1.22" + implementation "org.springframework:spring-messaging:6.1.12" + implementation "org.springframework.retry:spring-retry:2.0.8" implementation "org.springframework.security:spring-security-config:${spring_security_version}" implementation "org.springframework.security:spring-security-data:${spring_security_version}" @@ -421,13 +427,13 @@ dependencies { } implementation "io.springfox:springfox-bean-validators:3.0.0" implementation "com.mysql:mysql-connector-j:9.0.0" - implementation "org.postgresql:postgresql:42.7.3" + implementation "org.postgresql:postgresql:42.7.4" implementation "org.zalando:problem-spring-web:0.29.1" implementation "org.zalando:jackson-datatype-problem:0.27.1" implementation "com.ibm.icu:icu4j-charset:75.1" - implementation "com.github.seancfoley:ipaddress:5.5.0" - implementation "org.apache.maven:maven-model:3.9.8" + implementation "com.github.seancfoley:ipaddress:5.5.1" + implementation "org.apache.maven:maven-model:3.9.9" // NOTE: 3.0.2 is broken for splitting lecture specific PDFs implementation "org.apache.pdfbox:pdfbox:3.0.1" implementation "org.apache.commons:commons-csv:1.11.0" @@ -436,12 +442,15 @@ dependencies { implementation "net.lingala.zip4j:zip4j:2.11.5" implementation "org.jgrapht:jgrapht-core:1.5.2" // use newest version of guava to avoid security issues through outdated dependencies - implementation "com.google.guava:guava:33.2.1-jre" + implementation "com.google.guava:guava:33.3.0-jre" implementation "com.sun.activation:jakarta.activation:2.0.1" // use newest version of gson to avoid security issues through outdated dependencies implementation "com.google.code.gson:gson:2.11.0" + + implementation "com.google.errorprone:error_prone_annotations:2.31.0" + annotationProcessor "org.hibernate:hibernate-jpamodelgen:${hibernate_version}" annotationProcessor("org.glassfish.jaxb:jaxb-runtime:${jaxb_runtime_version}") { exclude group: "jakarta.ws.rs", module: "jsr311-api" @@ -470,11 +479,11 @@ dependencies { testImplementation "org.assertj:assertj-core:3.26.3" testImplementation "org.mockito:mockito-core:${mockito_version}" testImplementation "org.mockito:mockito-junit-jupiter:${mockito_version}" - testImplementation "io.github.classgraph:classgraph:4.8.174" + testImplementation "io.github.classgraph:classgraph:4.8.175" testImplementation "org.awaitility:awaitility:4.2.2" testImplementation "org.apache.maven.shared:maven-invoker:3.3.0" - testImplementation "org.gradle:gradle-tooling-api:8.9" - testImplementation "org.apache.maven.surefire:surefire-report-parser:3.3.1" + testImplementation "org.gradle:gradle-tooling-api:8.10" + testImplementation "org.apache.maven.surefire:surefire-report-parser:3.5.0" testImplementation "com.opencsv:opencsv:5.9" testImplementation("io.zonky.test:embedded-database-spring-test:2.5.1") { exclude group: "org.testcontainers", module: "mariadb" @@ -486,7 +495,7 @@ dependencies { } testImplementation("net.bytebuddy:byte-buddy") { version { - strictly "1.14.18" + strictly "1.15.1" } } // cannot update due to "Syntax error in SQL statement "WITH ids_to_delete" @@ -558,7 +567,7 @@ tasks.withType(Test).configureEach { } wrapper { - gradleVersion = "8.9" + gradleVersion = "8.10" } tasks.register("stage") { diff --git a/docker/artemis-migration-check-postgres.yml b/docker/artemis-migration-check-postgres.yml index 5f7cf661c39f..1ac60ab3ef45 100644 --- a/docker/artemis-migration-check-postgres.yml +++ b/docker/artemis-migration-check-postgres.yml @@ -9,8 +9,8 @@ services: service: artemis-app env_file: - ./artemis/config/postgres.env - - ./artemis/config/cypress.env - - ./artemis/config/cypress-postgres.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-postgres.env - ./artemis/config/migration-check.env depends_on: postgresql: diff --git a/docker/artemis/config/cypress-local.env b/docker/artemis/config/playwright-local.env similarity index 91% rename from docker/artemis/config/cypress-local.env rename to docker/artemis/config/playwright-local.env index 5ca5931297ad..6b7805deacca 100644 --- a/docker/artemis/config/cypress-local.env +++ b/docker/artemis/config/playwright-local.env @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------------------------------------------------- -# Artemis configuration overrides for the Cypress E2E Postgres setups +# Artemis configuration overrides for the Playwright E2E Postgres setups # ---------------------------------------------------------------------------------------------------------------------- SPRING_PROFILES_ACTIVE="artemis,scheduling,localvc,localci,buildagent,core,prod,docker" diff --git a/docker/artemis/config/cypress-postgres.env b/docker/artemis/config/playwright-postgres.env similarity index 95% rename from docker/artemis/config/cypress-postgres.env rename to docker/artemis/config/playwright-postgres.env index ae155aca2873..9cfcc9ade6a6 100644 --- a/docker/artemis/config/cypress-postgres.env +++ b/docker/artemis/config/playwright-postgres.env @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------------------------------------------------- -# Artemis configuration overrides for the Cypress E2E Postgres setups +# Artemis configuration overrides for the Playwright E2E Postgres setups # ---------------------------------------------------------------------------------------------------------------------- SPRING_PROFILES_ACTIVE="artemis,scheduling,jenkins,gitlab,core,prod,docker" diff --git a/docker/artemis/config/cypress.env b/docker/artemis/config/playwright.env similarity index 93% rename from docker/artemis/config/cypress.env rename to docker/artemis/config/playwright.env index c056a8d9004c..a3ee64a57442 100644 --- a/docker/artemis/config/cypress.env +++ b/docker/artemis/config/playwright.env @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------------------------------------------------- -# Common Artemis configurations for the Cypress E2E MySQL and Postgres setups +# Common Artemis configurations for the Playwright E2E MySQL and Postgres setups # ---------------------------------------------------------------------------------------------------------------------- SPRING_DATASOURCE_PASSWORD="" @@ -27,6 +27,8 @@ ARTEMIS_CONTINUOUSINTEGRATION_EMPTYCOMMITNECESSARY="true" ARTEMIS_APOLLON_CONVERSIONSERVICEURL="https://apollon.ase.in.tum.de/api/converter" +ARTEMIS_TELEMETRY_ENABLED="false" + # Token is valid 3 days JHIPSTER_SECURITY_AUTHENTICATION_JWT_TOKENVALIDITYINSECONDS="259200" # Token is valid 30 days @@ -38,6 +40,7 @@ INFO_IMPRINT="https://ase.in.tum.de/lehrstuhl_1/component/content/article/179-im INFO_TESTSERVER="true" INFO_TEXTASSESSMENTANALYTICSENABLED="true" INFO_STUDENTEXAMSTORESESSIONDATA="true" +INFO_OPERATORNAME="TUM" LOGGING_FILE_NAME="/opt/artemis/data/artemis.log" diff --git a/docker/cypress-E2E-tests-local.yml b/docker/cypress-E2E-tests-local.yml deleted file mode 100644 index cf69e47df760..000000000000 --- a/docker/cypress-E2E-tests-local.yml +++ /dev/null @@ -1,62 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress Setup MySQL -# ---------------------------------------------------------------------------------------------------------------------- - -services: - mysql: - extends: - file: ./mysql.yml - service: mysql - - artemis-app: - extends: - file: ./artemis.yml - service: artemis-app - user: 0:0 - depends_on: - mysql: - condition: service_healthy - env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-local.env - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - nginx: - extends: - file: ./nginx.yml - service: nginx - # the artemis-app service needs to be started, otherwise there are problems with name resolution in docker - depends_on: - artemis-app: - condition: service_started - volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - ports: - - "80:80" - - "443:443" - # see comments in artemis/config/cypress.env why this port is necessary - - "54321:54321" - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - artemis-app: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "Local" - SORRY_CYPRESS_PROJECT_ID: "artemis-local" - CYPRESS_createUsers: "true" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:setup && (npm run cypress:record:local & sleep 60 && npm run cypress:record:local & wait)" - -networks: - artemis: - driver: "bridge" - name: artemis -volumes: - artemis-mysql-data: - name: artemis-mysql-data - artemis-data: - name: artemis-data diff --git a/docker/cypress-E2E-tests-multi-node.yml b/docker/cypress-E2E-tests-multi-node.yml deleted file mode 100644 index 5fb326f75f60..000000000000 --- a/docker/cypress-E2E-tests-multi-node.yml +++ /dev/null @@ -1,134 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress setup for multi-node -# ---------------------------------------------------------------------------------------------------------------------- - -services: - artemis-app-node-1: - &artemis-app-base - container_name: artemis-app-node-1 - extends: - file: ./artemis.yml - service: artemis-app - image: ghcr.io/ls1intum/artemis:${ARTEMIS_DOCKER_TAG:-latest} - depends_on: - &depends-on-base - mysql: - condition: service_healthy - jhipster-registry: - condition: service_healthy - activemq-broker: - condition: service_healthy - pull_policy: always - restart: always - group_add: - - ${DOCKER_GROUP_ID:-0} - env_file: - - ./artemis/config/prod-multinode.env - - ./artemis/config/node1.env - - ./artemis/config/cypress.env - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - artemis-app-node-2: - <<: *artemis-app-base - container_name: artemis-app-node-2 - depends_on: - <<: *depends-on-base - artemis-app-node-1: - condition: service_healthy - env_file: - - ./artemis/config/prod-multinode.env - - ./artemis/config/node2.env - - ./artemis/config/cypress.env - - artemis-app-node-3: - <<: *artemis-app-base - container_name: artemis-app-node-3 - depends_on: - <<: *depends-on-base - artemis-app-node-1: - condition: service_healthy - env_file: - - ./artemis/config/prod-multinode.env - - ./artemis/config/node3.env - - ./artemis/config/cypress.env - - jhipster-registry: - extends: - file: ./broker-registry.yml - service: jhipster-registry - networks: - - artemis - - activemq-broker: - extends: - file: ./broker-registry.yml - service: activemq-broker - networks: - - artemis - - mysql: - extends: - file: ./mysql.yml - service: mysql - restart: always - - nginx: - extends: - file: ./nginx.yml - service: nginx - depends_on: - artemis-app-node-1: - condition: service_started - artemis-app-node-2: - condition: service_started - artemis-app-node-3: - condition: service_started - restart: always - ports: - - '80:80' - - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary - - '54321:54321' - volumes: - - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} - target: "/certs/fullchain.pem" - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} - target: "/certs/priv_key.pem" - - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - nginx: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "Local" - SORRY_CYPRESS_PROJECT_ID: "artemis-local" - CYPRESS_createUsers: "true" - command: > - sh -c ' - cd /app/artemis/src/test/cypress && - chmod 777 /root && - npm ci && - npm run cypress:setup && - (npm run cypress:record:local & sleep 60 && npm run cypress:record:local & wait) - ' - -networks: - artemis: - driver: "bridge" - name: artemis - -volumes: - artemis-mysql-data: - name: artemis-mysql-data - artemis-data: - name: artemis-data - diff --git a/docker/cypress-E2E-tests-mysql.yml b/docker/cypress-E2E-tests-mysql.yml deleted file mode 100644 index a14c4c163ce7..000000000000 --- a/docker/cypress-E2E-tests-mysql.yml +++ /dev/null @@ -1,60 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress Setup MySQL -# ---------------------------------------------------------------------------------------------------------------------- - -services: - mysql: - extends: - file: ./mysql.yml - service: mysql - - artemis-app: - extends: - file: ./artemis.yml - service: artemis-app - depends_on: - mysql: - condition: service_healthy - env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-mysql.env - - nginx: - extends: - file: ./nginx.yml - service: nginx - # the artemis-app service needs to be started, otherwise there are problems with name resolution in docker - depends_on: - artemis-app: - condition: service_started - volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - ports: - - "80:80" - - "443:443" - # see comments in artemis/config/cypress.env why this port is necessary - - "54321:54321" - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - artemis-app: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "MySQL" - SORRY_CYPRESS_PROJECT_ID: "artemis-mysql" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:setup && (npm run cypress:record:mysql & sleep 60 && npm run cypress:record:mysql & wait)" -# Old run method using plain cypress kept here as backup -# command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:run" - -networks: - artemis: - driver: "bridge" - name: artemis -volumes: - artemis-mysql-data: - name: artemis-mysql-data - artemis-data: - name: artemis-data diff --git a/docker/cypress-E2E-tests-postgres.yml b/docker/cypress-E2E-tests-postgres.yml deleted file mode 100644 index 41fe8edf5faa..000000000000 --- a/docker/cypress-E2E-tests-postgres.yml +++ /dev/null @@ -1,61 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress Setup Postgres -# ---------------------------------------------------------------------------------------------------------------------- - -services: - postgres: - extends: - file: ./postgres.yml - service: postgres - - artemis-app: - extends: - file: ./artemis.yml - service: artemis-app - depends_on: - postgres: - condition: service_healthy - env_file: - - ./artemis/config/postgres.env - - ./artemis/config/cypress.env - - ./artemis/config/cypress-postgres.env - - nginx: - extends: - file: ./nginx.yml - service: nginx - # the artemis-app service needs to be started, otherwise there are problems with name resolution in docker - depends_on: - artemis-app: - condition: service_started - volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro - ports: - - "80:80" - - "443:443" - # see comments in artemis/config/cypress.env why this port is necessary - - "54321:54321" - - artemis-cypress: - extends: - file: ./cypress.yml - service: artemis-cypress - depends_on: - artemis-app: - condition: service_healthy - environment: - CYPRESS_DB_TYPE: "Postgres" - SORRY_CYPRESS_PROJECT_ID: "artemis-postgres" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:setup && (npm run cypress:record:postgres & sleep 60 && npm run cypress:record:postgres & wait)" -# Old run method using plain cypress kept here as backup -# command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:run" - -networks: - artemis: - driver: "bridge" - name: artemis -volumes: - artemis-postgres-data: - name: artemis-postgres-data - artemis-data: - name: artemis-data diff --git a/docker/cypress.yml b/docker/cypress.yml deleted file mode 100644 index c4dd1aa58bbb..000000000000 --- a/docker/cypress.yml +++ /dev/null @@ -1,41 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Cypress base service -# ---------------------------------------------------------------------------------------------------------------------- - -services: - artemis-cypress: - # Cypress image with node and chrome browser installed (Cypress installation needs to be done separately because we require additional dependencies) - image: docker.io/cypress/browsers:node-20.6.1-chrome-116.0.5845.187-1-ff-117.0-edge-116.0.1938.76-1 - pull_policy: if_not_present - environment: - CYPRESS_baseUrl: "https://artemis-nginx" - CYPRESS_video: "${bamboo_cypress_video_enabled}" - CYPRESS_adminUsername: "${bamboo_artemis_admin_username}" - CYPRESS_adminPassword: "${bamboo_artemis_admin_password}" - CYPRESS_username: "${bamboo_cypress_username_template}" - CYPRESS_password: "${bamboo_cypress_password_template}" - CYPRESS_allowGroupCustomization: "true" - CYPRESS_studentGroupName: "artemis-e2etest-students" - CYPRESS_tutorGroupName: "artemis-e2etest-tutors" - CYPRESS_editorGroupName: "artemis-e2etest-editors" - CYPRESS_instructorGroupName: "artemis-e2etest-instructors" - CYPRESS_createUsers: "${bamboo_cypress_create_users}" - # use alternative cypress version to avoid blocking sorry cypress (see https://currents.dev/readme/integration-with-cypress/alternative-cypress-binaries) - CYPRESS_DOWNLOAD_MIRROR: "https://cy-cdn.currents.dev" - SORRY_CYPRESS_KEY: "${bamboo_sorry_cypress_record_secret}" - SORRY_CYPRESS_URL: "${bamboo_sorry_cypress_url}" - SORRY_CYPRESS_BUILD_ID: "${bamboo_buildNumber}" - SORRY_CYPRESS_BRANCH_NAME: "${bamboo_planRepository_branchName}" - SORRY_CYPRESS_RERUN_COUNT: "${bamboo_RerunBuildTriggerReason_noOfRetries}" - SORRY_CYPRESS_PROJECT_ID: "artemis-mysql" - NO_COLOR: "1" - command: sh -c "cd /app/artemis/src/test/cypress && chmod 777 /root && npm ci && npm run cypress:run" - volumes: - - ..:/app/artemis - networks: - - artemis - -networks: - artemis: - driver: "bridge" - name: artemis diff --git a/docker/nginx.yml b/docker/nginx.yml index 0baaf14a0629..6e111a256a91 100644 --- a/docker/nginx.yml +++ b/docker/nginx.yml @@ -18,8 +18,6 @@ services: - ./nginx/dhparam.pem:/etc/nginx/dhparam.pem:ro - ./nginx/nginx_503.html:/usr/share/nginx/html/503.html:ro - ./nginx/70-artemis-setup.sh:/docker-entrypoint.d/70-artemis-setup.sh - - ../src/test/cypress/certs/artemis-nginx+4.pem:/certs/fullchain.pem:ro - - ../src/test/cypress/certs/artemis-nginx+4-key.pem:/certs/priv_key.pem:ro - ../src/test/playwright/certs/artemis-nginx+4.pem:/certs/fullchain.pem:ro - ../src/test/playwright/certs/artemis-nginx+4-key.pem:/certs/priv_key.pem:ro # ulimits adopted from the nginx_security_limits.conf in the Artemis ansible collection @@ -29,7 +27,8 @@ services: hard: 1048576 ports: - "80:80" - - "443:443" + - "443:443/tcp" + - "443:443/udp" # HTTP/3 - "7921:7921" # Git SSH # expose the port to make it reachable docker internally even if the external port mapping changes expose: diff --git a/docker/nginx/artemis-nginx-cypress.conf b/docker/nginx/artemis-nginx-playwright.conf similarity index 100% rename from docker/nginx/artemis-nginx-cypress.conf rename to docker/nginx/artemis-nginx-playwright.conf diff --git a/docker/nginx/artemis-nginx.conf b/docker/nginx/artemis-nginx.conf index fa8f2d0376eb..cbc10836e993 100644 --- a/docker/nginx/artemis-nginx.conf +++ b/docker/nginx/artemis-nginx.conf @@ -18,8 +18,15 @@ server { } server { - listen 443 ssl http2; - listen [::]:443 ssl http2; + listen 443 ssl; + listen 443 quic reuseport; + listen [::]:443 ssl; + listen [::]:443 quic reuseport; + http2 on; + http3 on; + http3_hq on; + quic_retry on; + server_name _; ssl_certificate /certs/fullchain.pem; @@ -36,6 +43,7 @@ server { ssl_stapling on; ssl_stapling_verify on; # ssl_early_data on; + quic_gso on; include includes/artemis-server.conf; } diff --git a/docker/nginx/artemis-server.conf b/docker/nginx/artemis-server.conf index a9eb9d592d54..d00af9b9c3dd 100644 --- a/docker/nginx/artemis-server.conf +++ b/docker/nginx/artemis-server.conf @@ -23,6 +23,8 @@ location / { fastcgi_send_timeout 900s; fastcgi_read_timeout 900s; client_max_body_size 128M; + # used to advertise the availability of HTTP/3 + add_header alt-svc 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000'; } location /api/authenticate { diff --git a/docker/playwright-E2E-tests-multi-node.yml b/docker/playwright-E2E-tests-multi-node.yml index 576a695d37ca..bd8a0f52ff05 100644 --- a/docker/playwright-E2E-tests-multi-node.yml +++ b/docker/playwright-E2E-tests-multi-node.yml @@ -25,7 +25,7 @@ services: env_file: - ./artemis/config/prod-multinode.env - ./artemis/config/node1.env - - ./artemis/config/cypress.env + - ./artemis/config/playwright.env artemis-app-node-2: <<: *artemis-app-base @@ -37,7 +37,7 @@ services: env_file: - ./artemis/config/prod-multinode.env - ./artemis/config/node2.env - - ./artemis/config/cypress.env + - ./artemis/config/playwright.env artemis-app-node-3: <<: *artemis-app-base @@ -49,7 +49,7 @@ services: env_file: - ./artemis/config/prod-multinode.env - ./artemis/config/node3.env - - ./artemis/config/cypress.env + - ./artemis/config/playwright.env jhipster-registry: extends: @@ -86,17 +86,17 @@ services: ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' volumes: - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" diff --git a/docker/playwright-E2E-tests-mysql-localci.yml b/docker/playwright-E2E-tests-mysql-localci.yml index a5536baabbb9..aa501ae75192 100644 --- a/docker/playwright-E2E-tests-mysql-localci.yml +++ b/docker/playwright-E2E-tests-mysql-localci.yml @@ -17,8 +17,8 @@ services: mysql: condition: service_healthy env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-local.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-local.env volumes: - /var/run/docker.sock:/var/run/docker.sock @@ -31,11 +31,11 @@ services: artemis-app: condition: service_started volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' artemis-playwright: diff --git a/docker/playwright-E2E-tests-mysql.yml b/docker/playwright-E2E-tests-mysql.yml index 65f9a343b515..c269ce9b206e 100644 --- a/docker/playwright-E2E-tests-mysql.yml +++ b/docker/playwright-E2E-tests-mysql.yml @@ -16,8 +16,8 @@ services: mysql: condition: service_healthy env_file: - - ./artemis/config/cypress.env - - ./artemis/config/cypress-mysql.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-mysql.env nginx: extends: @@ -28,11 +28,11 @@ services: artemis-app: condition: service_started volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' artemis-playwright: diff --git a/docker/playwright-E2E-tests-postgres.yml b/docker/playwright-E2E-tests-postgres.yml index b2f1aa201bbf..92bde9b2cfe0 100644 --- a/docker/playwright-E2E-tests-postgres.yml +++ b/docker/playwright-E2E-tests-postgres.yml @@ -17,8 +17,8 @@ services: condition: service_healthy env_file: - ./artemis/config/postgres.env - - ./artemis/config/cypress.env - - ./artemis/config/cypress-postgres.env + - ./artemis/config/playwright.env + - ./artemis/config/playwright-postgres.env nginx: extends: @@ -29,11 +29,11 @@ services: artemis-app: condition: service_started volumes: - - ./nginx/artemis-nginx-cypress.conf:/etc/nginx/conf.d/artemis-nginx-cypress.conf:ro + - ./nginx/artemis-nginx-playwright.conf:/etc/nginx/conf.d/artemis-nginx-playwright.conf:ro ports: - '80:80' - '443:443' - # see comments in artemis/config/cypress.env why this port is necessary + # see comments in artemis/config/playwright.env why this port is necessary - '54321:54321' artemis-playwright: diff --git a/docker/sorry-cypress/.htpasswd b/docker/sorry-cypress/.htpasswd deleted file mode 100644 index 5122db186396..000000000000 --- a/docker/sorry-cypress/.htpasswd +++ /dev/null @@ -1,4 +0,0 @@ -# Sample .htaccess file with credentials artemis_admin:artemis_admin -# Change for production! - -artemis_admin: $apr1$95ecxdaf$I1MlWdUoSYOfjdlPqZRGq/ diff --git a/docker/sorry-cypress/README.md b/docker/sorry-cypress/README.md deleted file mode 100644 index 4ed3fb156daf..000000000000 --- a/docker/sorry-cypress/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Sorry Cypress - -Sorry Cypress is an open-source, self-hosted alternative to the Cypress Dashboard, which allows you to manage, execute, and observe your Cypress test runs and results. - -## Usage - -The dashboard is available on https://sorry-cypress.ase.cit.tum.de, which is secured with a basic authentication. The credentials can be found on [Confluence](https://confluence.ase.in.tum.de/display/ArTEMiS/Sorry+Cypress+Dashboard). - -After login, you can see the active projects for artemis. One for the MySQL and one for the Postgres runs. Clicking on either project will reveal the last runs. Each run is named with a combination of the branch name and the run number. E.g for the 4th run of the branch `feature/add-awesomeness` the name would be `feature/add-awesomeness #4`. On the run overview page, each run shows some basic information, like the run time, if the run is currently running (red dot in front of the run name), the passed and failed tests etc. -To debug one of the recent runs, click on the single run and sorry cypress now shows a table with all the different test files that were tested. Select a failed test and you can now see a video of this run. Since cypress is a graphical testing tool, analyzing a video is ofter much more helpful then analyzing the corresponding logs. - -*Hint*: Sometimes it takes a while until the videos are available. So if no video is shown for the run, just visit the page again some minutes later. - -## Tech Stack - -Also visit the sorry cypress docs for more information: https://docs.sorry-cypress.dev/ - -### Dashboard - -As mentioned earlier the dashboard allows the developers to get an overview of the last test runs and helps them debug the runs. - -### API - -The API service is used to gather information from the CI and also provide them to the dashboard. - -### MongoDB - -This service is used as a database to store all the run information. - -### Director - -A service that accepts data from the api and saves it into the database. - -### Minio - -A AWS S3 and Google Cloud compatible dropin replacement, that is used as a storage provider, storing the videos and screenshots of the run. - -### Nginx - -Used as a reverse proxy, that allows to access the different endpoints via HTTPS. diff --git a/docker/sorry-cypress/SETUP.md b/docker/sorry-cypress/SETUP.md deleted file mode 100644 index 7d2027c5af6b..000000000000 --- a/docker/sorry-cypress/SETUP.md +++ /dev/null @@ -1,25 +0,0 @@ -# Setup Sorry Cypress - -1. Use the docker compose file (called `sorry-cypress.yml`) in this folder as base. -2. Copy the `sorry-cypress.env` file to the folder, where the compose files is placed. Now change all the values in the env file accordingly. - 1. Replace the `` and `` with applicable values (e.g random). These values are needed to manage the minio instance. - 2. Set a random key for the `ALLOWED_KEYS` (by replacing ``). This key is needed to authorize bamboo against the sorry-cypress dashboard. -3. Place all the necessary NGINX config files in their locations - 1. Place the `nginx.conf` file from this folder into this path `files/nginx/nginx.conf` - 2. Place a generated `.htpasswd` file into this path `files/nginx/.htpasswd` (e.g. use a online htpasswd generator). This file is used for the basic auth part of the main dashboard. - 3. Place/link a public SSL certificate file in this path `files/nginx/fullchain.pem` - 4. Place/link a private SSL certificate file in this path `files/nginx/privkey.pem` -4. Ensure that all the URLs within the `nginx.conf` and the `sorry-cypress.yml` file are still valid -5. Start the containers by using `docker compose up -f sorry-cypress.yml` -6. Login into the minio dashboard (https://minio.sorry-cypress.ase.cit.tum.de) -7. Create a new user called "sorry-cypress" (Minio Dashboard -> Identity -> Users) -8. Assign this user only the `writeOnly` policy -9. Generate a random access key and a random secret key (e.g by using `openssl rand -base64 24`) -10. Create and set the just generated `ACCESSKEY` and `SECRETKEY` for this user in the `sorry-cypress.env` file (select the user -> Service Accounts --> Create Access Key) -11. Insert the `ACCESSKEY` into the .env file by replacing the `` value -12. Insert the `SECRETKEY` into the .env file by replacing the `` value -13. Within the bucket settings, set the lifecycle to delete files ("Expiry") after 14 days. -14. Under `Policies` create a new policy called `sorry-cypress` with the content of the `minio-user-policy.json` file. Now assign this policy to the `sorry-cypress` user (under `Identity` -> `Users`). This will allow the `sorry-cypress` user to upload and delete files to/from the bucket. -15. Recreate the director container, since the new keys need to be applied (e.g with the following commands `docker compose pull -f sorry-cypress.yml` & `docker compose up -f sorry-cypress.yml`) -16. Access the sorry-cypress dashboard (https://sorry-cypress.ase.cit.tum.de) and create two new projects `artemis-mysql` and `artemis-postgresql`. Set the timeout to a reasonable number (e.g 150 min) -17. Now everything should be setup diff --git a/docker/sorry-cypress/minio-user-policy.json b/docker/sorry-cypress/minio-user-policy.json deleted file mode 100644 index ed036808944a..000000000000 --- a/docker/sorry-cypress/minio-user-policy.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:DeleteObject", - "s3:GetBucketLocation", - "s3:GetObject", - "s3:ListBucket", - "s3:ListMultipartUploadParts", - "s3:PutObject", - "s3:AbortMultipartUpload" - ], - "Resource": [ - "arn:aws:s3:::sorry-cypress", - "arn:aws:s3:::sorry-cypress/*" - ] - } - ] -} diff --git a/docker/sorry-cypress/nginx.conf b/docker/sorry-cypress/nginx.conf deleted file mode 100644 index 4230e2b6961f..000000000000 --- a/docker/sorry-cypress/nginx.conf +++ /dev/null @@ -1,129 +0,0 @@ -http { - server { - listen 80; - server_name ${NGINX_MAIN_URL}; - - location / { - return 301 https://$host$request_uri; - } - } - - server { - listen 443 ssl; - server_name ${NGINX_MAIN_URL}; - - ssl_certificate /etc/certificates/fullchain.pem; - ssl_certificate_key /etc/certificates/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; - ssl_prefer_server_ciphers on; - - location / { - proxy_pass http://sry-cypress-dashboard:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - - auth_basic "Restricted"; - auth_basic_user_file /etc/nginx/.htpasswd; - } - - location /api { - proxy_pass http://sry-cypress-api:4000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - - auth_basic "Restricted"; - auth_basic_user_file /etc/nginx/.htpasswd; - } - - location /director { - rewrite ^/director(/?.*)$ $1 break; - proxy_pass http://sry-cypress-director:1234; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - } - - server { - listen 80; - server_name ${NGINX_STORAGE_URL}; - - location / { - return 301 https://$host$request_uri; - } - } - - server { - listen 443 ssl; - server_name ${NGINX_STORAGE_URL}; - - ssl_certificate /etc/certificates/fullchain.pem; - ssl_certificate_key /etc/certificates/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; - ssl_prefer_server_ciphers on; - - # This is needed to allow video file uploads, that are greater than 1MB - client_max_body_size 100M; - - location / { - proxy_pass http://sry-cypress-minio:9000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - } - - server { - listen 80; - server_name ${NGINX_MINIO_URL}; - - location / { - return 301 https://$host$request_uri; - } - } - - server { - listen 443 ssl; - server_name ${NGINX_MINIO_URL}; - - ssl_certificate /etc/certificates/fullchain.pem; - ssl_certificate_key /etc/certificates/privkey.pem; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; - ssl_prefer_server_ciphers on; - - # This is needed to allow video file uploads, that are greater than 1MB - client_max_body_size 100M; - - location / { - proxy_pass http://sry-cypress-minio:9090; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_redirect off; - } - } -} - -events { } diff --git a/docker/sorry-cypress/sorry-cypress.env b/docker/sorry-cypress/sorry-cypress.env deleted file mode 100644 index 13dda8bd8c07..000000000000 --- a/docker/sorry-cypress/sorry-cypress.env +++ /dev/null @@ -1,22 +0,0 @@ -SORRY_CYPRESS_TAG="2.5.4" - -MONGO_INITDB_ROOT_USERNAME="sorry-cypress" -MONGO_INITDB_ROOT_PASSWORD="sorry-cypress" -MONGODB_URI="mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017" -MONGODB_DATABASE="sorry-cypress" - -DASHBOARD_URL="https://sorry-cypress.ase.cit.tum.de" -ALLOWED_KEYS="" -MINIO_ACCESS_KEY="" -MINIO_SECRET_KEY="" -MINIO_ENDPOINT="storage.sorry-cypress.ase.cit.tum.de" -MINIO_URL="https://storage.sorry-cypress.ase.cit.tum.de" - -GRAPHQL_SCHEMA_URL="https://sorry-cypress.ase.cit.tum.de/api" - -MINIO_ROOT_USER="artemis" -MINIO_ROOT_PASSWORD="" - -NGINX_MAIN_URL="sorry-cypress.ase.cit.tum.de" -NGINX_STORAGE_URL="storage.sorry-cypress.ase.cit.tum.de" -NGINX_MINIO_URL="minio.sorry-cypress.ase.cit.tum.de" diff --git a/docker/sorry-cypress/sorry-cypress.yml b/docker/sorry-cypress/sorry-cypress.yml deleted file mode 100644 index baa7ef99bc32..000000000000 --- a/docker/sorry-cypress/sorry-cypress.yml +++ /dev/null @@ -1,107 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Sorry Cypress Setup -# ---------------------------------------------------------------------------------------------------------------------- - -services: - mongo: - image: docker.io/library/mongo:4.4 - container_name: sry-cypress-mongo - restart: always - volumes: - - ${MONGO_VOLUME_MOUNT:-./files/mongo}:/data/db - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - - director: - image: docker.io/agoldis/sorry-cypress-director:${SORRY_CYPRESS_TAG:-latest} - container_name: sry-cypress-director - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - EXECUTION_DRIVER: '../execution/mongo/driver' - SCREENSHOTS_DRIVER: '../screenshots/minio.driver' - MINIO_PORT: '443' - MINIO_USESSL: 'true' - MINIO_BUCKET: 'sorry-cypress' - PROBE_LOGGER: 'false' - depends_on: - - mongo - - api: - image: docker.io/agoldis/sorry-cypress-api:${SORRY_CYPRESS_TAG:-latest} - container_name: sry-cypress-api - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - APOLLO_PLAYGROUND: 'false' - depends_on: - - mongo - - dashboard: - image: docker.io/agoldis/sorry-cypress-dashboard:${SORRY_CYPRESS_TAG:-latest} - container_name: sry-cypress-dashboard - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - GRAPHQL_CLIENT_CREDENTIALS: 'include' - PORT: 8080 - CI_URL: '' - expose: - - '8080' - depends_on: - - mongo - - api - - storage: - image: docker.io/minio/minio - container_name: sry-cypress-minio - restart: always - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - volumes: - - ${MINIO_VOLUME_MOUNT:-./files/minio}:/data - command: minio server --console-address ":9090" /data - - createbuckets: - image: docker.io/minio/mc - container_name: sry-cypress-minio-bucket-creator - depends_on: - - storage - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - entrypoint: > - /bin/sh -c " - sleep 3; - /usr/bin/mc config host add myminio http://storage:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD; - /usr/bin/mc mb myminio/sorry-cypress; - /usr/bin/mc anonymous set download myminio/sorry-cypress; - exit 0; - " - - nginx: - image: docker.io/library/nginx - container_name: sry-cypress-nginx - restart: always - ports: - - 80:80 - - 443:443 - env_file: - - ${SORRY_CYPRESS_ENV_FILE:-./sorry-cypress.env} - environment: - NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx - volumes: - - type: bind - source: ${NGINX_PROXY_CONFIG_PATH:-./nginx.conf} - target: '/etc/nginx/templates/nginx.conf.template' - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../../src/test/cypress/certs/artemis-nginx+4.pem} - target: '/etc/certificates/fullchain.pem' - - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../../src/test/cypress/certs/artemis-nginx+4-key.pem} - target: '/etc/certificates/privkey.pem' - - type: bind - source: ${NGINX_PROXY_HTPASSWD:-./.htpasswd} - target: '/etc/nginx/.htpasswd' diff --git a/docker/test-server-multi-node-mysql-localci.yml b/docker/test-server-multi-node-mysql-localci.yml index 5bfc08a9c1fe..8de8a926068b 100644 --- a/docker/test-server-multi-node-mysql-localci.yml +++ b/docker/test-server-multi-node-mysql-localci.yml @@ -128,10 +128,10 @@ services: - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-multi-node-postgresql-localci.yml b/docker/test-server-multi-node-postgresql-localci.yml index 8e0dc02bb592..124a1936aab2 100644 --- a/docker/test-server-multi-node-postgresql-localci.yml +++ b/docker/test-server-multi-node-postgresql-localci.yml @@ -100,10 +100,10 @@ services: - ./nginx/artemis-upstream-multi-node.conf:/etc/nginx/includes/artemis-upstream.conf:ro - ./nginx/artemis-ssh-upstream-multi-node.conf:/etc/nginx/includes/artemis-ssh-upstream.conf:ro - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-mysql-localci.yml b/docker/test-server-mysql-localci.yml index 29b1881c6bb7..91c7c353bab0 100644 --- a/docker/test-server-mysql-localci.yml +++ b/docker/test-server-mysql-localci.yml @@ -47,10 +47,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-mysql.yml b/docker/test-server-mysql.yml index bcd82539e146..5f8c10981cc5 100644 --- a/docker/test-server-mysql.yml +++ b/docker/test-server-mysql.yml @@ -44,10 +44,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-postgresql-localci.yml b/docker/test-server-postgresql-localci.yml index 600cd29bf261..d93058788912 100644 --- a/docker/test-server-postgresql-localci.yml +++ b/docker/test-server-postgresql-localci.yml @@ -46,10 +46,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: diff --git a/docker/test-server-postgresql.yml b/docker/test-server-postgresql.yml index d09cb52dce53..0e3c37e95627 100644 --- a/docker/test-server-postgresql.yml +++ b/docker/test-server-postgresql.yml @@ -43,10 +43,10 @@ services: restart: always volumes: - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/cypress/certs/artemis-nginx+4.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-../src/test/playwright/certs/artemis-nginx+4.pem} target: "/certs/fullchain.pem" - type: bind - source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/cypress/certs/artemis-nginx+4-key.pem} + source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-../src/test/playwright/certs/artemis-nginx+4-key.pem} target: "/certs/priv_key.pem" networks: 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/admin/setup/customization.rst b/docs/admin/setup/customization.rst index e86adbeca56c..2094cc8b5069 100644 --- a/docs/admin/setup/customization.rst +++ b/docs/admin/setup/customization.rst @@ -7,4 +7,5 @@ instead of the TUM defaults: * The logo next to the “Artemis” heading on the navbar → ``${artemisRunDirectory}/public/images/logo.png`` * The favicon → ``${artemisRunDirectory}/logo/favicon.svg`` * The contact email address in the ``application-{dev,prod}.yml`` configuration file under the key ``info.contact`` +* The operator's name (e.g. university) and the operator's contact information (administrator email address and name) can be specified in the ``application-{dev,prod}.yml`` configuration file under the keys ``info.operatorName``, ``info.contact`` and ``info.operatorAdminName``. These values are also displayed on the ``/about`` page. The operator's name is required, while the administrator's name is optional. Artemis also uses this information for the :ref:`telemetry` service. * The maximal number of plagiarism results stored per plagiarism checks in the ``application-{dev,prod}.yml`` configuration file under the key ``artemis.plagiarism-checks.plagiarism-results-limit`` diff --git a/docs/admin/telemetry.rst b/docs/admin/telemetry.rst new file mode 100644 index 000000000000..95c3b8168f3a --- /dev/null +++ b/docs/admin/telemetry.rst @@ -0,0 +1,37 @@ +.. _telemetry: + +Telemetry +========= + +To help to improve Artemis, we collect some data when the application starts. +This feature can be disabled by setting `telemetry.enabled` in the `application-prod.yml` to `false`. +When this is set to false, no data is sent to the Artemis maintainer team. +By setting `telemetry.sendAdminDetails` to false, personal information of the instance's admin (i.e. contact email and name) is excluded from the telemetry data. +This includes the contact email and the administrator's name. + +Artemis collects the following data at the startup of an instance: + +* The used Artemis version +* The contact email address of the admin, which is set in `info.contact` +* The name of the admin, set in `info.operatorAdminName` (optional) +* The server's URL +* The operator's name +* The used profiles (e.g. Gitlab, Jenkins, LocalVC, Aeolus, ...) + +Example configuration in `application-prod.yml`: + +.. code-block:: yaml + + artemis: + telemetry: + enabled: true + sendAdminDetails: false + destination: telemetry.artemis.cit.tum.de + + info: + contact: contactMailAddress@cit.tum.de + operatorName: Technical University of Munich + operatorAdminName: Stephan Krusche + +We collect this data to enhance Artemis by understanding how it is used, ensuring compatibility with different configurations, and providing better support to our users. +Collecting admin contact information allows us to communicate important updates or address critical issues directly. 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/index.rst b/docs/index.rst index 64cd387ed5e1..32c130434e4f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -77,6 +77,7 @@ All these exercises are supposed to be run either live in the lecture with insta admin/database admin/knownIssues admin/benchmarking-tool + admin/telemetry .. toctree:: 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/docs/user/exercises/programming-exercise-features.inc b/docs/user/exercises/programming-exercise-features.inc index e73c16088efe..3675a9e4c2b0 100644 --- a/docs/user/exercises/programming-exercise-features.inc +++ b/docs/user/exercises/programming-exercise-features.inc @@ -33,6 +33,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------+---------+ | OCaml | yes | no | +----------------------+----------+---------+ + | Rust | yes | yes | + +----------------------+----------+---------+ - Not all ``templates`` support the same feature set and supported features can also change depending on the continuous integration system setup. Depending on the feature set, some options might not be available during the creation of the programming exercise. @@ -63,6 +65,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ | OCaml | no | no | no | no | n/a | yes | no | L: yes, J: no | +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ + | Rust | no | no | no | no | n/a | no | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+ - *Sequential Test Runs*: ``Artemis`` can generate a build plan which first executes structural and then behavioral tests. This feature can help students to better concentrate on the immediate challenge at hand. - *Static Code Analysis*: ``Artemis`` can generate a build plan which additionally executes static code analysis tools. diff --git a/eslint.config.js b/eslint.config.js index 14d17e283ee0..622299e984e1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -27,7 +27,6 @@ module.exports = [ 'repos-download/', 'src/main/generated/', 'src/main/resources/', - 'src/test/cypress/', // until we delete those files, we ignore them 'target/', 'uploads/', ], @@ -37,19 +36,13 @@ module.exports = [ languageOptions: { parser: typescriptParser, parserOptions: { - project: [ - './tsconfig.json', - './tsconfig.app.json', - './tsconfig.spec.json', - 'src/test/cypress/tsconfig.json', - 'src/test/playwright/tsconfig.json', - ], + project: ['./tsconfig.json', './tsconfig.app.json', './tsconfig.spec.json', 'src/test/playwright/tsconfig.json'], }, }, plugins: { '@typescript-eslint': tsPlugin, '@angular-eslint': angularPlugin, - 'prettier': prettierPlugin, + prettier: prettierPlugin, }, rules: { ...prettierPlugin.configs.recommended.rules, @@ -102,7 +95,7 @@ module.exports = [ }, plugins: { '@angular-eslint': angularPlugin, - 'prettier': prettierPlugin, + prettier: prettierPlugin, }, rules: { // ...angularPlugin.configs['template/recommended'].rules, @@ -126,7 +119,7 @@ module.exports = [ { files: ['src/test/javascript/**'], plugins: { - 'jest': jestPlugin, + jest: jestPlugin, 'jest-extended': jestExtendedPlugin, }, rules: { diff --git a/gradle.properties b/gradle.properties index db7d546da641..80dbe5279b3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,29 +6,29 @@ node_version=20.14.0 npm_version=10.7.0 # Dependency versions -jhipster_dependencies_version=8.6.0 -spring_boot_version=3.3.2 -spring_security_version=6.3.1 -# TODO: before we upgrade to 6.5.x, we need to make sure that there are no performance issues with empty sets or lists -hibernate_version=6.4.9.Final +jhipster_dependencies_version=8.7.0 +spring_boot_version=3.3.3 +spring_security_version=6.3.3 +# TODO: upgrading to 6.6.0 currently leads to issues due to internal changes in Hibernate and potentially wrong use in Artemis server code +hibernate_version=6.4.10.Final # TODO: can we update to 5.x? opensaml_version=4.3.2 jwt_version=0.12.6 jaxb_runtime_version=4.0.5 -# TODO: we cannot update to 5.5.0 because we currently use the CP Subsystem for fenced locks, however CP Subsystem is only available to Enterprise customers -hazelcast_version=5.4.0 +hazelcast_version=5.5.0 junit_version=5.10.2 -mockito_version=5.12.0 +mockito_version=5.13.0 fasterxml_version=2.17.2 jgit_version=6.10.0.202406032230-r sshd_version=2.13.2 -checkstyle_version=10.17.0 +checkstyle_version=10.18.1 jplag_version=5.1.0 slf4j_version=2.0.16 -sentry_version=7.13.0 +sentry_version=7.14.0 liquibase_version=4.29.1 docker_java_version=3.4.0 -logback_version=1.5.6 +logback_version=1.5.7 +java_parser_version=3.26.1 # gradle plugin version gradle_node_plugin_version=7.0.2 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4..a4b76b9530d6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 09523c0e5490..9355b4155759 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426907..f5feea6d6b11 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30dbdeee..9d21a21834d5 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/package-lock.json b/package-lock.json index 499b23182e65..f05b86d4cc8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,27 +1,27 @@ { "name": "artemis", - "version": "7.5.1", + "version": "7.5.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "artemis", - "version": "7.5.1", + "version": "7.5.2", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@angular/animations": "18.1.4", - "@angular/cdk": "18.1.4", - "@angular/common": "18.1.4", - "@angular/compiler": "18.1.4", - "@angular/core": "18.1.4", - "@angular/forms": "18.1.4", - "@angular/localize": "18.1.4", - "@angular/material": "18.1.4", - "@angular/platform-browser": "18.1.4", - "@angular/platform-browser-dynamic": "18.1.4", - "@angular/router": "18.1.4", - "@angular/service-worker": "18.1.4", + "@angular/animations": "18.2.3", + "@angular/cdk": "18.2.3", + "@angular/common": "18.2.3", + "@angular/compiler": "18.2.3", + "@angular/core": "18.2.3", + "@angular/forms": "18.2.3", + "@angular/localize": "18.2.3", + "@angular/material": "18.2.3", + "@angular/platform-browser": "18.2.3", + "@angular/platform-browser-dynamic": "18.2.3", + "@angular/router": "18.2.3", + "@angular/service-worker": "18.2.3", "@ctrl/ngx-emoji-mart": "9.2.0", "@danielmoncada/angular-datetime-picker": "18.1.0", "@fingerprintjs/fingerprintjs": "4.4.3", @@ -31,23 +31,21 @@ "@fortawesome/free-regular-svg-icons": "6.6.0", "@fortawesome/free-solid-svg-icons": "6.6.0", "@ls1intum/apollon": "3.3.14", - "@ng-bootstrap/ng-bootstrap": "17.0.0", + "@ng-bootstrap/ng-bootstrap": "17.0.1", "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "8.0.0", - "@sentry/angular": "8.25.0", + "@sentry/angular": "8.28.0", "@swimlane/ngx-charts": "20.5.0", "@swimlane/ngx-graph": "8.4.0", "@vscode/codicons": "0.0.36", - "ace-builds": "1.35.4", "bootstrap": "5.3.3", - "brace": "0.11.1", "compare-versions": "6.1.1", - "core-js": "3.38.0", + "core-js": "3.38.1", "crypto-js": "4.2.0", - "dayjs": "1.11.12", - "diff-match-patch-typescript": "1.0.8", + "dayjs": "1.11.13", + "diff-match-patch-typescript": "1.1.0", "dompurify": "3.1.6", - "export-to-csv": "1.3.0", + "export-to-csv": "1.4.0", "fast-json-patch": "3.1.1", "franc-min": "6.2.0", "html-diff-ts": "1.4.2", @@ -57,21 +55,22 @@ "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", - "posthog-js": "1.155.0", + "pdfjs-dist": "4.6.82", + "posthog-js": "1.160.3", "rxjs": "7.8.1", "showdown": "2.1.0", "showdown-highlight": "3.1.0", "showdown-katex": "0.6.0", - "simple-statistics": "7.8.3", + "simple-statistics": "7.8.5", "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,37 +78,37 @@ }, "devDependencies": { "@angular-builders/jest": "18.0.0", - "@angular-devkit/build-angular": "18.1.4", - "@angular-eslint/builder": "18.2.0", - "@angular-eslint/eslint-plugin": "18.2.0", - "@angular-eslint/eslint-plugin-template": "18.2.0", - "@angular-eslint/schematics": "18.2.0", - "@angular-eslint/template-parser": "18.2.0", - "@angular/cli": "18.1.4", - "@angular/compiler-cli": "18.1.4", - "@angular/language-service": "18.1.4", - "@sentry/types": "8.25.0", + "@angular-devkit/build-angular": "18.2.3", + "@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.3", + "@angular/compiler-cli": "18.2.3", + "@angular/language-service": "18.2.3", + "@sentry/types": "8.28.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.2.0", + "@types/node": "22.5.4", "@types/papaparse": "5.3.14", "@types/showdown": "2.0.6", "@types/smoothscroll-polyfill": "0.3.4", "@types/sockjs-client": "1.5.4", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.1.0", - "@typescript-eslint/parser": "8.1.0", - "eslint": "9.9.0", + "@typescript-eslint/eslint-plugin": "8.4.0", + "@typescript-eslint/parser": "8.4.0", + "eslint": "9.9.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", - "eslint-plugin-jest": "28.8.0", + "eslint-plugin-jest": "28.8.3", "eslint-plugin-jest-extended": "2.4.0", "eslint-plugin-prettier": "5.2.1", "folder-hash": "4.0.4", - "husky": "9.1.4", + "husky": "9.1.5", "jest": "29.7.0", "jest-canvas-mock": "2.5.2", "jest-date-mock": "1.0.10", @@ -117,11 +116,12 @@ "jest-fail-on-console": "3.3.0", "jest-junit": "16.0.0", "jest-preset-angular": "14.2.2", - "lint-staged": "15.2.8", - "ng-mocks": "14.13.0", + "lint-staged": "15.2.10", + "ng-mocks": "14.13.1", "prettier": "3.3.3", - "sass": "1.77.8", - "ts-jest": "29.2.4", + "rimraf": "6.0.1", + "sass": "1.78.0", + "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.1801.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1801.4.tgz", - "integrity": "sha512-Ch1ZwRh1N/vcCKHm4ErLcgZly3tlwdLUDGBaAIlhE3YFGq543Swv6a5IcDw0veD6iGFceJAmbrp+z5hmzI8p5A==", + "version": "0.1802.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.3.tgz", + "integrity": "sha512-WQ2AmkUKy1bqrDlNfozW8+VT2Tv/Fdmu4GIXps3ytZANyAKiIvTzmmql2cRCXXraa9FNMjLWNvz+qolDxWVdYQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.1.4", + "@angular-devkit/core": "18.2.3", "rxjs": "7.8.1" }, "engines": { @@ -227,48 +227,48 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.1.4.tgz", - "integrity": "sha512-CCoPT2fFw1DD3j9eSP3GKbp9KfvxQQfY6kV2aec0pqL/c6byz4/ku+rsV4lwE0N/dcaglwhttq4Xf+u+pkEpiw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.3.tgz", + "integrity": "sha512-uUQba0SIskKORHcPayt7LpqPRKD//48EW92SgGHEArn2KklM+FSYBOA9OtrJeZ/UAcoJpdLDtvyY4+S7oFzomg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1801.4", - "@angular-devkit/build-webpack": "0.1801.4", - "@angular-devkit/core": "18.1.4", - "@angular/build": "18.1.4", - "@babel/core": "7.24.7", - "@babel/generator": "7.24.7", + "@angular-devkit/architect": "0.1802.3", + "@angular-devkit/build-webpack": "0.1802.3", + "@angular-devkit/core": "18.2.3", + "@angular/build": "18.2.3", + "@babel/core": "7.25.2", + "@babel/generator": "7.25.0", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", - "@babel/plugin-transform-async-generator-functions": "7.24.7", + "@babel/plugin-transform-async-generator-functions": "7.25.0", "@babel/plugin-transform-async-to-generator": "7.24.7", "@babel/plugin-transform-runtime": "7.24.7", - "@babel/preset-env": "7.24.7", - "@babel/runtime": "7.24.7", - "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "18.1.4", + "@babel/preset-env": "7.25.3", + "@babel/runtime": "7.25.0", + "@discoveryjs/json-ext": "0.6.1", + "@ngtools/webpack": "18.2.3", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.19", + "autoprefixer": "10.4.20", "babel-loader": "9.1.3", "browserslist": "^4.21.5", "copy-webpack-plugin": "12.0.2", "critters": "0.0.24", "css-loader": "7.1.2", - "esbuild-wasm": "0.21.5", + "esbuild-wasm": "0.23.0", "fast-glob": "3.3.2", "http-proxy-middleware": "3.0.0", "https-proxy-agent": "7.0.5", - "istanbul-lib-instrument": "6.0.2", + "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", "less": "4.2.0", "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.3.1", - "magic-string": "0.30.10", + "magic-string": "0.30.11", "mini-css-extract-plugin": "2.9.0", "mrmime": "2.0.0", "open": "10.1.0", @@ -276,25 +276,24 @@ "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", "piscina": "4.6.1", - "postcss": "8.4.38", + "postcss": "8.4.41", "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", "sass": "1.77.6", - "sass-loader": "14.2.1", - "semver": "7.6.2", + "sass-loader": "16.0.0", + "semver": "7.6.3", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.29.2", + "terser": "5.31.6", "tree-kill": "1.2.2", "tslib": "2.6.3", - "undici": "6.19.2", - "vite": "5.3.2", + "vite": "5.4.0", "watchpack": "2.4.1", - "webpack": "5.92.1", - "webpack-dev-middleware": "7.2.1", + "webpack": "5.94.0", + "webpack-dev-middleware": "7.4.2", "webpack-dev-server": "5.0.4", - "webpack-merge": "5.10.0", + "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" }, "engines": { @@ -303,7 +302,7 @@ "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.21.5" + "esbuild": "0.23.0" }, "peerDependencies": { "@angular/compiler-cli": "^18.0.0", @@ -374,14 +373,21 @@ "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.1801.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1801.4.tgz", - "integrity": "sha512-Srhs/PcnuUaMiO9FLQLi1QiGZqtnG5NTpkufjJuWxolSLGNRmb/h/ZeCYgRnxeH/4jd8GCD31RD78qy+pviiLQ==", + "version": "0.1802.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.3.tgz", + "integrity": "sha512-/Nixv9uAg6v/OPoZa0PB0zi+iezzBkgLrnrJnestny5B536l9WRtsw97RjeQDu+x2BClQsxNe8NL2A7EvjVD6w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1801.4", + "@angular-devkit/architect": "0.1802.3", "rxjs": "7.8.1" }, "engines": { @@ -395,13 +401,13 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.1.4.tgz", - "integrity": "sha512-lKBsvbqW2QFL8terzNuSDSmKBo8//QNRO4qU5mVJ1fFf4xBJanXKoiAMuADhx+/owVIptnYT59IZ8jUAna+Srg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.3.tgz", + "integrity": "sha512-vbFs+ofNK9OWeMIcFarFjegXVklhtSdLTEFKZ9trDVr8alTJdjI9AiYa6OOUTDAyq0hqYxV26xlCisWAPe7s5w==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "8.16.0", + "ajv": "8.17.1", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.2", @@ -423,15 +429,15 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.1.4.tgz", - "integrity": "sha512-0ekArCeYqJngCKWZ9I+RtNObP/33zGkzWdJOmCB6nj9/ZevALZ6F4RDkHp0TqDYhOt+A2muI29ZK/cILmKA+sA==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.3.tgz", + "integrity": "sha512-N3tRAzBW2yWQhebvc1Ha18XTMSXOQTfr8HNjx7Fasx0Fg1tNyGR612MJNZw6je/PqyItKeUHOhztvFMfCQjRyg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.1.4", + "@angular-devkit/core": "18.2.3", "jsonc-parser": "3.3.1", - "magic-string": "0.30.10", + "magic-string": "0.30.11", "ora": "5.4.1", "rxjs": "7.8.1" }, @@ -442,36 +448,32 @@ } }, "node_modules/@angular-eslint/builder": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-18.2.0.tgz", - "integrity": "sha512-2NsrYqvVVha2XUUXm1T0XshW0d1TzYU6rXNCTut1t8qS/uinbxNiszKzJN1TuUsXFwpZfITRnEY3cjaqJDlsdA==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-18.3.0.tgz", + "integrity": "sha512-httEQyqyBw3+0CRtAa7muFxHrauRfkEfk/jmrh5fn2Eiu+I53hAqFPgrwVi1V6AP/kj2zbAiWhd5xM3pMJdoRQ==", "dev": true, "license": "MIT", - "dependencies": { - "@nx/devkit": "^19.0.6", - "nx": "^19.0.6" - }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": "*" } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.2.0.tgz", - "integrity": "sha512-p/YvlvDJscSAbNOOAbT/BRdscEfWpQunUK+KuWM6/PXL07tTVae5dmp8B8A5am7Cxvp+ZVLVLZG4LFYB1TX1cw==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.3.0.tgz", + "integrity": "sha512-v/59FxUKnMzymVce99gV43huxoqXWMb85aKvzlNvLN+ScDu6ZE4YMiTQNpfapVL2lkxhs0uwB3jH17EYd5TcsA==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-18.2.0.tgz", - "integrity": "sha512-vJ7pstQPqCqkvMrEsjjocvHdPBl/frs0+fqkckog2Sq0QisBEjUPkbImvId6dw7JzxSDSvttdAklakF97CE4VA==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-18.3.0.tgz", + "integrity": "sha512-Vl7gfPMXxvtHTjYdlzR161aj5xrqW6T57wd8ToQ7Gqzm0qHGfY6kE4SQobUa2LCYckTNSlv+zXe48C4ah/dSjw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.2.0", - "@angular-eslint/utils": "18.2.0" + "@angular-eslint/bundled-angular-compiler": "18.3.0", + "@angular-eslint/utils": "18.3.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -480,14 +482,14 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-18.2.0.tgz", - "integrity": "sha512-YHh+AUY9ubLAdmIRXH8vSpv+8EQkGjdX3B9xdj/grnrVzgzu+5W86F/spGp2tEny9l85R3JZNqjaMpW/vwibfw==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-18.3.0.tgz", + "integrity": "sha512-ddR/qwYbUeq9IpyVKrPbfZyRBTy6V8uc5I0JcBKttQ4CZ4joXhqsVgWFsI+JAMi8E66uNj1VC7NuKCOjDINv2Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.2.0", - "@angular-eslint/utils": "18.2.0", + "@angular-eslint/bundled-angular-compiler": "18.3.0", + "@angular-eslint/utils": "18.3.0", "aria-query": "5.3.0", "axobject-query": "4.1.0" }, @@ -498,17 +500,15 @@ } }, "node_modules/@angular-eslint/schematics": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-18.2.0.tgz", - "integrity": "sha512-6niXUrwyAmhuFcsRx88cOOVrko0EmsUGjLZ4yso3op/I9ZgI4SfuLCtl9gzJIR4TLBYc4zAuC/TE/rsLP+WCtw==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-18.3.0.tgz", + "integrity": "sha512-rQ4DEWwf3f5n096GAK6JvXD0SRzRJ52WRaIyKg8MMkk6qvUDfZI8seOkcbjDtZoIe6Ds7DfqSfJgNVte75qvPQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/eslint-plugin": "18.2.0", - "@angular-eslint/eslint-plugin-template": "18.2.0", - "@nx/devkit": "^19.0.6", - "ignore": "5.3.1", - "nx": "^19.0.6", + "@angular-eslint/eslint-plugin": "18.3.0", + "@angular-eslint/eslint-plugin-template": "18.3.0", + "ignore": "5.3.2", "semver": "7.6.3", "strip-json-comments": "3.1.1" }, @@ -518,13 +518,13 @@ } }, "node_modules/@angular-eslint/template-parser": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-18.2.0.tgz", - "integrity": "sha512-1jKH2fL8ir1ajcgu/N0xIWVtlpJQmbJBRRe1+WbBoomykcu1KnLwCSue/LuUDQOf3CTmMHxQE0f+58VpafYoyA==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-18.3.0.tgz", + "integrity": "sha512-1mUquqcnugI4qsoxcYZKZ6WMi6RPelDcJZg2YqGyuaIuhWmi3ZqJZLErSSpjP60+TbYZu7wM8Kchqa1bwJtEaQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.2.0", + "@angular-eslint/bundled-angular-compiler": "18.3.0", "eslint-scope": "^8.0.2" }, "peerDependencies": { @@ -533,13 +533,13 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-18.2.0.tgz", - "integrity": "sha512-g+b0L4RCZaKYPz4bGRRifo7g5guVJi2kUWymlDYmCkq3NhZng1HQQbNpVF1n5o034zT5lnaC5HENwaKIZ1Y37Q==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-18.3.0.tgz", + "integrity": "sha512-sCrkHkpxBJZLuCikdboZoawCfc2UgbJv+T14tu2uQCv+Vwzeadnu04vkeY2vTkA8GeBdBij/G9/N/nvwmwVw3g==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.2.0" + "@angular-eslint/bundled-angular-compiler": "18.3.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -548,9 +548,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.1.4.tgz", - "integrity": "sha512-m0yusB7BI3wrotx9F9rf7YUD5bvhF+lT2fLNF1QCzCU819rtLnDoj0b4/z+D0i5qe7gQjtAJ42e/Hv7eGuq0VQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.3.tgz", + "integrity": "sha512-rIATopHr83lYR0X05buHeHssq9CGw0I0YPIQcpUTGnlqIpJcQVCf7jCFn4KGZrE9V55hFY3MD4S28njlwCToQQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -559,42 +559,40 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.1.4" + "@angular/core": "18.2.3" } }, "node_modules/@angular/build": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.1.4.tgz", - "integrity": "sha512-jkqccHpGhxUOe0zIHpA1nPdeuPUxnBK7Wvazc2rA+ccI30BPrROkEDbrHP8yD8JeviUCFwwLE+hM+rRg+NneVw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.3.tgz", + "integrity": "sha512-USrD2Zvcb1te2dnqhH7JZ5XeJDg/t7fjUHR4f93vvMrnrncwCjLoHbHpz01HCHfcIVRgsYUdAmAi1iG7vpak7w==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1801.4", - "@babel/core": "7.24.7", + "@angular-devkit/architect": "0.1802.3", + "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", "@babel/plugin-syntax-import-attributes": "7.24.7", - "@inquirer/confirm": "3.1.11", + "@inquirer/confirm": "3.1.22", "@vitejs/plugin-basic-ssl": "1.1.0", - "ansi-colors": "4.1.3", "browserslist": "^4.23.0", "critters": "0.0.24", - "esbuild": "0.21.5", + "esbuild": "0.23.0", "fast-glob": "3.3.2", "https-proxy-agent": "7.0.5", - "lmdb": "3.0.12", - "magic-string": "0.30.10", + "listr2": "8.2.4", + "lmdb": "3.0.13", + "magic-string": "0.30.11", "mrmime": "2.0.0", - "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", "piscina": "4.6.1", - "rollup": "4.18.0", + "rollup": "4.20.0", "sass": "1.77.6", - "semver": "7.6.2", - "undici": "6.19.2", - "vite": "5.3.2", + "semver": "7.6.3", + "vite": "5.4.0", "watchpack": "2.4.1" }, "engines": { @@ -652,9 +650,9 @@ } }, "node_modules/@angular/cdk": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.1.4.tgz", - "integrity": "sha512-xFOg2wT2iLyJXqgeNRK1uF4Lxn0B1wzBjaEQoOwFm1EHOdu5D4mNOTwfuB3DkH4KWM+mI3Qtxd7vOhOXNwB3Dg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.3.tgz", + "integrity": "sha512-lUcpYTxPZuntJ1FK7V2ugapCGMIhT6TUDjIGgXfS9AxGSSKgwr8HNs6Ze9pcjYC44UhP40sYAZuiaFwmE60A2A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -669,27 +667,27 @@ } }, "node_modules/@angular/cli": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.1.4.tgz", - "integrity": "sha512-ppX4iilA6k+sKD6iRMRYnt2bH9Jpik+hJlndRBCjWo2EmEUQ04CBRKYONh8BLbnmwBxPG+/osUpcFrbkPCjQUw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.3.tgz", + "integrity": "sha512-40258vuliH6+p8QSByZe5EcIXSj0iR3PNF6yuusClR/ByToHOnmuPw7WC+AYr0ooozmqlim/EjQe4/037OUB3w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1801.4", - "@angular-devkit/core": "18.1.4", - "@angular-devkit/schematics": "18.1.4", - "@inquirer/prompts": "5.0.7", - "@listr2/prompt-adapter-inquirer": "2.0.13", - "@schematics/angular": "18.1.4", + "@angular-devkit/architect": "0.1802.3", + "@angular-devkit/core": "18.2.3", + "@angular-devkit/schematics": "18.2.3", + "@inquirer/prompts": "5.3.8", + "@listr2/prompt-adapter-inquirer": "2.0.15", + "@schematics/angular": "18.2.3", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", - "listr2": "8.2.3", - "npm-package-arg": "11.0.2", - "npm-pick-manifest": "9.0.1", + "listr2": "8.2.4", + "npm-package-arg": "11.0.3", + "npm-pick-manifest": "9.1.0", "pacote": "18.0.6", "resolve": "1.22.8", - "semver": "7.6.2", + "semver": "7.6.3", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, @@ -703,9 +701,9 @@ } }, "node_modules/@angular/common": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.1.4.tgz", - "integrity": "sha512-No4lCrL80WlAGg0DAyuPW+jsfA6EIQ06CFrRgt3R6YFrKbIuU0NKUt+D8IB7UNgTLNYXmurxapNf8jef8rq1wg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz", + "integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -714,14 +712,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.1.4", + "@angular/core": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.1.4.tgz", - "integrity": "sha512-Xdvm9trEmrWZaxCk3a7bt5kN/jdXBPukVsibFpu5lKl9ZL7j2sn4JUd7j/dVNRUIVsPahQMATAOgl8xdUJzh4Q==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.3.tgz", + "integrity": "sha512-Il3ljs0j1GaYoqYFdShjUP1ryck5xTOaA8uQuRgqwU0FOwEDfugSAM3Qf7nJx/sgxTM0Lm/Nrdv2u6i1gZWeuQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -730,7 +728,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.1.4" + "@angular/core": "18.2.3" }, "peerDependenciesMeta": { "@angular/core": { @@ -739,12 +737,12 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.1.4.tgz", - "integrity": "sha512-wOOLzxPLsDYsD+f6Bqr31ol8K7I4cm4k5uuaQl+wkLBpX9AD1rMi/7CPJrXAWBdgOW67uPzAdLBsK+axKfg91w==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.3.tgz", + "integrity": "sha512-BcmqYKnkcJTkGjuPztClZNQve7tdI290J5F3iZBx6c7/vaw8EU8EGZtpWYZpgiVn5S6jhcKyc1dLF9ggO9vftg==", "license": "MIT", "dependencies": { - "@babel/core": "7.24.9", + "@babel/core": "7.25.2", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", @@ -762,65 +760,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.1.4", + "@angular/compiler": "18.2.3", "typescript": ">=5.4 <5.6" } }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@angular/core": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.1.4.tgz", - "integrity": "sha512-+N3oWYFubT3GdCkBfD/CmH4DGjr/fGFQZChWbph2ZuPpK7JYNgfyvXS4SjLtdL4WTjjBevBTgR70GyLH/5EbKA==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz", + "integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -830,13 +777,13 @@ }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.14.0" + "zone.js": "~0.14.10" } }, "node_modules/@angular/forms": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.1.4.tgz", - "integrity": "sha512-PYaQ7/2toAwgJWIznVWgJAd3l8mjAreilGOVIMbBIaotL/EHRQjhlikitJEFDGXeVUarY/rm3IlLWBYnLyliyg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.3.tgz", + "integrity": "sha512-+OBaAH0e8hue9eyLnbgpxg1/X9fps6bwXECfJ0nL5BDPU5itZ428YJbEnj5bTx0hEbqfTRiV4LgexdI+D9eOpw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -845,16 +792,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.1.4", - "@angular/core": "18.1.4", - "@angular/platform-browser": "18.1.4", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.1.4.tgz", - "integrity": "sha512-nvO6lG/vMWRXUj+nY2HyvhOSlSYqBx1ER2rk5MezzYo3I0qgiNuV9YfgTyXLWywDAvc9jcTrIyi4nxBm6a0xvQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.2.3.tgz", + "integrity": "sha512-bTZ1O7s0uJqKdd9ImCleRS9Wg6yVy2ZXchnS5ap2gYJx51MJgwOM/fL6is0OsovtZG/UJaKK5FeEqUUxNqZJVA==", "dev": true, "license": "MIT", "engines": { @@ -862,12 +809,12 @@ } }, "node_modules/@angular/localize": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.1.4.tgz", - "integrity": "sha512-GqrVYc3PwYFd9nDbOV0nL2ZhsLfb1LOZLnpsGqTBJT6zfmbqTUnwQWjHmj6SoGzTQl9r9OAvawaMrICzB6Rrfw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.3.tgz", + "integrity": "sha512-ZTliuRfH/hGwQTmFb1FwKOyMUks2ATuFVFzKnxbsxoo+XgTg+e12FcUfPEfdtPAteZ9gSuc/9hP8sM0RzW0LPg==", "license": "MIT", "dependencies": { - "@babel/core": "7.24.9", + "@babel/core": "7.25.2", "@types/babel__core": "7.20.5", "fast-glob": "3.3.2", "yargs": "^17.2.1" @@ -881,72 +828,21 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.1.4", - "@angular/compiler-cli": "18.1.4" - } - }, - "node_modules/@angular/localize/node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "@angular/compiler": "18.2.3", + "@angular/compiler-cli": "18.2.3" } }, - "node_modules/@angular/localize/node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@angular/localize/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, "node_modules/@angular/material": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.1.4.tgz", - "integrity": "sha512-xmKIVOKZA8yFXrw4PsBvShFSYFQCnuVNGGuJlc8S8xvURh/f9P6hAo1Ua9pSBkOKz2W2dHE+726zOqL+o4kNxg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.3.tgz", + "integrity": "sha512-JFfvXaMHMhskncaxxus4sDvie9VYdMkfYgfinkLXpZlPFyn1IzjDw0c1BcrcsuD7UxQVZ/v5tucCgq1FQfGRpA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^18.0.0 || ^19.0.0", - "@angular/cdk": "18.1.4", + "@angular/cdk": "18.2.3", "@angular/common": "^18.0.0 || ^19.0.0", "@angular/core": "^18.0.0 || ^19.0.0", "@angular/forms": "^18.0.0 || ^19.0.0", @@ -955,9 +851,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.1.4.tgz", - "integrity": "sha512-zGx33St0JVYT8EZOaf0s8Twr0RgfU2cqEAc9Wwx9HVJ0pF5y4VnftK3pewwiHWDHkPfiJy0jBKbtrkVUSbgZfg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz", + "integrity": "sha512-M2ob4zN7tAcL2mx7U6KnZNqNFPFl9MlPBE0FrjQjIzAjU0wSYPIJXmaPu9aMUp9niyo+He5iX98I+URi2Yc99g==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -966,9 +862,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.1.4", - "@angular/common": "18.1.4", - "@angular/core": "18.1.4" + "@angular/animations": "18.2.3", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3" }, "peerDependenciesMeta": { "@angular/animations": { @@ -977,9 +873,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.1.4.tgz", - "integrity": "sha512-ZQQcKXGIriOzILTZxIbmDpGnwuiwfJ0xh2EmmnfC0zh/NB+li6whgplOLEciaHgsUKtDn7kNZFn2vKrx+B/cDQ==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.3.tgz", + "integrity": "sha512-nWi9ZxN4KpbJkttIckFO1PCoW0+gb/18xFO+JWyLBAtcbsudj/Mv0P/fdOaSfQdLkPhZfORr3ZcfiTkhmuGyEg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -988,16 +884,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.1.4", - "@angular/compiler": "18.1.4", - "@angular/core": "18.1.4", - "@angular/platform-browser": "18.1.4" + "@angular/common": "18.2.3", + "@angular/compiler": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3" } }, "node_modules/@angular/router": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.1.4.tgz", - "integrity": "sha512-982+bnO3uGFYjRFcQDoKmnWvUcZUvFxEpX/I2Yu+WmPJrY7fPJ693mBaWgwVFa0xIBNfjvJjNXdikGBz5UrMsw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.3.tgz", + "integrity": "sha512-fvD9eSDIiIbeYoUokoWkXzu7/ZaxlzKPUHFqX1JuKuH5ciQDeT/d7lp4mj31Bxammhohzi3+z12THJYsCkj/iQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1006,16 +902,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.1.4", - "@angular/core": "18.1.4", - "@angular/platform-browser": "18.1.4", + "@angular/common": "18.2.3", + "@angular/core": "18.2.3", + "@angular/platform-browser": "18.2.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.1.4.tgz", - "integrity": "sha512-7knx0I82ud6IWu2NOEtmtikApm/Ix8YEbXk2/J8YiN7ozGSTjTK5X8cpvFUzfG4MbARH6sxVAPffNJduzW4ZAw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.3.tgz", + "integrity": "sha512-KplaBYhhwsM3gPeOImfDGhAknN+BIcZJkHl8YRnhoUEFHsTZ8LTV02C4LWQL3YTu3pK+uj/lPMKi1CA37cXQ8g==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1027,8 +923,8 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.1.4", - "@angular/core": "18.1.4" + "@angular/common": "18.2.3", + "@angular/core": "18.2.3" } }, "node_modules/@babel/code-frame": { @@ -1045,30 +941,30 @@ } }, "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" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1090,12 +986,12 @@ "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -1147,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": { @@ -1158,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": { @@ -1203,19 +1099,6 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", @@ -1402,13 +1285,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "license": "MIT", "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -1430,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.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -1461,6 +1344,22 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", @@ -1607,13 +1506,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz", + "integrity": "sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1790,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" @@ -1839,16 +1738,16 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1908,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" @@ -1943,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": { @@ -2029,6 +1928,23 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -2407,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" @@ -2644,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" @@ -2661,20 +2577,21 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -2695,29 +2612,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -2726,7 +2644,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-object-super": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", @@ -2737,7 +2655,7 @@ "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", "@babel/plugin-transform-unicode-escapes": "^7.24.7", "@babel/plugin-transform-unicode-property-regex": "^7.24.7", "@babel/plugin-transform-unicode-regex": "^7.24.7", @@ -2746,7 +2664,7 @@ "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -2779,9 +2697,9 @@ "license": "MIT" }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -2805,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.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2823,12 +2741,12 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -2838,9 +2756,9 @@ } }, "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.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -2909,44 +2827,13 @@ } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.2.0.tgz", - "integrity": "sha512-E7Vgw78I93we4ZWdYCb4DGAwRROGkMIXk7/y87UmANR+J6qsWusmC3gLt0H+O0KOt5e6O38U8oJamgbudrES/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@emnapi/wasi-threads": "1.0.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", - "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", - "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" + "node": ">=14.17.0" } }, "node_modules/@emotion/is-prop-valid": { @@ -2977,9 +2864,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", "cpu": [ "ppc64" ], @@ -2990,13 +2877,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", "cpu": [ "arm" ], @@ -3007,13 +2894,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", "cpu": [ "arm64" ], @@ -3024,13 +2911,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", "cpu": [ "x64" ], @@ -3041,13 +2928,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", "cpu": [ "arm64" ], @@ -3058,13 +2945,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", "cpu": [ "x64" ], @@ -3075,13 +2962,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", "cpu": [ "arm64" ], @@ -3092,13 +2979,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", "cpu": [ "x64" ], @@ -3109,13 +2996,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", "cpu": [ "arm" ], @@ -3126,13 +3013,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", "cpu": [ "arm64" ], @@ -3143,13 +3030,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", "cpu": [ "ia32" ], @@ -3160,13 +3047,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", "cpu": [ "loong64" ], @@ -3177,13 +3064,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", "cpu": [ "mips64el" ], @@ -3194,13 +3081,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", "cpu": [ "ppc64" ], @@ -3211,13 +3098,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", "cpu": [ "riscv64" ], @@ -3228,13 +3115,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", "cpu": [ "s390x" ], @@ -3245,13 +3132,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", "cpu": [ "x64" ], @@ -3262,13 +3149,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", "cpu": [ "x64" ], @@ -3279,13 +3166,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", "cpu": [ "x64" ], @@ -3296,13 +3200,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", "cpu": [ "x64" ], @@ -3313,13 +3217,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", "cpu": [ "arm64" ], @@ -3330,13 +3234,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", "cpu": [ "ia32" ], @@ -3347,13 +3251,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", "cpu": [ "x64" ], @@ -3364,7 +3268,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -3394,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": { @@ -3518,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": { @@ -3657,41 +3561,16 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.4.7.tgz", - "integrity": "sha512-5YwCySyV1UEgqzz34gNsC38eKxRBtlRDpJLlKcRtTjlYA/yDKuc1rfw+hjw+2WJxbAZtaDPsRl5Zk7J14SBoBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.0.10", - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/checkbox/node_modules/@inquirer/core": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", - "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", + "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", "dev": true, "license": "MIT", "dependencies": { + "@inquirer/core": "^9.1.0", "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.1.0", - "@types/wrap-ansi": "^3.0.0", + "@inquirer/type": "^1.5.3", "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -3699,87 +3578,30 @@ } }, "node_modules/@inquirer/confirm": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.11.tgz", - "integrity": "sha512-3wWw10VPxQP279FO4bzWsf8YjIAq7NdwATJ4xS2h1uwsXZu/RmtOVV95rZ7yllS1h/dzu+uLewjMAzNDEj8h2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^8.2.4", - "@inquirer/type": "^1.3.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.4.tgz", - "integrity": "sha512-7vsXSfxtrrbwMTirfaKwPcjqJy7pzeuF/bP62yo1NQrRJ5HjmMlrhZml/Ljm9ODc1RnbhJlTeSnCkjtFddKjwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.3", - "@inquirer/type": "^1.3.3", - "@types/mute-stream": "^0.0.4", - "@types/node": "^20.14.9", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "picocolors": "^1.0.1", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/core/node_modules/@types/node": { - "version": "20.14.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz", - "integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@inquirer/core/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@inquirer/editor": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.1.22.tgz", - "integrity": "sha512-K1QwTu7GCK+nKOVRBp5HY9jt3DXOfPGPr6WRDrPImkcJRelG9UTx2cAtK1liXmibRrzJlTWOwqgWT3k2XnS62w==", + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", "dev": true, "license": "MIT", "dependencies": { "@inquirer/core": "^9.0.10", - "@inquirer/type": "^1.5.2", - "external-editor": "^3.1.0" + "@inquirer/type": "^1.5.2" }, "engines": { "node": ">=18" } }, - "node_modules/@inquirer/editor/node_modules/@inquirer/core": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", - "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "node_modules/@inquirer/core": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.1.0.tgz", + "integrity": "sha512-RZVfH//2ytTjmaBIzeKT1zefcQZzuruwkpTwwbe/i2jTl4o9M+iML5ChULzz6iw1Ok8iUBBsRCjY2IEbD8Ft4w==", "dev": true, "license": "MIT", "dependencies": { "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", + "@inquirer/type": "^1.5.3", "@types/mute-stream": "^0.0.4", - "@types/node": "^22.1.0", + "@types/node": "^22.5.2", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "cli-spinners": "^2.9.2", @@ -3794,40 +3616,30 @@ "node": ">=18" } }, - "node_modules/@inquirer/expand": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.1.22.tgz", - "integrity": "sha512-wTZOBkzH+ItPuZ3ZPa9lynBsdMp6kQ9zbjVPYEtSBG7UulGjg2kQiAnUjgyG4SlntpTce5bOmXAPvE4sguXjpA==", + "node_modules/@inquirer/editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", + "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.0.10", - "@inquirer/type": "^1.5.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "external-editor": "^3.1.0" }, "engines": { "node": ">=18" } }, - "node_modules/@inquirer/expand/node_modules/@inquirer/core": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", - "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "node_modules/@inquirer/expand": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.2.0.tgz", + "integrity": "sha512-PD0z1dTRTIlpcnXRMRvdVPfBe10jBf4i7YLBU8tNWDkf3HxqmdymVvqnT8XG+hxQSvqfpJCe13Jv2Iv1eB3bIg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.1.0", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -3845,138 +3657,95 @@ } }, "node_modules/@inquirer/input": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.2.9.tgz", - "integrity": "sha512-7Z6N+uzkWM7+xsE+3rJdhdG/+mQgejOVqspoW+w0AbSZnL6nq5tGMEVASaYVWbkoSzecABWwmludO2evU3d31g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.0.10", - "@inquirer/type": "^1.5.2" + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" }, "engines": { "node": ">=18" } }, - "node_modules/@inquirer/input/node_modules/@inquirer/core": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", - "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "node_modules/@inquirer/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", + "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.1.0", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" }, "engines": { "node": ">=18" } }, "node_modules/@inquirer/password": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.1.22.tgz", - "integrity": "sha512-5Fxt1L9vh3rAKqjYwqsjU4DZsEvY/2Gll+QkqR4yEpy6wvzLxdSgFhUcxfDAOtO4BEoTreWoznC0phagwLU5Kw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", + "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.0.10", - "@inquirer/type": "^1.5.2", + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", "ansi-escapes": "^4.3.2" }, "engines": { "node": ">=18" } }, - "node_modules/@inquirer/password/node_modules/@inquirer/core": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", - "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.1.0", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@inquirer/prompts": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.0.7.tgz", - "integrity": "sha512-GFcigCxJTKCH3aECzMIu4FhgLJWnFvMXzpI4CCSoELWFtkOOU2P+goYA61+OKpGrB8fPE7q6n8zAXBSlZRrHjQ==", + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz", + "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^2.3.7", - "@inquirer/confirm": "^3.1.11", - "@inquirer/editor": "^2.1.11", - "@inquirer/expand": "^2.1.11", - "@inquirer/input": "^2.1.11", - "@inquirer/password": "^2.1.11", - "@inquirer/rawlist": "^2.1.11", - "@inquirer/select": "^2.3.7" + "@inquirer/checkbox": "^2.4.7", + "@inquirer/confirm": "^3.1.22", + "@inquirer/editor": "^2.1.22", + "@inquirer/expand": "^2.1.22", + "@inquirer/input": "^2.2.9", + "@inquirer/number": "^1.0.10", + "@inquirer/password": "^2.1.22", + "@inquirer/rawlist": "^2.2.4", + "@inquirer/search": "^1.0.7", + "@inquirer/select": "^2.4.7" }, "engines": { "node": ">=18" } }, "node_modules/@inquirer/rawlist": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.2.4.tgz", - "integrity": "sha512-pb6w9pWrm7EfnYDgQObOurh2d2YH07+eDo3xQBsNAM2GRhliz6wFXGi1thKQ4bN6B0xDd6C3tBsjdr3obsCl3Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", + "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.0.10", - "@inquirer/type": "^1.5.2", + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" } }, - "node_modules/@inquirer/rawlist/node_modules/@inquirer/core": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", - "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "node_modules/@inquirer/search": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", + "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", "dev": true, "license": "MIT", "dependencies": { + "@inquirer/core": "^9.1.0", "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.1.0", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", + "@inquirer/type": "^1.5.3", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -3984,41 +3753,16 @@ } }, "node_modules/@inquirer/select": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.4.7.tgz", - "integrity": "sha512-JH7XqPEkBpNWp3gPCqWqY8ECbyMoFcCZANlL6pV9hf59qK6dGmkOlx1ydyhY+KZ0c5X74+W6Mtp+nm2QX0/MAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^9.0.10", - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/select/node_modules/@inquirer/core": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", - "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", "dev": true, "license": "MIT", "dependencies": { + "@inquirer/core": "^9.1.0", "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.2", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.1.0", - "@types/wrap-ansi": "^3.0.0", + "@inquirer/type": "^1.5.3", "ansi-escapes": "^4.3.2", - "cli-spinners": "^2.9.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -4026,9 +3770,9 @@ } }, "node_modules/@inquirer/type": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.2.tgz", - "integrity": "sha512-w9qFkumYDCNyDZmNQjf/n6qQuvQ4dMC3BJesY4oF+yr0CxR5vxujflAVeIcS6U336uzi9GM0kAfZlLrZ9UTkpA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.3.tgz", + "integrity": "sha512-xUQ14WQGR/HK5ei+2CvgcwoH9fQ4PgPGmVFSN0pc1+fVyDL3MREhyAY7nxEErSu6CkllBM3D7e3e+kOvtu+eIg==", "dev": true, "license": "MIT", "dependencies": { @@ -4352,6 +4096,16 @@ "node": ">=8" } }, + "node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/console/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4476,12 +4230,22 @@ "node": ">=8" } }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", + "node_modules/@jest/core/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4673,6 +4437,16 @@ "node": ">=8" } }, + "node_modules/@jest/reporters/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/reporters/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4746,6 +4520,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/test-sequencer/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/transform": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", @@ -4843,6 +4627,16 @@ "node": ">=8" } }, + "node_modules/@jest/transform/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/transform/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5074,13 +4868,13 @@ "license": "MIT" }, "node_modules/@listr2/prompt-adapter-inquirer": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.13.tgz", - "integrity": "sha512-nAl6teTt7EWSjttNavAnv3uFR3w3vPP3OTYmHyPNHzKhAj2NoBDHmbS3MGpvvO8KXXPASnHjEGrrKrdKTMKPnQ==", + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz", + "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/type": "^1.3.3" + "@inquirer/type": "^1.5.1" }, "engines": { "node": ">=18.0.0" @@ -5090,9 +4884,9 @@ } }, "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.12.tgz", - "integrity": "sha512-vgTwzNUD3Hy4aqtGhX2+nV/usI0mwy3hDRuTjs8VcK0BLiMVEpNQXgzwlWEgPmA8AAPloUgyOs2nK5clJF5oIg==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz", + "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==", "cpu": [ "arm64" ], @@ -5104,9 +4898,9 @@ ] }, "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.12.tgz", - "integrity": "sha512-qOt0hAhj2ZLY6aEWu85rzt5zcyCAQITMhCMEPNlo1tuYekpVAdkQNiwXxEkCjBYvwTskvXuwXOOUpjuSc+aJnA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.13.tgz", + "integrity": "sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==", "cpu": [ "x64" ], @@ -5118,9 +4912,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.12.tgz", - "integrity": "sha512-Ggd/UXpE+alMncbELCXA3OKpDj9bDBR3qVO7WRTxstloDglRAHfZmUJgTkeaNKjFO1JHqS7AKy0jba9XebZB1w==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.13.tgz", + "integrity": "sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==", "cpu": [ "arm" ], @@ -5132,9 +4926,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.12.tgz", - "integrity": "sha512-Qy4cFXFe9h1wAWMsojex8x1ifvw2kqiZv686YiRTdQEzAfc3vJASHFcD/QejHUCx7YHMYdnUoCS45rG2AiGDTQ==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.13.tgz", + "integrity": "sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==", "cpu": [ "arm64" ], @@ -5146,9 +4940,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-x64": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.12.tgz", - "integrity": "sha512-c+noT9IofktxktFllKHFmci8ka2SYGSLN17pj/KSl1hg7mmfAiGp4xxFxEwMLTb+SX95vP1DFiR++1I3WLVxvA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.13.tgz", + "integrity": "sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==", "cpu": [ "x64" ], @@ -5160,9 +4954,9 @@ ] }, "node_modules/@lmdb/lmdb-win32-x64": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.12.tgz", - "integrity": "sha512-CO3MFV8gUx16NU/CyyuumAKblESwvoGVA2XhQKZ976OTOxaTbb8F8D3f0iiZ4MYqsN74jIrFuCmXpPnpjbhfOQ==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz", + "integrity": "sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==", "cpu": [ "x64" ], @@ -5218,6 +5012,93 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", @@ -5302,22 +5183,10 @@ "win32" ] }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", - "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@emnapi/core": "^1.1.0", - "@emnapi/runtime": "^1.1.0", - "@tybys/wasm-util": "^0.9.0" - } - }, "node_modules/@ng-bootstrap/ng-bootstrap": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-17.0.0.tgz", - "integrity": "sha512-hTbBtozJlpevF1RO6J2adCoXiAkMTPV3wmXIyK05dVha4VsKjHibgaL6YldToKoh6ElQnIYkPEIJHX9z5EtyMw==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-17.0.1.tgz", + "integrity": "sha512-utbm8OXIoqVVYGVzQkOS773ymbjc+UMkXv8lyi7hTqLhCQs0rZ0yA74peqVZRuOGXLHgcSTA7fnJhA80iQOblw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -5332,9 +5201,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.1.4.tgz", - "integrity": "sha512-suoeZjd+7qd3ivzbNGGSzHtY/WMxTKU6ZD1gIIya0Un8Ve1eVxfq6Si6ReKqhygO8zN3paJMATn8sMmAV7qVrw==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.3.tgz", + "integrity": "sha512-DDuBHcu23qckt43SexBJaPEIeMc/HKaFOidILZM9D4gU4C9VroMActdR218dvQ802QfL0S46t5Ykz8ENprIfjA==", "dev": true, "license": "MIT", "engines": { @@ -5570,22 +5439,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/package-json/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", @@ -5679,89 +5532,143 @@ "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/devkit": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.5.7.tgz", - "integrity": "sha512-sTEwqsAT6bMturU14o/0O6v509OkwGOglxpbiL/zIYO/fDkMoNgnhlHBIT87i4YVuofMz2Z+hTfjDskzDPRSYw==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", - "dependencies": { - "@nx/devkit": "19.5.7" + "optional": true, + "engines": { + "node": ">=14" } }, - "node_modules/@nrwl/tao": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.5.7.tgz", - "integrity": "sha512-c1rN6HY97+cEwoM5Q9412399Ac1rw7pI/3IS5iJSYkeI5TTGOobIpdCavJPZVcfqo4+wegXPA3F/OmulgbOUJA==", + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, "license": "MIT", - "dependencies": { - "nx": "19.5.7", - "tslib": "^2.3.0" + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, - "bin": { - "tao": "index.js" + "funding": { + "url": "https://opencollective.com/unts" } }, - "node_modules/@nx/devkit": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.5.7.tgz", - "integrity": "sha512-mUtZQcdqbF0Q9HfyG14jmpPCtZ1GnVaLNIADZv5SLpFyfh4ZjaBw6wdjPj7Sp3imLoyqMrcd9nCRNO2hlem8bw==", - "dev": true, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@redux-saga/core": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.3.0.tgz", + "integrity": "sha512-L+i+qIGuyWn7CIg7k1MteHGfttKPmxwZR5E7OsGikCL2LzYA0RERlaUY00Y3P3ZV2EYgrsYlBrGs6cJP5OKKqA==", "license": "MIT", "dependencies": { - "@nrwl/devkit": "19.5.7", - "ejs": "^3.1.7", - "enquirer": "~2.3.6", - "ignore": "^5.0.4", - "minimatch": "9.0.3", - "semver": "^7.5.3", - "tmp": "~0.2.1", - "tslib": "^2.3.0", - "yargs-parser": "21.1.1" + "@babel/runtime": "^7.6.3", + "@redux-saga/deferred": "^1.2.1", + "@redux-saga/delay-p": "^1.2.1", + "@redux-saga/is": "^1.1.3", + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1", + "typescript-tuple": "^2.2.1" }, - "peerDependencies": { - "nx": ">= 17 <= 20" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/redux-saga" + } + }, + "node_modules/@redux-saga/deferred": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", + "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==", + "license": "MIT" + }, + "node_modules/@redux-saga/delay-p": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", + "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", + "license": "MIT", + "dependencies": { + "@redux-saga/symbols": "^1.1.3" + } + }, + "node_modules/@redux-saga/is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", + "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", + "license": "MIT", + "dependencies": { + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1" } }, - "node_modules/@nx/nx-darwin-arm64": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.5.7.tgz", - "integrity": "sha512-5jFAZSfV8QVNoxOXayZw4/jNJbxMMctNOYZW8Qj4eU8Ti+OmhsLgouxz/9enCh5SDITriOMZ7IHZ9rhrlGQoig==", + "node_modules/@redux-saga/symbols": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", + "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==", + "license": "MIT" + }, + "node_modules/@redux-saga/types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", + "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "cpu": [ + "arm64" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@nx/nx-darwin-x64": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.5.7.tgz", - "integrity": "sha512-Ss+rF2+MQxyKrNnSYAeEGhtdE9hUHiTqyjJo4n1lvIWJ++TairOCtk5QRHrYLgAxE1XTf0OabcsDzegxv7yk3Q==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@nx/nx-freebsd-x64": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.5.7.tgz", - "integrity": "sha512-FMLXcUr3mw/v4LvmNqHMAXy2k+T/hp2YpdBUq9ExteMfRywFsnKNlm39n/quniFsgKthEMdvvzxSQppRKaVwIw==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", "cpu": [ "x64" ], @@ -5769,16 +5676,13 @@ "license": "MIT", "optional": true, "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } + "darwin" + ] }, - "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.5.7.tgz", - "integrity": "sha512-LhJ342HutpR258lBLVTkXd6x2Uj4ZPJ6xKdfEm+FYQvG1byPr2L0TlNXcfSBkYtd7wRn0qg9eQZoCV/5+w415Q==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", "cpu": [ "arm" ], @@ -5787,292 +5691,26 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.5.7.tgz", - "integrity": "sha512-Q6gN+VNLisg7mYPTXC5JuGCP/s9tLjJFclKdH6FoP5K1Hgy88KK1uUoivDIfI8xaEgyLqphD1AAqokiFWZNWsg==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@nx/nx-linux-arm64-musl": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.5.7.tgz", - "integrity": "sha512-BsYNcYujNKb+uE7PrJp4PrX8a3G9oy+THaUr0t5+L435HjuZDBiK+tK9JzYGvM0bR5FOYm5K99I1DVD/Hv0snw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-linux-x64-gnu": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.5.7.tgz", - "integrity": "sha512-ILaLU8b5lUokYVF3vxAVj62qFok1hexiNzBdLGJPI1OkPGELtLyb8RymI3939iJoNMk1DS3/6dqK7NHXvHX8Mw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-linux-x64-musl": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.5.7.tgz", - "integrity": "sha512-LfTnO4JZebLugioMk+GTptv3N38Wj2i2Pko0bdRZaKba+INGSlUgFqoRuO0KqZEmVIUGrxfkfqIN3HghVQ4D/Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.5.7.tgz", - "integrity": "sha512-cCTttdbf1AKuDU8j108SpIMWs53A/0mOVDPOPpa+oKkvBaI8ruZkxOceMjWZjWULd2gi1nS+5nJePpbrdQ8mkg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-win32-x64-msvc": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.5.7.tgz", - "integrity": "sha512-EqSnjpq1PNR/C8/YkL+Gn79dDfQ+HwJM8VJOt4qoCOQ9gQZqNJphjW2hg0H8WxLYezMScx3fbL99mvJO7ab2Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@redux-saga/core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.3.0.tgz", - "integrity": "sha512-L+i+qIGuyWn7CIg7k1MteHGfttKPmxwZR5E7OsGikCL2LzYA0RERlaUY00Y3P3ZV2EYgrsYlBrGs6cJP5OKKqA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.6.3", - "@redux-saga/deferred": "^1.2.1", - "@redux-saga/delay-p": "^1.2.1", - "@redux-saga/is": "^1.1.3", - "@redux-saga/symbols": "^1.1.3", - "@redux-saga/types": "^1.2.1", - "typescript-tuple": "^2.2.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/redux-saga" - } - }, - "node_modules/@redux-saga/deferred": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", - "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==", - "license": "MIT" - }, - "node_modules/@redux-saga/delay-p": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", - "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", - "license": "MIT", - "dependencies": { - "@redux-saga/symbols": "^1.1.3" - } - }, - "node_modules/@redux-saga/is": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", - "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", - "license": "MIT", - "dependencies": { - "@redux-saga/symbols": "^1.1.3", - "@redux-saga/types": "^1.2.1" - } - }, - "node_modules/@redux-saga/symbols": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", - "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==", - "license": "MIT" - }, - "node_modules/@redux-saga/types": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", - "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", "cpu": [ "arm64" ], @@ -6084,9 +5722,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", "cpu": [ "arm64" ], @@ -6098,9 +5736,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", "cpu": [ "ppc64" ], @@ -6112,9 +5750,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", "cpu": [ "riscv64" ], @@ -6126,9 +5764,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", "cpu": [ "s390x" ], @@ -6140,9 +5778,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", "cpu": [ "x64" ], @@ -6154,9 +5792,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", "cpu": [ "x64" ], @@ -6168,9 +5806,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", "cpu": [ "arm64" ], @@ -6182,9 +5820,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", "cpu": [ "ia32" ], @@ -6196,9 +5834,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", "cpu": [ "x64" ], @@ -6210,14 +5848,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "18.1.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.1.4.tgz", - "integrity": "sha512-M3edVYKiAGWAAKs7XDLpz1OKUy4STVMT+46Y44ydYz06hI8m/dJfS8ZHTvXPl7JhkrIrSDEMed+WidZtGPIxMg==", + "version": "18.2.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.3.tgz", + "integrity": "sha512-whSON70z9HYb4WboVXmPFE/RLKJJQLWNzNcUyi8OSDZkQbJnYgPp0///n738m26Y/XeJDv11q1gESy+Zl2AdUw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.1.4", - "@angular-devkit/schematics": "18.1.4", + "@angular-devkit/core": "18.2.3", + "@angular-devkit/schematics": "18.2.3", "jsonc-parser": "3.3.1" }, "engines": { @@ -6227,73 +5865,73 @@ } }, "node_modules/@sentry-internal/browser-utils": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.25.0.tgz", - "integrity": "sha512-nlWgp1lVhNQOTUplW85G3qm0fOIgAhJ/sl/31OIuScVrITYhYDF2bO+Zv/jQ8YsdUBAUXqY1tPT9wwPJklnPhw==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.28.0.tgz", + "integrity": "sha512-tE9++KEy8SlqibTmYymuxFVAnutsXBqrwQ936WJbjaMfkqXiro7C1El0ybkprskd0rKS7kln20Q6nQlNlMEoTA==", "license": "MIT", "dependencies": { - "@sentry/core": "8.25.0", - "@sentry/types": "8.25.0", - "@sentry/utils": "8.25.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/feedback": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.25.0.tgz", - "integrity": "sha512-327I5XJAFrsgjc5qUKxZ9rff3WNCfGvf1fIii70LQ2YQhQgG4XHZILmkD06ETEyXb+H1tkrNQQEJ1/d4ai+q5g==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.28.0.tgz", + "integrity": "sha512-5vYunPCDBLCJ8QNnhepacdYheiN+UtYxpGAIaC/zjBC1nDuBgWs+TfKPo1UlO/1sesfgs9ibpxtShOweucL61g==", "license": "MIT", "dependencies": { - "@sentry/core": "8.25.0", - "@sentry/types": "8.25.0", - "@sentry/utils": "8.25.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.25.0.tgz", - "integrity": "sha512-3f7x8EYthyj157uV9V8vBjun+1gJnHhh2+i0qxYLhMGx7N2Fq0J3Bvvo1rosSg+fYh5HzPNZDufwIRdg5C/MQw==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.28.0.tgz", + "integrity": "sha512-70jvzzOL5O74gahgXKyRkZgiYN93yly5gq+bbj4/6NRQ+EtPd285+ccy0laExdfyK0ugvvwD4v+1MQit52OAsg==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.25.0", - "@sentry/core": "8.25.0", - "@sentry/types": "8.25.0", - "@sentry/utils": "8.25.0" + "@sentry-internal/browser-utils": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.25.0.tgz", - "integrity": "sha512-dPXlkAbkFL1DBum8rGTaHS+apJKaXEZJF9gLcBBKTruhTCizrugFLxajzIfVSiFVuwNKuJWa2fzhzbeQM0ee7w==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.28.0.tgz", + "integrity": "sha512-RfpYHDHMUKGeEdx41QtHITjEn6P3tGaDPHvatqdrD3yv4j+wbJ6laX1PrIxCpGFUtjdzkqi/KUcvUd2kzbH/FA==", "license": "MIT", "dependencies": { - "@sentry-internal/replay": "8.25.0", - "@sentry/core": "8.25.0", - "@sentry/types": "8.25.0", - "@sentry/utils": "8.25.0" + "@sentry-internal/replay": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/angular": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.25.0.tgz", - "integrity": "sha512-sOHQrmD9B0adZxKTKs7CS0PaEShOBYRoCKOGjNx0iCEpPiswHHi9DzpniRYk1vNIL3Z0O7/eWoudnBojZNs8/g==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.28.0.tgz", + "integrity": "sha512-zHl0OSgBsHnQCINepRxYDsosvKnwJPc9tdRJyIgQ6JCG1kWZf0lHncXRnJBkBSrJk2wJQ0acondhwHRyAptRGg==", "license": "MIT", "dependencies": { - "@sentry/browser": "8.25.0", - "@sentry/core": "8.25.0", - "@sentry/types": "8.25.0", - "@sentry/utils": "8.25.0", + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "tslib": "^2.4.1" }, "engines": { @@ -6307,52 +5945,52 @@ } }, "node_modules/@sentry/browser": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.25.0.tgz", - "integrity": "sha512-51bdVGXjyooqVGzaSGsnExqRTt9NvZ1zGFsxbbCSXi5UoEFN6zdMUz6jKYsL2K80eeELP2VKOVlobHlEzeJQfw==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.28.0.tgz", + "integrity": "sha512-i/gjMYzIGQiPFH1pCbdnTwH9xs9mTAqzN+goP3GWX5a58frc7h8vxyA/5z0yMd0aCW6U8mVxnoAT72vGbKbx0g==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.25.0", - "@sentry-internal/feedback": "8.25.0", - "@sentry-internal/replay": "8.25.0", - "@sentry-internal/replay-canvas": "8.25.0", - "@sentry/core": "8.25.0", - "@sentry/types": "8.25.0", - "@sentry/utils": "8.25.0" + "@sentry-internal/browser-utils": "8.28.0", + "@sentry-internal/feedback": "8.28.0", + "@sentry-internal/replay": "8.28.0", + "@sentry-internal/replay-canvas": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/core": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.25.0.tgz", - "integrity": "sha512-7KtglbrW1eX4DOHkf6i4rRIExEf2CgtQ99qZ8gn5FUaAmNMg0rK7bb1yZMx0RZtp5G1TSz/S0jQQgxHWebaEig==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.28.0.tgz", + "integrity": "sha512-+If9uubvpZpvaQQw4HLiKPhrSS9/KcoA/AcdQkNm+5CVwAoOmDPtyYfkPBgfo2hLZnZQqR1bwkz/PrNoOm+gqA==", "license": "MIT", "dependencies": { - "@sentry/types": "8.25.0", - "@sentry/utils": "8.25.0" + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/types": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.25.0.tgz", - "integrity": "sha512-ojim0gDcRhGJPguYrtms4FsprX4xZz3LGNk9Z0hwTbSVEdlhQIInsQ7CYcdM3sjUs+qT7kfpxTRZGUeZNRRJcA==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.28.0.tgz", + "integrity": "sha512-hOfqfd92/AzBrEdMgmmV1VfOXJbIfleFTnerRl0mg/+CcNgP/6+Fdonp354TD56ouWNF2WkOM6sEKSXMWp6SEQ==", "license": "MIT", "engines": { "node": ">=14.18" } }, "node_modules/@sentry/utils": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.25.0.tgz", - "integrity": "sha512-mVlkV7S62ZZ2jM38/kOwWx2xoW8fUv2cjw2IwFKoAIPyLBh3mo1WJtvfdtN/rXGjQWZJBKW53EWaWnD00rkjyA==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.28.0.tgz", + "integrity": "sha512-smhk7PJpvDMQ2DB5p2qn9UeoUHdU41IgjMmS2xklZpa8tjzBTxDeWpGvrX2fuH67D9bAJuLC/XyZjJCHLoEW5g==", "license": "MIT", "dependencies": { - "@sentry/types": "8.25.0" + "@sentry/types": "8.28.0" }, "engines": { "node": ">=14.18" @@ -6654,32 +6292,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -6797,28 +6409,6 @@ "@types/trusted-types": "*" } }, - "node_modules/@types/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -6981,13 +6571,13 @@ } }, "node_modules/@types/node": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz", - "integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==", + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.13.0" + "undici-types": "~6.19.2" } }, "node_modules/@types/node-forge": { @@ -7031,9 +6621,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.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -7187,17 +6777,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.1.0.tgz", - "integrity": "sha512-LlNBaHFCEBPHyD4pZXb35mzjGkuGKXU5eeCA1SxvHfiRES0E82dOounfVpL4DCqYvJEKab0bZIA0gCRpdLKkCw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz", + "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.1.0", - "@typescript-eslint/type-utils": "8.1.0", - "@typescript-eslint/utils": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/type-utils": "8.4.0", + "@typescript-eslint/utils": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -7221,16 +6811,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.1.0.tgz", - "integrity": "sha512-U7iTAtGgJk6DPX9wIWPPOlt1gO57097G06gIcl0N0EEnNw8RGD62c+2/DiP/zL7KrkqnnqF7gtFGR7YgzPllTA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz", + "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.1.0", - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/typescript-estree": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4" }, "engines": { @@ -7250,14 +6840,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.1.0.tgz", - "integrity": "sha512-DsuOZQji687sQUjm4N6c9xABJa7fjvfIdjqpSIIVOgaENf2jFXiM9hIBZOL3hb6DHK9Nvd2d7zZnoMLf9e0OtQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz", + "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0" + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7268,14 +6858,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.1.0.tgz", - "integrity": "sha512-oLYvTxljVvsMnldfl6jIKxTaU7ok7km0KDrwOt1RHYu6nxlhN3TIx8k5Q52L6wR33nOwDgM7VwW1fT1qMNfFIA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz", + "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.1.0", - "@typescript-eslint/utils": "8.1.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/utils": "8.4.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -7293,9 +6883,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.1.0.tgz", - "integrity": "sha512-q2/Bxa0gMOu/2/AKALI0tCKbG2zppccnRIRCW6BaaTlRVaPKft4oVYPp7WOPpcnsgbr0qROAVCVKCvIQ0tbWog==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz", + "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==", "dev": true, "license": "MIT", "engines": { @@ -7307,16 +6897,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.1.0.tgz", - "integrity": "sha512-NTHhmufocEkMiAord/g++gWKb0Fr34e9AExBRdqgWdVBaKoei2dIyYKD9Q0jBnvfbEA5zaf8plUFMUH6kQ0vGg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz", + "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/visitor-keys": "8.1.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", @@ -7335,33 +6925,17 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.1.0.tgz", - "integrity": "sha512-ypRueFNKTIFwqPeJBfeIpxZ895PQhNyH4YID6js0UoBImWYoSjBsahUn9KMiJXh94uOjVBgHD9AmkyPsPnFwJA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.1.0", - "@typescript-eslint/types": "8.1.0", - "@typescript-eslint/typescript-estree": "8.1.0" + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7375,13 +6949,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.1.0.tgz", - "integrity": "sha512-ba0lNI19awqZ5ZNKh6wCModMwoZs457StTebQ0q1NP58zSi2F6MOZRXwfKZy+jB78JNJ/WH8GSh2IQNzXX8Nag==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz", + "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.1.0", + "@typescript-eslint/types": "8.4.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -7593,64 +7167,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/@yarnpkg/parsers": { - "version": "3.0.0-rc.46", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", - "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "js-yaml": "^3.10.0", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.15.0" - } - }, - "node_modules/@yarnpkg/parsers/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@yarnpkg/parsers/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@zkochan/js-yaml": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz", - "integrity": "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", @@ -7675,12 +7191,6 @@ "node": ">= 0.6" } }, - "node_modules/ace-builds": { - "version": "1.35.4", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.35.4.tgz", - "integrity": "sha512-r0KQclhZ/uk5a4zOqRYQkJuQuu4vFMiA6VTj54Tk4nI1TUR3iEMMppZkWbNoWEgWwv4ciDloObb9Rf4V55Qgjw==", - "license": "BSD-3-Clause" - }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -7784,16 +7294,16 @@ } }, "node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -7916,6 +7426,43 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -7958,9 +7505,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" }, @@ -7972,9 +7519,9 @@ "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -7992,11 +7539,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -8009,18 +7556,6 @@ "postcss": "^8.1.0" } }, - "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -8116,6 +7651,16 @@ "node": ">=8" } }, + "node_modules/babel-jest/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/babel-jest/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8268,24 +7813,27 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" @@ -8312,7 +7860,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/base64-js": { @@ -8464,12 +8012,6 @@ "@popperjs/core": "^2.11.8" } }, - "node_modules/brace": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz", - "integrity": "sha512-Fc8Ne62jJlKHiG/ajlonC4Sd66Pq68fFwK4ihJGNZpGqboc324SQk+lRvMzpPRuJOmfrJefdG8/7JdWX4bzJ2Q==", - "license": "MIT" - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -8657,22 +8199,6 @@ "dev": true, "license": "ISC" }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -8723,9 +8249,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001651", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", - "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "version": "1.0.30001657", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001657.tgz", + "integrity": "sha512-DPbJAlP8/BAXy3IgiWmZKItubb3TYGP0WscQQlVGIfT4s/YlFYVuJgyOsQNP7rJRChx/qdMeLJQJP0Sgg2yjNA==", "funding": [ { "type": "opencollective", @@ -8742,6 +8268,22 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -8801,7 +8343,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, + "devOptional": true, "license": "ISC", "engines": { "node": ">=10" @@ -8834,9 +8376,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", + "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==", "dev": true, "license": "MIT" }, @@ -9067,6 +8609,16 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -9146,7 +8698,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/connect-history-api-fallback": { @@ -9159,6 +8711,13 @@ "node": ">=0.8" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -9277,57 +8836,10 @@ "node": ">=10.13.0" } }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", - "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/path-type": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/core-js": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.0.tgz", - "integrity": "sha512-XPpwqEodRljce9KswjZShh95qJ1URisBeKCjUdq27YdenkslVe7OO0ZJhlYXAChW7OhXaRLl8AAba7IBfoIHug==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -9336,9 +8848,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": { @@ -10032,9 +9544,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.12", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz", - "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==", + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", "license": "MIT" }, "node_modules/debug": { @@ -10061,6 +9573,19 @@ "dev": true, "license": "MIT" }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -10190,6 +9715,13 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -10225,7 +9757,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -10259,9 +9791,9 @@ } }, "node_modules/diff-match-patch-typescript": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/diff-match-patch-typescript/-/diff-match-patch-typescript-1.0.8.tgz", - "integrity": "sha512-UPvsAUDje0DUOhx5V5jrXPe/5GHyBwZzS4myPFDM3Tbd/xJQyXbMkklc6aFqKBYzyhtdSMPD1CHHTDye/7cgow==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/diff-match-patch-typescript/-/diff-match-patch-typescript-1.1.0.tgz", + "integrity": "sha512-7WFVb3bRj5o+xRJtd1mLpbB9o19GE1FpY/v7z4GgMurmyaxZnuYdsEwn/K93ugn3nB+ce7KMn9hYjfAtXmUkVQ==", "license": "Apache-2.0" }, "node_modules/diff-sequences": { @@ -10287,6 +9819,16 @@ "node": ">=8" } }, + "node_modules/dir-glob/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -10365,48 +9907,12 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", - "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dotenv": "^16.4.4" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true, - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" + "license": "MIT" }, "node_modules/ee-first": { "version": "1.1.1", @@ -10432,9 +9938,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz", - "integrity": "sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA==", + "version": "1.5.15", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.15.tgz", + "integrity": "sha512-Z4rIDoImwEJW+YYKnPul4DzqsWVqYetYVN3XqDmRpgV0mjz0hYTaeeh+8/9CL1bk3AHYmF4freW/NTiVoXA2gA==", "license": "ISC" }, "node_modules/emittery": { @@ -10451,9 +9957,9 @@ } }, "node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, "license": "MIT" }, @@ -10502,16 +10008,6 @@ "node": ">=0.10.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enhanced-resolve": { "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", @@ -10526,19 +10022,6 @@ "node": ">=10.13.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -10637,9 +10120,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10647,51 +10130,52 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" } }, "node_modules/esbuild-wasm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.21.5.tgz", - "integrity": "sha512-L/FlOPMMFtw+6qPAbuPvJXdrOYOp9yx/PEwSrIZW0qghY4vgV003evdYDwqQ/9ENMQI0B6RMod9xT4FHtto6OQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz", + "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==", "dev": true, "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -10714,17 +10198,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", @@ -10904,26 +10388,41 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-plugin-deprecation/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/eslint-plugin-deprecation/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-deprecation/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/eslint-plugin-jest": { - "version": "28.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz", - "integrity": "sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==", + "version": "28.8.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.3.tgz", + "integrity": "sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11091,6 +10590,37 @@ "node": ">=4.0" } }, + "node_modules/eslint-plugin-jest-extended/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jest-extended/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/eslint-plugin-prettier": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", @@ -11507,9 +11037,9 @@ "license": "Apache-2.0" }, "node_modules/export-to-csv": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/export-to-csv/-/export-to-csv-1.3.0.tgz", - "integrity": "sha512-msPjbfozZdYzDghAEKmCVH5veMeKHNacplE6noXvGiA8AeV1qa/SOxp6JXDjF9R8Kf6v3ypI6jskiY19dkhZeA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/export-to-csv/-/export-to-csv-1.4.0.tgz", + "integrity": "sha512-6CX17Cu+rC2Fi2CyZ4CkgVG3hLl6BFsdAxfXiZkmDFIDY4mRx2y2spdeH6dqPHI9rP+AsHEfGeKz84Uuw7+Pmg==", "license": "MIT", "engines": { "node": "^v12.20.0 || >=v14.13.0" @@ -11594,19 +11124,6 @@ "node": ">=4" } }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -11657,6 +11174,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -11694,22 +11218,6 @@ "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", "license": "MIT" }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -11873,9 +11381,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.8.tgz", + "integrity": "sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig==", "dev": true, "funding": [ { @@ -11972,69 +11480,6 @@ "node": ">= 0.6" } }, - "node_modules/front-matter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", - "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1" - } - }, - "node_modules/front-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/front-matter/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/front-matter/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true, - "license": "MIT" - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -12052,7 +11497,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/fsevents": { @@ -12079,6 +11524,67 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -12175,7 +11681,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -12215,7 +11721,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -12226,7 +11732,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -12245,21 +11751,21 @@ } }, "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -12356,6 +11862,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -12618,9 +12131,9 @@ } }, "node_modules/husky": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz", - "integrity": "sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz", + "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==", "dev": true, "license": "MIT", "bin": { @@ -12691,9 +12204,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -12871,7 +12384,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -12956,9 +12469,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": { @@ -13227,9 +12740,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -13609,6 +13122,16 @@ "node": ">=8" } }, + "node_modules/jest-circus/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-circus/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13841,6 +13364,16 @@ "node": ">=8" } }, + "node_modules/jest-config/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-config/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14386,6 +13919,16 @@ "node": ">=8" } }, + "node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-message-util/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14569,6 +14112,16 @@ "node": ">=8" } }, + "node_modules/jest-resolve/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-resolve/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14809,6 +14362,16 @@ "node": ">=8" } }, + "node_modules/jest-runtime/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-runtime/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -15427,19 +14990,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -15528,9 +15078,9 @@ } }, "node_modules/launch-editor": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.1.tgz", - "integrity": "sha512-elBx2l/tp9z99X5H/qev8uyDywVh0VXAwEbjk8kJhnc5grOFkGh7aW6q55me9xnYbss261XtnUrysZ+XvGbhQA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", "dev": true, "license": "MIT", "dependencies": { @@ -15683,19 +15233,16 @@ } }, "node_modules/lines-and-columns": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", - "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } + "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.2.8", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.8.tgz", - "integrity": "sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ==", + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", "dev": true, "license": "MIT", "dependencies": { @@ -15705,7 +15252,7 @@ "execa": "~8.0.1", "lilconfig": "~3.1.2", "listr2": "~8.2.4", - "micromatch": "~4.0.7", + "micromatch": "~4.0.8", "pidtree": "~0.6.0", "string-argv": "~0.3.2", "yaml": "~2.5.0" @@ -15720,32 +15267,6 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/lint-staged/node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -15759,13 +15280,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/lint-staged/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true, - "license": "MIT" - }, "node_modules/lint-staged/node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", @@ -15826,24 +15340,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/lint-staged/node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -15902,22 +15398,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/lint-staged/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -15931,35 +15411,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/listr2": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.3.tgz", - "integrity": "sha512-Lllokma2mtoniUOS94CcOErHWAug5iu7HOmDrvWgpw8jyQH2fomgB+7lZS4HWZxytUuQwkGOwe49FvwVaA85Xw==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", "dev": true, "license": "MIT", "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", - "log-update": "^6.0.0", + "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" }, @@ -16035,9 +15497,9 @@ } }, "node_modules/lmdb": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.12.tgz", - "integrity": "sha512-JnoEulTgveoC64vlYJ9sufGLuNkk6TcxSYpKxSC9aM42I61jIv3pQH0fgb6qW7HV0+FNqA3g1WCQQYfhfawGoQ==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.13.tgz", + "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -16052,12 +15514,12 @@ "download-lmdb-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "3.0.12", - "@lmdb/lmdb-darwin-x64": "3.0.12", - "@lmdb/lmdb-linux-arm": "3.0.12", - "@lmdb/lmdb-linux-arm64": "3.0.12", - "@lmdb/lmdb-linux-x64": "3.0.12", - "@lmdb/lmdb-win32-x64": "3.0.12" + "@lmdb/lmdb-darwin-arm64": "3.0.13", + "@lmdb/lmdb-darwin-x64": "3.0.13", + "@lmdb/lmdb-linux-arm": "3.0.13", + "@lmdb/lmdb-linux-arm64": "3.0.13", + "@lmdb/lmdb-linux-x64": "3.0.13", + "@lmdb/lmdb-win32-x64": "3.0.13" } }, "node_modules/loader-runner": { @@ -16373,13 +15835,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/make-dir": { @@ -16509,9 +15971,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", @@ -16592,6 +16054,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", @@ -16621,9 +16096,9 @@ "license": "ISC" }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -16790,7 +16265,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -16804,7 +16279,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -16817,14 +16292,14 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -16840,9 +16315,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": { @@ -16945,6 +16420,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/nan": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -17021,9 +16503,9 @@ "license": "MIT" }, "node_modules/ng-mocks": { - "version": "14.13.0", - "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.13.0.tgz", - "integrity": "sha512-cQ6nUj/P+v7X52gYU6bAj/03iDKl2pzbPy2V0tx/d5lxME063Vxc190p6UPlJkbRIxcB+OqSALPgQvy83efzjw==", + "version": "14.13.1", + "resolved": "https://registry.npmjs.org/ng-mocks/-/ng-mocks-14.13.1.tgz", + "integrity": "sha512-eyfnjXeC108SqVD09i/cBwCpKkK0JjBoAg8jp7oQS2HS081K3WJTttFpgLGeLDYKmZsZ6nYpI+HHNQ3OksaJ7A==", "dev": true, "license": "MIT", "funding": { @@ -17093,6 +16575,52 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT", + "optional": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -17129,9 +16657,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "dev": true, "license": "MIT", "bin": { @@ -17186,22 +16714,6 @@ "node": ">=16" } }, - "node_modules/node-gyp/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", @@ -17225,13 +16737,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-machine-id": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", - "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true, - "license": "MIT" - }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -17325,9 +16830,9 @@ } }, "node_modules/npm-package-arg": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.2.tgz", - "integrity": "sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", "dev": true, "license": "ISC", "dependencies": { @@ -17354,9 +16859,9 @@ } }, "node_modules/npm-pick-manifest": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.1.tgz", - "integrity": "sha512-Udm1f0l2nXb3wxDpKjfohwgdFUSV50UVwzEIpDXVsbDMXVIEF81a/i0UhuQbhrPMMmdiq3+YMFLFIRVLs3hxQw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", "dev": true, "license": "ISC", "dependencies": { @@ -17402,6 +16907,20 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -17422,321 +16941,6 @@ "dev": true, "license": "MIT" }, - "node_modules/nx": { - "version": "19.5.7", - "resolved": "https://registry.npmjs.org/nx/-/nx-19.5.7.tgz", - "integrity": "sha512-AUmGgE19NB4m/7oHYQVdzZHtclVevD8w0/nNzzjDJE823T8oeoNhmc9MfCLz+/2l2KOp+Wqm+8LiG9/xWpXk0g==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@napi-rs/wasm-runtime": "0.2.4", - "@nrwl/tao": "19.5.7", - "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "3.0.0-rc.46", - "@zkochan/js-yaml": "0.0.7", - "axios": "^1.7.2", - "chalk": "^4.1.0", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^8.0.1", - "dotenv": "~16.4.5", - "dotenv-expand": "~11.0.6", - "enquirer": "~2.3.6", - "figures": "3.2.0", - "flat": "^5.0.2", - "front-matter": "^4.0.2", - "fs-extra": "^11.1.0", - "ignore": "^5.0.4", - "jest-diff": "^29.4.1", - "jsonc-parser": "3.2.0", - "lines-and-columns": "~2.0.3", - "minimatch": "9.0.3", - "node-machine-id": "1.1.12", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "ora": "5.3.0", - "semver": "^7.5.3", - "string-width": "^4.2.3", - "strong-log-transformer": "^2.1.0", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0", - "yargs": "^17.6.2", - "yargs-parser": "21.1.1" - }, - "bin": { - "nx": "bin/nx.js", - "nx-cloud": "bin/nx-cloud.js" - }, - "optionalDependencies": { - "@nx/nx-darwin-arm64": "19.5.7", - "@nx/nx-darwin-x64": "19.5.7", - "@nx/nx-freebsd-x64": "19.5.7", - "@nx/nx-linux-arm-gnueabihf": "19.5.7", - "@nx/nx-linux-arm64-gnu": "19.5.7", - "@nx/nx-linux-arm64-musl": "19.5.7", - "@nx/nx-linux-x64-gnu": "19.5.7", - "@nx/nx-linux-x64-musl": "19.5.7", - "@nx/nx-win32-arm64-msvc": "19.5.7", - "@nx/nx-win32-x64-msvc": "19.5.7" - }, - "peerDependencies": { - "@swc-node/register": "^1.8.0", - "@swc/core": "^1.3.85" - }, - "peerDependenciesMeta": { - "@swc-node/register": { - "optional": true - }, - "@swc/core": { - "optional": true - } - } - }, - "node_modules/nx/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/nx/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/nx/node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nx/node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nx/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/nx/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nx/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/nx/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/nx/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/nx/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nx/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/nx/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nx/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/nx/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nx/node_modules/ora": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", - "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nx/node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nx/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/nx/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nx/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -17793,7 +16997,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -18179,13 +17383,6 @@ "dev": true, "license": "MIT" }, - "node_modules/parse-json/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, "node_modules/parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", @@ -18261,7 +17458,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -18316,25 +17513,51 @@ "license": "MIT" }, "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pepjs": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.5.3.tgz", - "integrity": "sha512-5yHVB9OHqKd9fr/OIsn8ss0NgThQ9buaqrEuwr9Or5YjPp6h+WTDKWZI+xZLaBGZCtODTnFtlSHNmhFsq67THg==", - "license": "MIT" + "node_modules/path2d": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.1.tgz", + "integrity": "sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "node_modules/pdfjs-dist": { + "version": "4.6.82", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.6.82.tgz", + "integrity": "sha512-BUOryeRFwvbLe0lOU6NhkJNuVQUp06WxlJVVCsxdmJ4y5cU3O3s3/0DunVdK1PMm7v2MUw52qKYaidhDH1Z9+w==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "canvas": "^2.11.2", + "path2d": "^0.2.1" + } + }, + "node_modules/pepjs": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/pepjs/-/pepjs-0.5.3.tgz", + "integrity": "sha512-5yHVB9OHqKd9fr/OIsn8ss0NgThQ9buaqrEuwr9Or5YjPp6h+WTDKWZI+xZLaBGZCtODTnFtlSHNmhFsq67THg==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/picomatch": { @@ -18499,9 +17722,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": [ { @@ -18630,9 +17853,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", - "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { @@ -18650,9 +17873,9 @@ "license": "MIT" }, "node_modules/posthog-js": { - "version": "1.155.0", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.155.0.tgz", - "integrity": "sha512-gxi72Qcp7Vnq6efe5gNxsq84zyEFd33NUmoLSgcbMPhxU30qgc89Aw/N2mRB4mGrD3Mq0rCnDJUzGFdN59nR0g==", + "version": "1.160.3", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.160.3.tgz", + "integrity": "sha512-mGvxOIlWPtdPx8EI0MQ81wNKlnH2K0n4RqwQOl044b34BCKiFVzZ7Hc7geMuZNaRAvCi5/5zyGeWHcAYZQxiMQ==", "license": "MIT", "dependencies": { "fflate": "^0.4.8", @@ -18661,9 +17884,9 @@ } }, "node_modules/preact": { - "version": "10.23.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.23.1.tgz", - "integrity": "sha512-O5UdRsNh4vdZaTieWe3XOgSpdMAmkIYBCT3VhQDlKrzyCm8lUYsk0fmVEvoQQifoOjFRTaHZO69ylrzTW2BH+A==", + "version": "10.23.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.23.2.tgz", + "integrity": "sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==", "license": "MIT", "funding": { "type": "opencollective", @@ -18829,13 +18052,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -19438,62 +18654,115 @@ "license": "MIT" }, "node_modules/rimraf": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", - "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, "license": "ISC", "dependencies": { - "glob": "^10.3.7" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { "rimraf": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", + "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", + "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", "dev": true, "license": "MIT", "dependencies": { @@ -19507,22 +18776,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", "fsevents": "~2.3.2" } }, @@ -19592,9 +18861,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", + "version": "1.78.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz", + "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -19610,9 +18879,9 @@ } }, "node_modules/sass-loader": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.2.1.tgz", - "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", + "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", "dev": true, "license": "MIT", "dependencies": { @@ -19878,6 +19147,13 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -20073,10 +19349,43 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "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.5", + "resolved": "https://registry.npmjs.org/simple-statistics/-/simple-statistics-7.8.5.tgz", + "integrity": "sha512-yw4aOnkvPLbL80zamrEKznAnk5cIIkjEcx/z0aQl+m/YKMmVufrnWgWJWRspqZtwh+ElZXRhJ0MtnUjFUQV5Ow==", "license": "ISC", "engines": { "node": "*" @@ -20090,13 +19399,16 @@ "license": "MIT" }, "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/slice-ansi": { @@ -20322,9 +19634,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" }, @@ -20606,24 +19918,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strong-log-transformer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", - "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "duplexer": "^0.1.1", - "minimist": "^1.2.0", - "through": "^2.3.4" - }, - "bin": { - "sl-log-transformer": "bin/sl-log-transformer.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/styled-components": { "version": "5.3.11", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", @@ -20727,7 +20021,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -20741,43 +20035,11 @@ "node": ">=10" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -20790,7 +20052,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -20803,7 +20065,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, + "devOptional": true, "license": "ISC", "engines": { "node": ">=8" @@ -20813,13 +20075,13 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/terser": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", - "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -21030,13 +20292,6 @@ "tslib": "^2" } }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -21051,13 +20306,16 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, "engines": { - "node": ">=14.14" + "node": ">=0.6.0" } }, "node_modules/tmpl": { @@ -21114,16 +20372,6 @@ "node": ">=6" } }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/tr46": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", @@ -21207,21 +20455,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" @@ -21325,9 +20573,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": { @@ -21462,20 +20710,10 @@ "typescript-compare": "^0.0.2" } }, - "node_modules/undici": { - "version": "6.19.5", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.5.tgz", - "integrity": "sha512-LryC15SWzqQsREHIOUybavaIHF5IoL0dJ9aWWxL/PgT1KfqAW5225FZpDUFlt9xiDMS2/S7DOKhFWA7RLksWdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, "node_modules/undici-types": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", - "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true, "license": "MIT" }, @@ -21563,13 +20801,13 @@ } }, "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/unpipe": { @@ -21654,136 +20892,570 @@ "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4.0" + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" ], + "dev": true, "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=10.12.0" + "node": ">=12" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/vite": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz", - "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==", + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" - }, "bin": { - "vite": "bin/vite.js" + "esbuild": "bin/esbuild" }, "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" + "node": ">=12" }, "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/w3c-xmlserializer": { @@ -21924,13 +21596,12 @@ } }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -21939,7 +21610,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -21972,9 +21643,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": { @@ -22087,18 +21758,18 @@ } }, "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", - "wildcard": "^2.0.0" + "wildcard": "^2.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/webpack-sources": { @@ -22312,6 +21983,48 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", @@ -22503,7 +22216,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -22601,9 +22314,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index 614ce711c400..ab806fb582e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "artemis", - "version": "7.5.1", + "version": "7.5.2", "description": "Interactive Learning with Individual Feedback", "private": true, "license": "MIT", @@ -13,18 +13,18 @@ "node_modules" ], "dependencies": { - "@angular/animations": "18.1.4", - "@angular/cdk": "18.1.4", - "@angular/common": "18.1.4", - "@angular/compiler": "18.1.4", - "@angular/core": "18.1.4", - "@angular/forms": "18.1.4", - "@angular/localize": "18.1.4", - "@angular/material": "18.1.4", - "@angular/platform-browser": "18.1.4", - "@angular/platform-browser-dynamic": "18.1.4", - "@angular/router": "18.1.4", - "@angular/service-worker": "18.1.4", + "@angular/animations": "18.2.3", + "@angular/cdk": "18.2.3", + "@angular/common": "18.2.3", + "@angular/compiler": "18.2.3", + "@angular/core": "18.2.3", + "@angular/forms": "18.2.3", + "@angular/localize": "18.2.3", + "@angular/material": "18.2.3", + "@angular/platform-browser": "18.2.3", + "@angular/platform-browser-dynamic": "18.2.3", + "@angular/router": "18.2.3", + "@angular/service-worker": "18.2.3", "@ctrl/ngx-emoji-mart": "9.2.0", "@danielmoncada/angular-datetime-picker": "18.1.0", "@fingerprintjs/fingerprintjs": "4.4.3", @@ -34,23 +34,21 @@ "@fortawesome/free-regular-svg-icons": "6.6.0", "@fortawesome/free-solid-svg-icons": "6.6.0", "@ls1intum/apollon": "3.3.14", - "@ng-bootstrap/ng-bootstrap": "17.0.0", + "@ng-bootstrap/ng-bootstrap": "17.0.1", "@ngx-translate/core": "15.0.0", "@ngx-translate/http-loader": "8.0.0", - "@sentry/angular": "8.25.0", + "@sentry/angular": "8.28.0", "@swimlane/ngx-charts": "20.5.0", "@swimlane/ngx-graph": "8.4.0", "@vscode/codicons": "0.0.36", - "ace-builds": "1.35.4", "bootstrap": "5.3.3", - "brace": "0.11.1", "compare-versions": "6.1.1", - "core-js": "3.38.0", + "core-js": "3.38.1", "crypto-js": "4.2.0", - "dayjs": "1.11.12", - "diff-match-patch-typescript": "1.0.8", + "dayjs": "1.11.13", + "diff-match-patch-typescript": "1.1.0", "dompurify": "3.1.6", - "export-to-csv": "1.3.0", + "export-to-csv": "1.4.0", "fast-json-patch": "3.1.1", "franc-min": "6.2.0", "html-diff-ts": "1.4.2", @@ -60,21 +58,22 @@ "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", - "posthog-js": "1.155.0", + "pdfjs-dist": "4.6.82", + "posthog-js": "1.160.3", "rxjs": "7.8.1", "showdown": "2.1.0", "showdown-highlight": "3.1.0", "showdown-katex": "0.6.0", - "simple-statistics": "7.8.3", + "simple-statistics": "7.8.5", "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 +90,6 @@ "@typescript-eslint/utils": { "eslint": "^9.9.0" }, - "axios": "1.7.4", "braces": "3.0.3", "critters": "0.0.24", "debug": "4.3.6", @@ -99,56 +97,55 @@ "eslint": "^9.9.0" }, "eslint-plugin-jest": { - "@typescript-eslint/eslint-plugin": "^8.1.0" + "@typescript-eslint/eslint-plugin": "^8.4.0" }, "jsdom": "24.1.1", "katex": "0.16.11", - "postcss": "8.4.40", + "postcss": "8.4.41", + "rimraf": "6.0.1", "semver": "7.6.3", "showdown-katex": { "showdown": "2.1.0" }, "tough-cookie": "4.1.4", - "undici": "6.19.5", - "vite": "5.3.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.1.4", - "@angular-eslint/builder": "18.2.0", - "@angular-eslint/eslint-plugin": "18.2.0", - "@angular-eslint/eslint-plugin-template": "18.2.0", - "@angular-eslint/schematics": "18.2.0", - "@angular-eslint/template-parser": "18.2.0", - "@angular/cli": "18.1.4", - "@angular/compiler-cli": "18.1.4", - "@angular/language-service": "18.1.4", - "@sentry/types": "8.25.0", + "@angular-devkit/build-angular": "18.2.3", + "@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.3", + "@angular/compiler-cli": "18.2.3", + "@angular/language-service": "18.2.3", + "@sentry/types": "8.28.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.2.0", + "@types/node": "22.5.4", "@types/papaparse": "5.3.14", "@types/showdown": "2.0.6", "@types/smoothscroll-polyfill": "0.3.4", "@types/sockjs-client": "1.5.4", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.1.0", - "@typescript-eslint/parser": "8.1.0", - "eslint": "9.9.0", + "@typescript-eslint/eslint-plugin": "8.4.0", + "@typescript-eslint/parser": "8.4.0", + "eslint": "9.9.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", - "eslint-plugin-jest": "28.8.0", + "eslint-plugin-jest": "28.8.3", "eslint-plugin-jest-extended": "2.4.0", "eslint-plugin-prettier": "5.2.1", "folder-hash": "4.0.4", - "husky": "9.1.4", + "husky": "9.1.5", "jest": "29.7.0", "jest-canvas-mock": "2.5.2", "jest-date-mock": "1.0.10", @@ -156,11 +153,12 @@ "jest-fail-on-console": "3.3.0", "jest-junit": "16.0.0", "jest-preset-angular": "14.2.2", - "lint-staged": "15.2.8", - "ng-mocks": "14.13.0", + "lint-staged": "15.2.10", + "ng-mocks": "14.13.1", "prettier": "3.3.3", - "sass": "1.77.8", - "ts-jest": "29.2.4", + "rimraf": "6.0.1", + "sass": "1.78.0", + "ts-jest": "29.2.5", "typescript": "5.5.4", "weak-napi": "2.0.2" }, diff --git a/settings.gradle b/settings.gradle index b9f9d365979c..c99752089fcd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,6 +14,7 @@ pluginManagement { rootProject.name = 'Artemis' +// needed for rest call and endpoint analysis include 'supporting_scripts:analysis-of-endpoint-connections' // needed for programming exercise templates diff --git a/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java b/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java index e64bb62fccbb..674012737006 100644 --- a/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java +++ b/src/main/java/de/tum/in/www1/artemis/ArtemisApp.java @@ -19,11 +19,12 @@ import org.springframework.core.env.Environment; import de.tum.in.www1.artemis.config.ProgrammingLanguageConfiguration; +import de.tum.in.www1.artemis.config.TheiaConfiguration; import tech.jhipster.config.DefaultProfileUtil; import tech.jhipster.config.JHipsterConstants; @SpringBootApplication -@EnableConfigurationProperties({ LiquibaseProperties.class, ProgrammingLanguageConfiguration.class }) +@EnableConfigurationProperties({ LiquibaseProperties.class, ProgrammingLanguageConfiguration.class, TheiaConfiguration.class }) public class ArtemisApp { private static final Logger log = LoggerFactory.getLogger(ArtemisApp.class); diff --git a/src/main/java/de/tum/in/www1/artemis/config/PropertiesConfigurationGuard.java b/src/main/java/de/tum/in/www1/artemis/config/PropertiesConfigurationGuard.java new file mode 100644 index 000000000000..898bf2d78c38 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/config/PropertiesConfigurationGuard.java @@ -0,0 +1,31 @@ +package de.tum.in.www1.artemis.config; + +import static de.tum.in.www1.artemis.config.Constants.PROFILE_SCHEDULING; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Component +@Profile(PROFILE_SCHEDULING) +public class PropertiesConfigurationGuard implements InitializingBean { + + private static final Logger log = LoggerFactory.getLogger(PropertiesConfigurationGuard.class); + + @Value("${info.operatorName:#{null}}") + private String operatorName; + + /** + * Checks if the info.operatorName value is set in the configuration ymls, and exits the application if not. + */ + public void afterPropertiesSet() { + if (this.operatorName == null || this.operatorName.isEmpty()) { + log.error( + "The name of the operator (University) is not configured in the application-prod.yml! It is needed to be displayed in the /about page, and for the telemetry service."); + throw new IllegalArgumentException("The name of the operator (university) must be configured, but is not!"); + } + } +} diff --git a/src/main/java/de/tum/in/www1/artemis/config/TheiaConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/TheiaConfiguration.java new file mode 100644 index 000000000000..02a50a5201ed --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/config/TheiaConfiguration.java @@ -0,0 +1,43 @@ +package de.tum.in.www1.artemis.config; + +import static de.tum.in.www1.artemis.config.Constants.PROFILE_THEIA; + +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage; + +@Profile(PROFILE_THEIA) +@Configuration +@ConfigurationProperties(prefix = "theia") +public class TheiaConfiguration { + + private Map> images; + + public void setImages(final Map> images) { + this.images = images; + } + + /** + * Get the images for all languages + * + * @return a map of language -> [flavor/name -> image-link] + */ + public Map> getImagesForAllLanguages() { + return images; + } + + /** + * Get the images for a specific language + * + * @param language the language for which the images should be retrieved + * @return a map of flavor/name -> image-link + */ + public Map getImagesForLanguage(ProgrammingLanguage language) { + return images.get(language); + } + +} diff --git a/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java b/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java index 45bfdaf4a97b..f88d93656d1b 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java +++ b/src/main/java/de/tum/in/www1/artemis/config/connector/GitLabApiConfiguration.java @@ -8,6 +8,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Configuration @Profile("gitlab | gitlabci") public class GitLabApiConfiguration { diff --git a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java index 73d81028276b..41662e7c3be6 100644 --- a/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java +++ b/src/main/java/de/tum/in/www1/artemis/config/lti/CustomLti13Configurer.java @@ -5,9 +5,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.web.authentication.logout.LogoutFilter; -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.stereotype.Component; +import de.tum.in.www1.artemis.security.jwt.JWTFilter; import de.tum.in.www1.artemis.service.OnlineCourseConfigurationService; import de.tum.in.www1.artemis.service.connectors.lti.Lti13Service; import de.tum.in.www1.artemis.web.filter.Lti13LaunchFilter; @@ -74,7 +74,7 @@ public void configure(HttpSecurity http) { // https://www.imsglobal.org/spec/security/v1p0/#step-3-authentication-response OAuth2LoginAuthenticationFilter defaultLoginFilter = configureLoginFilter(clientRegistrationRepository(http), oidcLaunchFlowAuthenticationProvider, authorizationRequestRepository); - http.addFilterAfter(new Lti13LaunchFilter(defaultLoginFilter, "/" + LTI13_LOGIN_PATH, lti13Service(http)), AbstractPreAuthenticatedProcessingFilter.class); + http.addFilterAfter(new Lti13LaunchFilter(defaultLoginFilter, "/" + LTI13_LOGIN_PATH, lti13Service(http)), JWTFilter.class); } protected Lti13Service lti13Service(HttpSecurity http) { diff --git a/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java b/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java index b44ce90a2e67..ccb797194f8d 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/ProgrammingExercise.java @@ -732,8 +732,9 @@ public String toString() { public void validateProgrammingSettings() { // Check if a participation mode was selected - if (!Boolean.TRUE.equals(isAllowOnlineEditor()) && !Boolean.TRUE.equals(isAllowOfflineIde())) { - throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor or the offline IDE", "Exercise", "noParticipationModeAllowed"); + if (!Boolean.TRUE.equals(isAllowOnlineEditor()) && !Boolean.TRUE.equals(isAllowOfflineIde()) && !isAllowOnlineIde()) { + throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor, the offline IDE, or the online IDE", "Exercise", + "noParticipationModeAllowed"); } // Check if Xcode has no online code editor enabled @@ -745,6 +746,11 @@ public void validateProgrammingSettings() { if (getProgrammingLanguage() == null) { throw new BadRequestAlertException("No programming language was specified", "Exercise", "programmingLanguageNotSet"); } + + // Check if theia image was selected if the online IDE is enabled + if (isAllowOnlineIde() && buildConfig.getTheiaImage() == null) { + throw new BadRequestAlertException("The Theia image must be selected if the online IDE is enabled", "Exercise", "theiaImageNotSet"); + } } /** diff --git a/src/main/java/de/tum/in/www1/artemis/domain/enumeration/ProgrammingLanguage.java b/src/main/java/de/tum/in/www1/artemis/domain/enumeration/ProgrammingLanguage.java index 3d43ae71fc93..60da52e071f2 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/enumeration/ProgrammingLanguage.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/enumeration/ProgrammingLanguage.java @@ -47,7 +47,8 @@ public enum ProgrammingLanguage { VHDL, ASSEMBLER, SWIFT, - OCAML + OCAML, + RUST ); // @formatter:on diff --git a/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisCompetencyGenerationSession.java b/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisCompetencyGenerationSession.java deleted file mode 100644 index 4353f30a63c1..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/domain/iris/session/IrisCompetencyGenerationSession.java +++ /dev/null @@ -1,51 +0,0 @@ -package de.tum.in.www1.artemis.domain.iris.session; - -import jakarta.persistence.DiscriminatorValue; -import jakarta.persistence.Entity; -import jakarta.persistence.ManyToOne; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; - -import de.tum.in.www1.artemis.domain.Course; -import de.tum.in.www1.artemis.domain.User; - -/** - * A IrisCompetencyGenerationSession is a session specific to a course and user. - * This is used for course editors to generate competency recommendations. - */ -@Entity -@DiscriminatorValue("COMPETENCY_GENERATION") -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public class IrisCompetencyGenerationSession extends IrisSession { - - @ManyToOne - @JsonIgnore - private Course course; - - @ManyToOne - @JsonIgnore - private User user; - - public Course getCourse() { - return course; - } - - public void setCourse(Course course) { - this.course = course; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - @Override - public String toString() { - return "IrisCompetencyGenerationSession{" + "id=" + getId() + ", course=" + (course == null ? "null" : course.getId()) + ", user=" - + (user == null ? "null" : user.getName()) + '}'; - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java b/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java index 16800e0021c7..816d18157234 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/lti/Lti13ClientRegistration.java @@ -37,6 +37,9 @@ public class Lti13ClientRegistration { @JsonProperty("jwks_uri") private String jwksUri; + @JsonProperty("logo_uri") + private String logoUri; + @JsonProperty("token_endpoint_auth_method") private String tokenEndpointAuthMethod; @@ -67,6 +70,7 @@ public Lti13ClientRegistration(String serverUrl, String clientRegistrationId) { this.setRedirectUris(List.of(serverUrl + "/" + CustomLti13Configurer.LTI13_LOGIN_REDIRECT_PROXY_PATH)); this.setInitiateLoginUri(serverUrl + "/" + CustomLti13Configurer.LTI13_LOGIN_INITIATION_PATH + "/" + clientRegistrationId); this.setJwksUri(serverUrl + "/.well-known/jwks.json"); + this.setLogoUri(serverUrl + "/public/images/logo.png"); Lti13ToolConfiguration toolConfiguration = getLti13ToolConfiguration(serverUrl); this.setLti13ToolConfiguration(toolConfiguration); @@ -83,6 +87,7 @@ private static Lti13ToolConfiguration getLti13ToolConfiguration(String serverUrl } toolConfiguration.setDomain(domain); toolConfiguration.setTargetLinkUri(serverUrl + "/courses"); + toolConfiguration.setDescription("Artemis: Interactive Learning with Individual Feedback"); toolConfiguration.setClaims(Arrays.asList("iss", "email", "sub", "name", "given_name", "family_name")); Message deepLinkingMessage = new Message(CustomLti13Configurer.LTI13_DEEPLINK_MESSAGE_REQUEST, serverUrl + "/" + CustomLti13Configurer.LTI13_DEEPLINK_REDIRECT_PATH); toolConfiguration.setMessages(List.of(deepLinkingMessage)); @@ -145,6 +150,14 @@ public void setJwksUri(String jwksUri) { this.jwksUri = jwksUri; } + public String getLogoUri() { + return logoUri; + } + + public void setLogoUri(String logoUri) { + this.logoUri = logoUri; + } + public String getTokenEndpointAuthMethod() { return tokenEndpointAuthMethod; } @@ -179,6 +192,8 @@ public static class Lti13ToolConfiguration { @JsonProperty("target_link_uri") private String targetLinkUri; + private String description; + private List messages; private List claims; @@ -199,6 +214,14 @@ public void setTargetLinkUri(String targetLinkUri) { this.targetLinkUri = targetLinkUri; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public List getMessages() { return messages; } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/notification/NotificationTargetFactory.java b/src/main/java/de/tum/in/www1/artemis/domain/notification/NotificationTargetFactory.java index 86a856dffce2..76f24a8456c4 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/notification/NotificationTargetFactory.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/notification/NotificationTargetFactory.java @@ -339,11 +339,11 @@ public static String extractNotificationUrl(Notification notification, String ba * If the post is not associated with a conversation or messaging is disabled in the course, the URL leads to the communication page. * * @param post which information will be needed to create the URL - * @param baseUrl the prefix (depends on current set up (e.g. "http://localhost:9000/courses")) + * @param baseUrl the prefix (depends on current set up (e.g. "http://localhost:9000")) * @return viable URL to the notification related page */ public static String extractNotificationUrl(Post post, String baseUrl) { - // e.g. http://localhost:8080/courses/1/messages?conversationId=123 - return baseUrl + "/courses/" + post.getConversation().getCourse().getId() + "/messages?conversationId=" + post.getConversation().getId(); + // e.g. http://localhost:8080/courses/1/communication?conversationId=123 + return baseUrl + "/courses/" + post.getConversation().getCourse().getId() + "/communication?conversationId=" + post.getConversation().getId(); } } diff --git a/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java b/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java index 44f21ed0bf25..77279e2a6593 100644 --- a/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java +++ b/src/main/java/de/tum/in/www1/artemis/exception/GitLabCIException.java @@ -1,5 +1,8 @@ package de.tum.in.www1.artemis.exception; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public class GitLabCIException extends ContinuousIntegrationException { public GitLabCIException(String message) { diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java index 470e789cdb27..1187d8c0e11d 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseRepository.java @@ -222,8 +222,6 @@ SELECT COUNT(c) > 0 List findAllByShortName(String shortName); - Optional findById(long courseId); - /** * Returns the title of the course with the given id. * diff --git a/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java index 127e51c9efab..afac757c9ccd 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ExamRepository.java @@ -58,7 +58,7 @@ public interface ExamRepository extends ArtemisJpaRepository { /** * Find all exams for multiple courses that are already visible to the user (either registered, at least tutor or the exam is a test exam) * - * @param courseIds set of courseIds that the exams should be retreived + * @param courseIds set of courseIds that the exams should be retrieved * @param userId the id of the user requesting the exams * @param groupNames the groups of the user requesting the exams * @param now the current date, typically ZonedDateTime.now() diff --git a/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java index b86b9ea3abc3..3924394b81ee 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ExamSessionRepository.java @@ -39,9 +39,9 @@ Set findAllExamSessionsWithTheSameIpAddressAndBrowserFingerprintByE @Param("studentExamId") Long studentExamId, @Param("ipAddress") String ipAddress, @Param("browserFingerprintHash") String browserFingerprintHash); @Query(""" - SELECT es - FROM ExamSession es - WHERE es.studentExam.exam.id = :examId + SELECT es + FROM ExamSession es + WHERE es.studentExam.exam.id = :examId """) Set findAllExamSessionsByExamId(@Param("examId") long examId); } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/PersistenceAuditEventRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/PersistenceAuditEventRepository.java index 815b57746081..172de00d1839 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/PersistenceAuditEventRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/PersistenceAuditEventRepository.java @@ -3,6 +3,7 @@ import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD; import java.time.Instant; +import java.util.Comparator; import java.util.List; import java.util.Optional; @@ -52,6 +53,7 @@ default Page findAllWithDataByAuditEventDateBetween(Instan return Page.empty(pageable); } List result = findWithDataByIdIn(ids); + result.sort(Comparator.comparing(event -> ids.indexOf(event.getId()))); return new PageImpl<>(result, pageable, countByAuditEventDateBetween(fromDate, toDate)); } @@ -73,6 +75,7 @@ default Page findAllWithData(@NotNull Pageable pageable) { return Page.empty(pageable); } List result = findWithDataByIdIn(ids); + result.sort(Comparator.comparing(event -> ids.indexOf(event.getId()))); return new PageImpl<>(result, pageable, count()); } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java index 8cd15a15b573..83c8b716db94 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/StudentParticipationRepository.java @@ -39,6 +39,7 @@ import de.tum.in.www1.artemis.domain.participation.StudentParticipation; import de.tum.in.www1.artemis.domain.quiz.QuizSubmittedAnswerCount; import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository; +import de.tum.in.www1.artemis.web.rest.dto.feedback.FeedbackDetailDTO; /** * Spring Data JPA repository for the Participation entity. @@ -703,18 +704,38 @@ default Page findAllWithEagerSubmissionsAndResultsByExerci """) List findAllWithEagerLegalSubmissionsAndEagerResultsByExerciseId(@Param("exerciseId") long exerciseId); + /** + * Retrieves all distinct `StudentParticipation` entities for a specific exercise, + * including their latest non-illegal submission and the latest rated result for each submission. + * The method fetches related submissions, results, student, and team data to avoid the N+1 select problem. + * + *

+ * The method ensures that: + *

    + *
  • Only participations belonging to the specified exercise are retrieved.
  • + *
  • Participations marked as a test run are excluded.
  • + *
  • Only the latest non-illegal submission for each participation is considered.
  • + *
  • Only the latest rated result for each submission is considered.
  • + *
+ * + * @param exerciseId the ID of the exercise for which to retrieve participations. + * @return a list of distinct `StudentParticipation` entities matching the criteria. + */ @Query(""" SELECT DISTINCT p FROM StudentParticipation p - LEFT JOIN FETCH p.results r - LEFT JOIN FETCH r.submission rs LEFT JOIN FETCH p.submissions s - LEFT JOIN FETCH s.results sr + LEFT JOIN FETCH s.results r + LEFT JOIN FETCH p.student + LEFT JOIN FETCH p.team WHERE p.exercise.id = :exerciseId AND p.testRun = FALSE - AND p.submissions IS NOT EMPTY - AND (s.type <> de.tum.in.www1.artemis.domain.enumeration.SubmissionType.ILLEGAL OR s.type IS NULL) - AND (rs.type <> de.tum.in.www1.artemis.domain.enumeration.SubmissionType.ILLEGAL OR rs.type IS NULL) + AND s.id = (SELECT MAX(s2.id) + FROM p.submissions s2 + WHERE s2.type <> de.tum.in.www1.artemis.domain.enumeration.SubmissionType.ILLEGAL OR s2.type IS NULL) + AND r.id = (SELECT MAX(r2.id) + FROM s.results r2 + WHERE r2.rated = TRUE) """) List findAllForPlagiarism(@Param("exerciseId") long exerciseId); @@ -1210,4 +1231,53 @@ SELECT COALESCE(AVG(p.presentationScore), 0) AND p.presentationScore IS NOT NULL """) double getAvgPresentationScoreByCourseId(@Param("courseId") long courseId); + + /** + * Retrieves aggregated feedback details for a given exercise, including the count of each unique feedback detail text and test case name. + *
+ * The relative count and task number are initially set to 0 and are calculated in a separate step in the service layer. + * + * @param exerciseId Exercise ID. + * @return a list of {@link FeedbackDetailDTO} objects, with the relative count and task number set to 0. + */ + @Query(""" + SELECT new de.tum.in.www1.artemis.web.rest.dto.feedback.FeedbackDetailDTO( + COUNT(f.id), + 0, + f.detailText, + f.testCase.testName, + 0 + ) + FROM StudentParticipation p + JOIN p.results r + JOIN r.feedbacks f + WHERE p.exercise.id = :exerciseId + AND p.testRun = FALSE + AND r.id = ( + SELECT MAX(pr.id) + FROM p.results pr + ) + AND f.positive = FALSE + GROUP BY f.detailText, f.testCase.testName + """) + List findAggregatedFeedbackByExerciseId(@Param("exerciseId") long exerciseId); + + /** + * Counts the distinct number of latest results for a given exercise, excluding those in practice mode. + * + * @param exerciseId Exercise ID. + * @return The count of distinct latest results for the exercise. + */ + @Query(""" + SELECT COUNT(DISTINCT r.id) + FROM StudentParticipation p + JOIN p.results r + WHERE p.exercise.id = :exerciseId + AND p.testRun = FALSE + AND r.id = ( + SELECT MAX(pr.id) + FROM p.results pr + ) + """) + long countDistinctResultsByExerciseId(@Param("exerciseId") long exerciseId); } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java index e659f82db28d..04dd3b8732f2 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java @@ -765,6 +765,17 @@ default Page findAllWithGroupsByIsDeletedIsFalse(Pageable pageable) { """) void updateUserSshPublicKeyHash(@Param("userId") long userId, @Param("sshPublicKeyHash") String sshPublicKeyHash, @Param("sshPublicKey") String sshPublicKey); + @Modifying + @Transactional // ok because of modifying query + @Query(""" + UPDATE User user + SET user.vcsAccessToken = :vcsAccessToken, + user.vcsAccessTokenExpiryDate = :vcsAccessTokenExpiryDate + WHERE user.id = :userId + """) + void updateUserVcsAccessToken(@Param("userId") long userId, @Param("vcsAccessToken") String vcsAccessToken, + @Param("vcsAccessTokenExpiryDate") ZonedDateTime vcsAccessTokenExpiryDate); + @Modifying @Transactional // ok because of modifying query @Query(""" diff --git a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisCompetencyGenerationSessionRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisCompetencyGenerationSessionRepository.java deleted file mode 100644 index 978749785907..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/repository/iris/IrisCompetencyGenerationSessionRepository.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.tum.in.www1.artemis.repository.iris; - -import de.tum.in.www1.artemis.domain.iris.session.IrisCompetencyGenerationSession; -import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository; - -/** - * Repository interface for managing {@link IrisCompetencyGenerationSession} entities. - */ -public interface IrisCompetencyGenerationSessionRepository extends ArtemisJpaRepository { - - /** - * Finds the latest {@link IrisCompetencyGenerationSession} based on its course and user. - * - * @param courseId The ID of the course. - * @param userId The ID of the user. - * - * @return The latest competency generation session - */ - IrisCompetencyGenerationSession findFirstByCourseIdAndUserIdOrderByCreationDateDesc(long courseId, long userId); -} diff --git a/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java b/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java index 6fb44f9e1bec..3fd9d62c03e1 100644 --- a/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java +++ b/src/main/java/de/tum/in/www1/artemis/security/jwt/JWTCookieService.java @@ -18,8 +18,6 @@ @Service public class JWTCookieService { - private static final String CYPRESS_PROFILE = "cypress"; - private static final String DEVELOPMENT_PROFILE = "dev"; private final TokenProvider tokenProvider; @@ -61,9 +59,8 @@ public ResponseCookie buildLogoutCookie() { */ private ResponseCookie buildJWTCookie(String jwt, Duration duration) { - // TODO - Remove cypress workaround once cypress uses https and find a better solution for testing locally in Safari Collection activeProfiles = Arrays.asList(environment.getActiveProfiles()); - boolean isSecure = !activeProfiles.contains(CYPRESS_PROFILE) && !activeProfiles.contains(DEVELOPMENT_PROFILE); + boolean isSecure = !activeProfiles.contains(DEVELOPMENT_PROFILE); return ResponseCookie.from(JWT_COOKIE_NAME, jwt).httpOnly(true) // Must be httpOnly .sameSite("Lax") // Must be Lax to allow navigation links to Artemis to work diff --git a/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java b/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java index 4143a0e00bbe..724a8f98e347 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/AuthorizationCheckService.java @@ -10,8 +10,6 @@ import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotNull; -import javax.annotation.CheckReturnValue; - import org.hibernate.Hibernate; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; @@ -19,6 +17,8 @@ import org.springframework.stereotype.Service; import org.springframework.web.server.ResponseStatusException; +import com.google.errorprone.annotations.CheckReturnValue; + import de.tum.in.www1.artemis.domain.Authority; import de.tum.in.www1.artemis.domain.Course; import de.tum.in.www1.artemis.domain.Exercise; diff --git a/src/main/java/de/tum/in/www1/artemis/service/ResultService.java b/src/main/java/de/tum/in/www1/artemis/service/ResultService.java index 3ec13cb3d5e0..139a3b01b01c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ResultService.java @@ -34,6 +34,7 @@ import de.tum.in.www1.artemis.domain.enumeration.BuildPlanType; import de.tum.in.www1.artemis.domain.enumeration.FeedbackType; import de.tum.in.www1.artemis.domain.exam.Exam; +import de.tum.in.www1.artemis.domain.hestia.ProgrammingExerciseTask; import de.tum.in.www1.artemis.domain.participation.Participation; import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseParticipation; import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation; @@ -49,11 +50,15 @@ import de.tum.in.www1.artemis.repository.ResultRepository; import de.tum.in.www1.artemis.repository.SolutionProgrammingExerciseParticipationRepository; import de.tum.in.www1.artemis.repository.StudentExamRepository; +import de.tum.in.www1.artemis.repository.StudentParticipationRepository; import de.tum.in.www1.artemis.repository.TemplateProgrammingExerciseParticipationRepository; import de.tum.in.www1.artemis.repository.UserRepository; +import de.tum.in.www1.artemis.repository.hestia.ProgrammingExerciseTaskRepository; import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.service.connectors.localci.dto.ResultBuildJob; import de.tum.in.www1.artemis.service.connectors.lti.LtiNewResultService; +import de.tum.in.www1.artemis.service.hestia.ProgrammingExerciseTaskService; +import de.tum.in.www1.artemis.web.rest.dto.feedback.FeedbackDetailDTO; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; import de.tum.in.www1.artemis.web.websocket.ResultWebsocketService; @@ -99,6 +104,10 @@ public class ResultService { private final BuildLogEntryService buildLogEntryService; + private final StudentParticipationRepository studentParticipationRepository; + + private final ProgrammingExerciseTaskService programmingExerciseTaskService; + public ResultService(UserRepository userRepository, ResultRepository resultRepository, Optional ltiNewResultService, ResultWebsocketService resultWebsocketService, ComplaintResponseRepository complaintResponseRepository, RatingRepository ratingRepository, FeedbackRepository feedbackRepository, LongFeedbackTextRepository longFeedbackTextRepository, ComplaintRepository complaintRepository, @@ -106,7 +115,8 @@ public ResultService(UserRepository userRepository, ResultRepository resultRepos TemplateProgrammingExerciseParticipationRepository templateProgrammingExerciseParticipationRepository, SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository, ProgrammingExerciseStudentParticipationRepository programmingExerciseStudentParticipationRepository, StudentExamRepository studentExamRepository, - BuildJobRepository buildJobRepository, BuildLogEntryService buildLogEntryService) { + BuildJobRepository buildJobRepository, BuildLogEntryService buildLogEntryService, StudentParticipationRepository studentParticipationRepository, + ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, ProgrammingExerciseTaskService programmingExerciseTaskService) { this.userRepository = userRepository; this.resultRepository = resultRepository; this.ltiNewResultService = ltiNewResultService; @@ -125,6 +135,8 @@ public ResultService(UserRepository userRepository, ResultRepository resultRepos this.studentExamRepository = studentExamRepository; this.buildJobRepository = buildJobRepository; this.buildLogEntryService = buildLogEntryService; + this.studentParticipationRepository = studentParticipationRepository; + this.programmingExerciseTaskService = programmingExerciseTaskService; } /** @@ -513,4 +525,33 @@ private Result shouldSaveResult(@NotNull Result result, boolean shouldSave) { return result; } } + + /** + * Retrieves aggregated feedback details for a given exercise, calculating relative counts based on the total number of distinct results. + * The task numbers are assigned based on the associated test case names, using the set of tasks fetched from the database. + *
+ * For each feedback detail: + * 1. The relative count is calculated as a percentage of the total number of distinct results for the exercise. + * 2. The task number is determined by matching the test case name with the tasks. + * + * @param exerciseId The ID of the exercise for which feedback details should be retrieved. + * @return A list of FeedbackDetailDTO objects, each containing: + * - feedback count, + * - relative count (as a percentage of distinct results), + * - detail text, + * - test case name, + * - determined task number (based on the test case name). + */ + public List findAggregatedFeedbackByExerciseId(long exerciseId) { + long distinctResultCount = studentParticipationRepository.countDistinctResultsByExerciseId(exerciseId); + Set tasks = programmingExerciseTaskService.getTasksWithUnassignedTestCases(exerciseId); + List feedbackDetails = studentParticipationRepository.findAggregatedFeedbackByExerciseId(exerciseId); + + return feedbackDetails.stream().map(detail -> { + double relativeCount = (detail.count() * 100.0) / distinctResultCount; + int taskNumber = tasks.stream().filter(task -> task.getTestCases().stream().anyMatch(tc -> tc.getTestName().equals(detail.testCaseName()))).findFirst() + .map(task -> tasks.stream().toList().indexOf(task) + 1).orElse(0); + return new FeedbackDetailDTO(detail.count(), relativeCount, detail.detailText(), detail.testCaseName(), taskNumber); + }).toList(); + } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/ContinuousIntegrationService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/ContinuousIntegrationService.java index f22eebc954bf..2cb70653b3d4 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/ContinuousIntegrationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/ci/ContinuousIntegrationService.java @@ -219,8 +219,8 @@ enum RepositoryCheckoutPath implements CustomizableCheckoutPath { @Override public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { - case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY -> "assignment"; - case JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, RUST, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST -> "assignment"; + case JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } @@ -230,9 +230,9 @@ public String forProgrammingLanguage(ProgrammingLanguage language) { @Override public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { - case JAVA, PYTHON, HASKELL, KOTLIN, SWIFT, EMPTY -> ""; + case JAVA, PYTHON, HASKELL, KOTLIN, SWIFT, EMPTY, RUST -> ""; case C, VHDL, ASSEMBLER, OCAML -> "tests"; - case JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, RUST, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java index 4bf736963136..1b27d35bd3dd 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/AbstractGitLabAuthorizationInterceptor.java @@ -10,6 +10,9 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public abstract class AbstractGitLabAuthorizationInterceptor implements ClientHttpRequestInterceptor { private static final String GITLAB_AUTHORIZATION_HEADER_NAME = "Private-Token"; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java index 9a78a348d9d8..2e461a5e1106 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabAuthorizationInterceptor.java @@ -3,6 +3,9 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlab") @Component public class GitLabAuthorizationInterceptor extends AbstractGitLabAuthorizationInterceptor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java index b34cd9c61dba..f217ef874422 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabException.java @@ -2,6 +2,9 @@ import de.tum.in.www1.artemis.exception.VersionControlException; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public class GitLabException extends VersionControlException { public GitLabException() { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java index e5d5fb7b3c4e..e903ffe004f5 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabPersonalAccessTokenManagementService.java @@ -31,6 +31,9 @@ /** * Provides VCS access token services for GitLab via means of personal access tokens. */ +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlab") public class GitLabPersonalAccessTokenManagementService extends VcsTokenManagementService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java index 103a58652025..4cdddb4cf7a1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabService.java @@ -66,6 +66,9 @@ import de.tum.in.www1.artemis.service.connectors.vcs.VersionControlRepositoryPermission; import de.tum.in.www1.artemis.service.util.UrlUtils; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlab") @Service public class GitLabService extends AbstractVersionControlService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java index 298dee6f4e1b..36f65cc51902 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserDoesNotExistException.java @@ -1,5 +1,8 @@ package de.tum.in.www1.artemis.service.connectors.gitlab; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + public class GitLabUserDoesNotExistException extends GitLabException { public GitLabUserDoesNotExistException(String login) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java index 9733c7a131c0..0f8f67198b55 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitLabUserManagementService.java @@ -28,6 +28,9 @@ import de.tum.in.www1.artemis.service.connectors.vcs.VcsTokenManagementService; import de.tum.in.www1.artemis.service.connectors.vcs.VcsUserManagementService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlab") public class GitLabUserManagementService implements VcsUserManagementService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java index b24a6013dc93..951aafa36d8d 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/GitlabInfoContributor.java @@ -11,6 +11,9 @@ import de.tum.in.www1.artemis.config.Constants; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Component @Profile("gitlab") public class GitlabInfoContributor implements InfoContributor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java index 6942e2da6343..d937d570bb45 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabCommitDTO.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabCommitDTO(@JsonProperty("id") String hash, String message, ZonedDateTime timestamp, @JsonProperty("url") String commitUrl, Author author, List added, diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java index 2662aef4d7d7..0ceec1bc02dc 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenListResponseDTO.java @@ -9,6 +9,9 @@ * * @param id The id of the personal access token. */ +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPersonalAccessTokenListResponseDTO(@JsonProperty Long id) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java index 263b15057000..154830c818b3 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenRequestDTO.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPersonalAccessTokenRequestDTO(String name, @JsonProperty("user_id") Long userId, String[] scopes, @JsonProperty("expires_at") Date expiresAt) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java index e8f4333975f7..6920d3db2bee 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPersonalAccessTokenResponseDTO.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPersonalAccessTokenResponseDTO(String name, @JsonProperty("user_id") Long userId, String[] scopes, @JsonProperty("expires_at") Date expiresAt, String token) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java index 605679d176bf..71353195ac21 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabProjectDTO.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabProjectDTO(int id, String name, String description, @JsonProperty("web_url") URL webUrl, @JsonProperty("git_ssh_url") String sshUrl, diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java index 23858285deda..917628db09e8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabPushNotificationDTO.java @@ -8,6 +8,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabPushNotificationDTO(@JsonProperty("object_kind") String triggerType, @JsonProperty("event_name") String eventName, @JsonProperty("before") String previousHash, diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java index f4e84034af8c..245eefed4498 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlab/dto/GitLabRepositoryDTO.java @@ -5,6 +5,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) public record GitLabRepositoryDTO(String name, String url, String description, URL homepage) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java index 79de2d27aa42..258aa2df4269 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIAuthorizationInterceptor.java @@ -5,6 +5,9 @@ import de.tum.in.www1.artemis.service.connectors.gitlab.AbstractGitLabAuthorizationInterceptor; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Component public class GitLabCIAuthorizationInterceptor extends AbstractGitLabAuthorizationInterceptor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java index 80e9dab30be0..23500df008f1 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIBuildPlanService.java @@ -19,6 +19,9 @@ import de.tum.in.www1.artemis.service.ResourceLoaderService; import de.tum.in.www1.artemis.service.connectors.ci.AbstractBuildPlanCreator; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlabci") public class GitLabCIBuildPlanService extends AbstractBuildPlanCreator { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java index 832d100a7585..ee088f9d901a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIInfoContributor.java @@ -10,6 +10,9 @@ import de.tum.in.www1.artemis.config.Constants; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Component @Profile("gitlabci") public class GitLabCIInfoContributor implements InfoContributor { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java index 55ecbb0e447c..661824ea06d8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIProgrammingLanguageFeatureService.java @@ -2,6 +2,7 @@ import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.EMPTY; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.JAVA; +import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.RUST; import static de.tum.in.www1.artemis.domain.enumeration.ProjectType.MAVEN_MAVEN; import static de.tum.in.www1.artemis.domain.enumeration.ProjectType.PLAIN_MAVEN; @@ -13,6 +14,9 @@ import de.tum.in.www1.artemis.service.programming.ProgrammingLanguageFeature; import de.tum.in.www1.artemis.service.programming.ProgrammingLanguageFeatureService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlabci") public class GitLabCIProgrammingLanguageFeatureService extends ProgrammingLanguageFeatureService { @@ -20,5 +24,6 @@ public class GitLabCIProgrammingLanguageFeatureService extends ProgrammingLangua public GitLabCIProgrammingLanguageFeatureService() { programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false, false)); programmingLanguageFeatures.put(JAVA, new ProgrammingLanguageFeature(JAVA, false, false, false, true, false, List.of(PLAIN_MAVEN, MAVEN_MAVEN), false, false)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, false, false, false, List.of(), false, false)); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java index 620e22e09a55..f71e0fe8253b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIResultService.java @@ -25,6 +25,9 @@ import de.tum.in.www1.artemis.service.hestia.TestwiseCoverageService; import de.tum.in.www1.artemis.service.programming.ProgrammingExerciseFeedbackCreationService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Service public class GitLabCIResultService extends AbstractContinuousIntegrationResultService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java index 2c78eb608c4e..45d66cf19272 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIService.java @@ -42,6 +42,9 @@ import de.tum.in.www1.artemis.service.connectors.ci.notification.dto.TestResultsDTO; import de.tum.in.www1.artemis.web.rest.dto.CheckoutDirectoriesDTO; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Service public class GitLabCIService extends AbstractContinuousIntegrationService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java index 72c4fcb6150b..d2fe1c62525f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCITriggerService.java @@ -15,6 +15,9 @@ import de.tum.in.www1.artemis.service.UriService; import de.tum.in.www1.artemis.service.connectors.ci.ContinuousIntegrationTriggerService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Profile("gitlabci") @Service public class GitLabCITriggerService implements ContinuousIntegrationTriggerService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java index f70dd7a82ffa..e15e0dd7038f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/gitlabci/GitLabCIUserManagementService.java @@ -12,6 +12,9 @@ import de.tum.in.www1.artemis.exception.ContinuousIntegrationException; import de.tum.in.www1.artemis.service.connectors.ci.CIUserManagementService; +// Gitlab support will be removed in 8.0.0. Please migrate to LocalVC using e.g. the PR https://github.com/ls1intum/Artemis/pull/8972 +@Deprecated(since = "7.5.0", forRemoval = true) + @Service @Profile("gitlabci") public class GitLabCIUserManagementService implements CIUserManagementService { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsProgrammingLanguageFeatureService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsProgrammingLanguageFeatureService.java index 533e4c709005..d8a4abd0fea3 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/JenkinsProgrammingLanguageFeatureService.java @@ -6,6 +6,7 @@ import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.JAVA; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.KOTLIN; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.PYTHON; +import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.RUST; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.SWIFT; import static de.tum.in.www1.artemis.domain.enumeration.ProjectType.FACT; import static de.tum.in.www1.artemis.domain.enumeration.ProjectType.GCC; @@ -39,5 +40,6 @@ public JenkinsProgrammingLanguageFeatureService() { programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, true, true, true, false, List.of(PLAIN), false, false)); programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, false, true, false, false, List.of(FACT, GCC), false, false)); programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, false, false, false, false, true, List.of(), false, false)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, false, false, false, List.of(), false, false)); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/build_plan/JenkinsBuildPlanService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/build_plan/JenkinsBuildPlanService.java index a97f293e1b53..929a9e1e5750 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/build_plan/JenkinsBuildPlanService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/jenkins/build_plan/JenkinsBuildPlanService.java @@ -184,8 +184,8 @@ private JenkinsXmlConfigBuilder builderFor(ProgrammingLanguage programmingLangua throw new UnsupportedOperationException("Xcode templates are not available for Jenkins."); } return switch (programmingLanguage) { - case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY -> jenkinsBuildPlanCreator; - case VHDL, ASSEMBLER, OCAML, JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, RUST, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY, RUST -> jenkinsBuildPlanCreator; + case VHDL, ASSEMBLER, OCAML, JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException(programmingLanguage + " templates are not available for Jenkins."); }; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIProgrammingLanguageFeatureService.java index 276ab23671fa..101e0540521f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIProgrammingLanguageFeatureService.java @@ -8,6 +8,7 @@ import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.KOTLIN; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.OCAML; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.PYTHON; +import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.RUST; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.SWIFT; import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.VHDL; import static de.tum.in.www1.artemis.domain.enumeration.ProjectType.FACT; @@ -46,5 +47,6 @@ public LocalCIProgrammingLanguageFeatureService() { programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), false, true)); programmingLanguageFeatures.put(OCAML, new ProgrammingLanguageFeature(OCAML, false, false, false, false, true, List.of(), false, true)); programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), false, true)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, false, false, false, List.of(), false, true)); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java index fd7e0cc0dc89..dfc7dcf776c2 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/LocalCIResultProcessingService.java @@ -20,7 +20,6 @@ import com.hazelcast.collection.ItemEvent; import com.hazelcast.collection.ItemListener; import com.hazelcast.core.HazelcastInstance; -import com.hazelcast.cp.lock.FencedLock; import com.hazelcast.map.IMap; import de.tum.in.www1.artemis.domain.BuildJob; @@ -73,8 +72,6 @@ public class LocalCIResultProcessingService { private IMap buildAgentInformation; - private FencedLock resultQueueLock; - private UUID listenerId; public LocalCIResultProcessingService(@Qualifier("hazelcastInstance") HazelcastInstance hazelcastInstance, ProgrammingExerciseGradingService programmingExerciseGradingService, @@ -97,7 +94,6 @@ public LocalCIResultProcessingService(@Qualifier("hazelcastInstance") HazelcastI public void init() { this.resultQueue = this.hazelcastInstance.getQueue("buildResultQueue"); this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); - this.resultQueueLock = this.hazelcastInstance.getCPSubsystem().getLock("resultQueueLock"); this.listenerId = resultQueue.addItemListener(new ResultQueueListener(), true); } @@ -112,9 +108,7 @@ public void removeListener() { public void processResult() { // set lock to prevent multiple nodes from processing the same build job - resultQueueLock.lock(); ResultQueueItem resultQueueItem = resultQueue.poll(); - resultQueueLock.unlock(); if (resultQueueItem == null) { return; diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java index bdaea1f46fd5..939fa41cee19 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/SharedQueueManagementService.java @@ -27,7 +27,6 @@ import com.hazelcast.collection.IQueue; import com.hazelcast.core.HazelcastInstance; -import com.hazelcast.cp.lock.FencedLock; import com.hazelcast.map.IMap; import com.hazelcast.topic.ITopic; @@ -66,11 +65,6 @@ public class SharedQueueManagementService { private IMap dockerImageCleanupInfo; - /** - * Lock to prevent multiple nodes from processing the same build job. - */ - private FencedLock sharedLock; - private ITopic canceledBuildJobsTopic; public SharedQueueManagementService(BuildJobRepository buildJobRepository, @Qualifier("hazelcastInstance") HazelcastInstance hazelcastInstance, ProfileService profileService) { @@ -86,7 +80,6 @@ public SharedQueueManagementService(BuildJobRepository buildJobRepository, @Qual public void init() { this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); this.processingJobs = this.hazelcastInstance.getMap("processingJobs"); - this.sharedLock = this.hazelcastInstance.getCPSubsystem().getLock("buildJobQueueLock"); this.queue = this.hazelcastInstance.getQueue("buildJobQueue"); this.canceledBuildJobsTopic = hazelcastInstance.getTopic("canceledBuildJobsTopic"); this.dockerImageCleanupInfo = this.hazelcastInstance.getMap("dockerImageCleanupInfo"); @@ -148,28 +141,22 @@ public List getBuildAgentInformationWithoutRecentBuildJob * @param buildJobId id of the build job to cancel */ public void cancelBuildJob(String buildJobId) { - sharedLock.lock(); - try { - // Remove build job if it is queued - if (queue.stream().anyMatch(job -> Objects.equals(job.id(), buildJobId))) { - List toRemove = new ArrayList<>(); - for (BuildJobQueueItem job : queue) { - if (Objects.equals(job.id(), buildJobId)) { - toRemove.add(job); - } - } - queue.removeAll(toRemove); - } - else { - // Cancel build job if it is currently being processed - BuildJobQueueItem buildJob = processingJobs.remove(buildJobId); - if (buildJob != null) { - triggerBuildJobCancellation(buildJobId); + // Remove build job if it is queued + if (queue.stream().anyMatch(job -> Objects.equals(job.id(), buildJobId))) { + List toRemove = new ArrayList<>(); + for (BuildJobQueueItem job : queue) { + if (Objects.equals(job.id(), buildJobId)) { + toRemove.add(job); } } + queue.removeAll(toRemove); } - finally { - sharedLock.unlock(); + else { + // Cancel build job if it is currently being processed + BuildJobQueueItem buildJob = processingJobs.remove(buildJobId); + if (buildJob != null) { + triggerBuildJobCancellation(buildJobId); + } } } @@ -188,30 +175,17 @@ private void triggerBuildJobCancellation(String buildJobId) { * Cancel all queued build jobs. */ public void cancelAllQueuedBuildJobs() { - sharedLock.lock(); - try { - log.debug("Cancelling all queued build jobs"); - queue.clear(); - } - finally { - sharedLock.unlock(); - } + log.debug("Cancelling all queued build jobs"); + queue.clear(); } /** * Cancel all running build jobs. */ public void cancelAllRunningBuildJobs() { - sharedLock.lock(); - try { - for (BuildJobQueueItem buildJob : processingJobs.values()) { - cancelBuildJob(buildJob.id()); - } - } - finally { - sharedLock.unlock(); + for (BuildJobQueueItem buildJob : processingJobs.values()) { + cancelBuildJob(buildJob.id()); } - } /** @@ -220,13 +194,7 @@ public void cancelAllRunningBuildJobs() { * @param agentName name of the agent */ public void cancelAllRunningBuildJobsForAgent(String agentName) { - sharedLock.lock(); - try { - processingJobs.values().stream().filter(job -> Objects.equals(job.buildAgentAddress(), agentName)).forEach(job -> cancelBuildJob(job.id())); - } - finally { - sharedLock.unlock(); - } + processingJobs.values().stream().filter(job -> Objects.equals(job.buildAgentAddress(), agentName)).forEach(job -> cancelBuildJob(job.id())); } /** @@ -235,19 +203,13 @@ public void cancelAllRunningBuildJobsForAgent(String agentName) { * @param courseId id of the course */ public void cancelAllQueuedBuildJobsForCourse(long courseId) { - sharedLock.lock(); - try { - List toRemove = new ArrayList<>(); - for (BuildJobQueueItem job : queue) { - if (job.courseId() == courseId) { - toRemove.add(job); - } + List toRemove = new ArrayList<>(); + for (BuildJobQueueItem job : queue) { + if (job.courseId() == courseId) { + toRemove.add(job); } - queue.removeAll(toRemove); - } - finally { - sharedLock.unlock(); } + queue.removeAll(toRemove); } /** @@ -269,25 +231,19 @@ public void cancelAllRunningBuildJobsForCourse(long courseId) { * @param participationId id of the participation */ public void cancelAllJobsForParticipation(long participationId) { - sharedLock.lock(); - try { - List toRemove = new ArrayList<>(); - for (BuildJobQueueItem queuedJob : queue) { - if (queuedJob.participationId() == participationId) { - toRemove.add(queuedJob); - } + List toRemove = new ArrayList<>(); + for (BuildJobQueueItem queuedJob : queue) { + if (queuedJob.participationId() == participationId) { + toRemove.add(queuedJob); } - queue.removeAll(toRemove); + } + queue.removeAll(toRemove); - for (BuildJobQueueItem runningJob : processingJobs.values()) { - if (runningJob.participationId() == participationId) { - cancelBuildJob(runningJob.id()); - } + for (BuildJobQueueItem runningJob : processingJobs.values()) { + if (runningJob.participationId() == participationId) { + cancelBuildJob(runningJob.id()); } } - finally { - sharedLock.unlock(); - } } /** diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/BuildJobExecutionService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/BuildJobExecutionService.java index fcfc9357ce21..305c68a46c9b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/BuildJobExecutionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/BuildJobExecutionService.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.UUID; import jakarta.annotation.Nullable; @@ -36,6 +37,7 @@ import de.tum.in.www1.artemis.domain.VcsRepositoryUri; import de.tum.in.www1.artemis.domain.enumeration.RepositoryType; import de.tum.in.www1.artemis.domain.enumeration.StaticCodeAnalysisTool; +import de.tum.in.www1.artemis.exception.GitException; import de.tum.in.www1.artemis.exception.LocalCIException; import de.tum.in.www1.artemis.service.connectors.localci.dto.BuildJobQueueItem; import de.tum.in.www1.artemis.service.connectors.localci.dto.BuildResult; @@ -64,6 +66,8 @@ public class BuildJobExecutionService { private final BuildLogsMap buildLogsMap; + private static final int MAX_CLONE_RETRIES = 3; + @Value("${artemis.version-control.default-branch:main}") private String defaultBranch; @@ -275,18 +279,19 @@ private BuildResult runScriptAndParseResults(BuildJobQueueItem buildJob, String buildJobContainerService.stopContainer(containerName); // Delete the cloned repositories - deleteCloneRepo(assignmentRepositoryUri, assignmentRepoCommitHash, buildJob.id()); - deleteCloneRepo(testRepositoryUri, assignmentRepoCommitHash, buildJob.id()); + deleteCloneRepo(assignmentRepositoryUri, assignmentRepoCommitHash, buildJob.id(), assignmentRepositoryPath); + deleteCloneRepo(testRepositoryUri, assignmentRepoCommitHash, buildJob.id(), testsRepositoryPath); // do not try to delete the temp repository if it does not exist or is the same as the assignment reposity if (solutionRepositoryUri != null && !Objects.equals(assignmentRepositoryUri.repositorySlug(), solutionRepositoryUri.repositorySlug())) { - deleteCloneRepo(solutionRepositoryUri, assignmentRepoCommitHash, buildJob.id()); + deleteCloneRepo(solutionRepositoryUri, assignmentRepoCommitHash, buildJob.id(), solutionRepositoryPath); } - for (VcsRepositoryUri auxiliaryRepositoryUri : auxiliaryRepositoriesUris) { - deleteCloneRepo(auxiliaryRepositoryUri, assignmentRepoCommitHash, buildJob.id()); + + for (int i = 0; i < auxiliaryRepositoriesUris.length; i++) { + deleteCloneRepo(auxiliaryRepositoriesUris[i], assignmentRepoCommitHash, buildJob.id(), auxiliaryRepositoriesPaths[i]); } try { - FileUtils.deleteDirectory(Path.of(CHECKED_OUT_REPOS_TEMP_DIR, assignmentRepoCommitHash).toFile()); + deleteRepoParentFolder(assignmentRepoCommitHash, assignmentRepositoryPath, testRepoCommitHash, testsRepositoryPath); } catch (IOException e) { msg = "Could not delete " + CHECKED_OUT_REPOS_TEMP_DIR + " directory"; @@ -453,32 +458,52 @@ private BuildResult constructBuildResult(List fai } private Path cloneRepository(VcsRepositoryUri repositoryUri, @Nullable String commitHash, boolean checkout, String buildJobId) { + Repository repository = null; + + for (int attempt = 1; attempt <= MAX_CLONE_RETRIES; attempt++) { + try { + // Generate a random folder name for the repository parent folder if the commit hash is null. This is to avoid conflicts when cloning multiple repositories. + String repositoryParentFolder = commitHash != null ? commitHash : UUID.randomUUID().toString(); + // Clone the assignment repository into a temporary directory + repository = buildJobGitService.cloneRepository(repositoryUri, + Path.of(CHECKED_OUT_REPOS_TEMP_DIR, repositoryParentFolder, repositoryUri.folderNameForRepositoryUri())); + + break; + } + catch (GitAPIException | IOException | URISyntaxException e) { + if (attempt >= MAX_CLONE_RETRIES) { + String msg = "Error while cloning repository " + repositoryUri.repositorySlug() + " with uri " + repositoryUri + " after " + MAX_CLONE_RETRIES + " attempts"; + buildLogsMap.appendBuildLogEntry(buildJobId, msg); + throw new LocalCIException(msg, e); + } + buildLogsMap.appendBuildLogEntry(buildJobId, + "Attempt " + attempt + " to clone repository " + repositoryUri.repositorySlug() + " failed due to " + e.getMessage() + ". Retrying..."); + } + } + try { - // Clone the assignment repository into a temporary directory - // TODO: use a random value if commitHash is null - Repository repository = buildJobGitService.cloneRepository(repositoryUri, Path.of(CHECKED_OUT_REPOS_TEMP_DIR, commitHash, repositoryUri.folderNameForRepositoryUri())); if (checkout && commitHash != null) { // Checkout the commit hash buildJobGitService.checkoutRepositoryAtCommit(repository, commitHash); } + // if repository is not closed, it causes weird IO issues when trying to delete the repository later on // java.io.IOException: Unable to delete file: ...\.git\objects\pack\... repository.closeBeforeDelete(); return repository.getLocalPath(); } - catch (GitAPIException | IOException | URISyntaxException e) { - String msg = "Error while cloning repository " + repositoryUri.repositorySlug() + " with uri " + repositoryUri; + catch (GitException e) { + String msg = "Error while checking out commit " + commitHash + " in repository " + repositoryUri.repositorySlug(); buildLogsMap.appendBuildLogEntry(buildJobId, msg); throw new LocalCIException(msg, e); } } - private void deleteCloneRepo(VcsRepositoryUri repositoryUri, @Nullable String commitHash, String buildJobId) { + private void deleteCloneRepo(VcsRepositoryUri repositoryUri, @Nullable String commitHash, String buildJobId, Path repositoryPath) { String msg; try { - // TODO: handle the case when commitHash is null - Repository repository = buildJobGitService.getExistingCheckedOutRepositoryByLocalPath( - Paths.get(CHECKED_OUT_REPOS_TEMP_DIR, commitHash, repositoryUri.folderNameForRepositoryUri()), repositoryUri, defaultBranch); + Path repositoryPathForDeletion = commitHash != null ? Paths.get(CHECKED_OUT_REPOS_TEMP_DIR, commitHash, repositoryUri.folderNameForRepositoryUri()) : repositoryPath; + Repository repository = buildJobGitService.getExistingCheckedOutRepositoryByLocalPath(repositoryPathForDeletion, repositoryUri, defaultBranch); if (repository == null) { msg = "Repository with commit hash " + commitHash + " not found"; buildLogsMap.appendBuildLogEntry(buildJobId, msg); @@ -497,4 +522,16 @@ private void deleteCloneRepo(VcsRepositoryUri repositoryUri, @Nullable String co throw new LocalCIException(msg, e); } } + + private void deleteRepoParentFolder(String assignmentRepoCommitHash, Path assignmentRepositoryPath, String testRepoCommitHash, Path testsRepositoryPath) throws IOException { + Path assignmentRepo = assignmentRepoCommitHash != null ? Path.of(CHECKED_OUT_REPOS_TEMP_DIR, assignmentRepoCommitHash) + : getRepositoryParentFolderPath(assignmentRepositoryPath); + FileUtils.deleteDirectory(assignmentRepo.toFile()); + Path testRepo = testRepoCommitHash != null ? Path.of(CHECKED_OUT_REPOS_TEMP_DIR, testRepoCommitHash) : getRepositoryParentFolderPath(testsRepositoryPath); + FileUtils.deleteDirectory(testRepo.toFile()); + } + + private Path getRepositoryParentFolderPath(Path repoPath) { + return repoPath.getParent().getParent(); + } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java index 853b89493827..d19d0fae96ba 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/localci/buildagent/SharedQueueProcessingService.java @@ -32,7 +32,6 @@ import com.hazelcast.collection.ItemEvent; import com.hazelcast.collection.ItemListener; import com.hazelcast.core.HazelcastInstance; -import com.hazelcast.cp.lock.FencedLock; import com.hazelcast.map.IMap; import de.tum.in.www1.artemis.domain.BuildLogEntry; @@ -65,11 +64,6 @@ public class SharedQueueProcessingService { private final BuildAgentSshKeyService buildAgentSSHKeyService; - /** - * Lock to prevent multiple nodes from processing the same build job. - */ - private FencedLock sharedLock; - private IQueue queue; private IQueue resultQueue; @@ -104,7 +98,6 @@ public SharedQueueProcessingService(@Qualifier("hazelcastInstance") HazelcastIns public void init() { this.buildAgentInformation = this.hazelcastInstance.getMap("buildAgentInformation"); this.processingJobs = this.hazelcastInstance.getMap("processingJobs"); - this.sharedLock = this.hazelcastInstance.getCPSubsystem().getLock("buildJobQueueLock"); this.queue = this.hazelcastInstance.getQueue("buildJobQueue"); this.resultQueue = this.hazelcastInstance.getQueue("buildResultQueue"); this.listenerId = this.queue.addItemListener(new QueuedBuildJobItemListener(), true); @@ -176,14 +169,8 @@ private void checkAvailabilityAndProcessNextBuild() { return; } - // Lock the queue to prevent multiple nodes from processing the same build job - sharedLock.lock(); - try { - buildJob = addToProcessingJobs(); - } - finally { - sharedLock.unlock(); - } + buildJob = addToProcessingJobs(); + processBuild(buildJob); } catch (RejectedExecutionException e) { diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/LtiService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/LtiService.java index bd7b778439a9..0dd67f46a739 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/LtiService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/lti/LtiService.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseCookie; @@ -42,6 +43,9 @@ @Profile("lti") public class LtiService { + @Value("${artemis.lti.trustExternalLTISystems:false}") + private boolean trustExternalLTISystems; + public static final String LTI_GROUP_NAME = "lti"; protected static final List SIMPLE_USER_LIST_AUTHORITY = Collections.singletonList(new SimpleGrantedAuthority(Role.STUDENT.getAuthority())); @@ -105,6 +109,14 @@ public void authenticateLtiUser(String email, String username, String firstName, // 2. Case: Lookup user with the LTI email address and make sure it's not in use if (artemisAuthenticationProvider.getUsernameForEmail(email).isPresent() || userRepository.findOneByEmailIgnoreCase(email).isPresent()) { log.info("User with email {} already exists. Email is already in use.", email); + + if (trustExternalLTISystems) { + log.info("Trusting external LTI system. Authenticating user with email: {}", email); + User user = userRepository.findUserWithGroupsAndAuthoritiesByEmail(email).orElseThrow(); + SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user.getLogin(), user.getPassword(), user.getGrantedAuthorities())); + return; + } + throw new LtiEmailAlreadyInUseException(); } @@ -179,23 +191,16 @@ private void addUserToExerciseGroup(User user, Course course) { * @param response the response to add the JWT cookie to */ public void buildLtiResponse(UriComponentsBuilder uriComponentsBuilder, HttpServletResponse response) { - // TODO SK: why do we logout the user here if it was already activated? - User user = userRepository.getUser(); if (!user.getActivated()) { - log.info("User is not activated. Adding JWT cookie for activation."); - log.info("Add JWT cookie so the user will be logged in"); - ResponseCookie responseCookie = jwtCookieService.buildLoginCookie(true); - response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString()); - + log.info("User is not activated. Adding initialize parameter to query."); uriComponentsBuilder.queryParam("initialize", ""); } - else { - log.info("User is activated. Adding JWT cookie for logout."); - prepareLogoutCookie(response); - uriComponentsBuilder.queryParam("ltiSuccessLoginRequired", user.getLogin()); - } + + log.info("Add/Update JWT cookie so the user will be logged in."); + ResponseCookie responseCookie = jwtCookieService.buildLoginCookie(true); + response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString()); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisJobService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisJobService.java index 1c9320aed1d0..24dc20bffa65 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisJobService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisJobService.java @@ -2,6 +2,7 @@ import java.security.SecureRandom; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletRequest; @@ -19,6 +20,7 @@ import de.tum.in.www1.artemis.service.connectors.pyris.job.IngestionWebhookJob; import de.tum.in.www1.artemis.service.connectors.pyris.job.PyrisJob; import de.tum.in.www1.artemis.web.rest.errors.AccessForbiddenException; +import de.tum.in.www1.artemis.web.rest.errors.ConflictException; /** * The PyrisJobService class is responsible for managing Pyris jobs in the Artemis system. @@ -58,6 +60,20 @@ public void init() { jobMap = hazelcastInstance.getMap("pyris-job-map"); } + /** + * Creates a token for an arbitrary job, runs the provided function with the token as an argument, + * and stores the job in the job map. + * + * @param tokenToJobFunction the function to run with the token + * @return the generated token + */ + public String createTokenForJob(Function tokenToJobFunction) { + var token = generateJobIdToken(); + var job = tokenToJobFunction.apply(token); + jobMap.put(token, job); + return token; + } + public String addExerciseChatJob(Long courseId, Long exerciseId, Long sessionId) { var token = generateJobIdToken(); var job = new ExerciseChatJob(token, courseId, exerciseId, sessionId); @@ -111,13 +127,15 @@ public PyrisJob getJob(String token) { * 2. Retrieves the PyrisJob object associated with the provided token. * 3. Throws an AccessForbiddenException if the token is invalid or not provided. *

- * The token was previously generated via {@link #addJob(Long, Long, Long)} + * The token was previously generated via {@link #createTokenForJob(Function)} * - * @param request the HttpServletRequest object representing the incoming request + * @param request the HttpServletRequest object representing the incoming request + * @param jobClass the class of the PyrisJob object to cast the retrieved job to + * @param the type of the PyrisJob object * @return the PyrisJob object associated with the token * @throws AccessForbiddenException if the token is invalid or not provided */ - public PyrisJob getAndAuthenticateJobFromHeaderElseThrow(HttpServletRequest request) { + public Job getAndAuthenticateJobFromHeaderElseThrow(HttpServletRequest request, Class jobClass) { var authHeader = request.getHeader("Authorization"); if (!authHeader.startsWith("Bearer ")) { throw new AccessForbiddenException("No valid token provided"); @@ -127,7 +145,10 @@ public PyrisJob getAndAuthenticateJobFromHeaderElseThrow(HttpServletRequest requ if (job == null) { throw new AccessForbiddenException("No valid token provided"); } - return job; + if (!jobClass.isInstance(job)) { + throw new ConflictException("Run ID is not a " + jobClass.getSimpleName(), "Job", "invalidRunId"); + } + return jobClass.cast(job); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisPipelineService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisPipelineService.java index 54c8d2daf5f3..a2fc3f1c3ce4 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisPipelineService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisPipelineService.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,21 +19,19 @@ import de.tum.in.www1.artemis.domain.ProgrammingExercise; import de.tum.in.www1.artemis.domain.ProgrammingSubmission; import de.tum.in.www1.artemis.domain.competency.CompetencyJol; -import de.tum.in.www1.artemis.domain.iris.session.IrisChatSession; import de.tum.in.www1.artemis.domain.iris.session.IrisCourseChatSession; import de.tum.in.www1.artemis.domain.iris.session.IrisExerciseChatSession; import de.tum.in.www1.artemis.domain.participation.StudentParticipation; import de.tum.in.www1.artemis.repository.CourseRepository; import de.tum.in.www1.artemis.repository.StudentParticipationRepository; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisPipelineExecutionDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisPipelineExecutionSettingsDTO; -import de.tum.in.www1.artemis.service.connectors.pyris.dto.chat.PyrisChatPipelineExecutionBaseDataDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.chat.course.PyrisCourseChatPipelineExecutionDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.chat.exercise.PyrisExerciseChatPipelineExecutionDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.data.PyrisCourseDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.data.PyrisExtendedCourseDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.data.PyrisUserDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.status.PyrisStageDTO; -import de.tum.in.www1.artemis.service.connectors.pyris.dto.status.PyrisStageState; import de.tum.in.www1.artemis.service.iris.exception.IrisException; import de.tum.in.www1.artemis.service.iris.websocket.IrisChatWebsocketService; import de.tum.in.www1.artemis.service.metrics.LearningMetricsService; @@ -79,69 +77,48 @@ public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJo } /** - * Executes a chat pipeline for a given chat session subtype. - * This method prepares the execution data, executes the specified pipeline, and handles the state updates. + * Executes a pipeline on Pyris, identified by the given name and variant. + * The pipeline execution is tracked by a unique job token, which must be provided by the caller. + * The caller must additionally provide a mapper function to create the concrete DTO type for this pipeline from the base DTO. + * The status of the pipeline execution is updated via a consumer that accepts a list of stages. This method will + * call the consumer with the initial stages of the pipeline execution. Later stages will be sent back from Pyris, + * and need to be handled in the endpoint that receives the status updates. *

- * The general idea of this being generic is that the pipeline execution is the same for all chat sessions, - * but the specific data required for the pipeline execution is different for each session / pipeline type. - * Therefore, the specific data is provided by a function that accepts the basic chat data and returns the more specific data. * - * @param variant the variant of the pipeline - * @param session the active chat session, must inherit from {@link IrisChatSession} - * @param pipelineName the name of the pipeline to be executed - * @param executionDtoSupplier a function that accepts basic chat data and returns an execution DTO specific to the pipeline being executed - * @param jobTokenSupplier a supplier that provides a unique job token for tracking the pipeline execution - * @param the type of the chat session - * @param the type of the execution DTO + * @param name the name of the pipeline to be executed + * @param variant the variant of the pipeline + * @param jobToken a unique job token for tracking the pipeline execution + * @param dtoMapper a function to create the concrete DTO type for this pipeline from the base DTO + * @param statusUpdater a consumer to update the status of the pipeline execution */ - private void executeChatPipeline(String variant, T session, String pipelineName, - Function executionDtoSupplier, Supplier jobTokenSupplier) { - - // Retrieve the unique job token for this pipeline execution - var jobToken = jobTokenSupplier.get(); - - // Set up initial pipeline execution settings with the server base URL - var settingsDTO = new PyrisPipelineExecutionSettingsDTO(jobToken, List.of(), artemisBaseUrl); - + public void executePipeline(String name, String variant, String jobToken, Function dtoMapper, Consumer> statusUpdater) { // Define the preparation stages of pipeline execution with their initial states // There will be more stages added in Pyris later - var preparingRequestStageInProgress = new PyrisStageDTO("Preparing", 10, PyrisStageState.IN_PROGRESS, null); - var preparingRequestStageDone = new PyrisStageDTO("Preparing", 10, PyrisStageState.DONE, null); - var executingPipelineStageNotStarted = new PyrisStageDTO("Executing pipeline", 30, PyrisStageState.NOT_STARTED, null); + var preparing = new PyrisStageDTO("Preparing", 10, null, null); + var executing = new PyrisStageDTO("Executing pipeline", 30, null, null); // Send initial status update indicating that the preparation stage is in progress - irisChatWebsocketService.sendStatusUpdate(session, List.of(preparingRequestStageInProgress, executingPipelineStageNotStarted)); + statusUpdater.accept(List.of(preparing.inProgress(), executing.notStarted())); - try { - // Prepare the base execution data for the pipeline. - // It is shared among chat pipelines and included as field "base" in the specific execution DTOs. - var base = new PyrisChatPipelineExecutionBaseDataDTO(pyrisDTOService.toPyrisMessageDTOList(session.getMessages()), new PyrisUserDTO(session.getUser()), settingsDTO, - List.of(preparingRequestStageDone) // The initial stage is done when the request arrives at Pyris - ); - - // Prepare the specific execution data for the pipeline - // This is implementation-specific and includes additional data required for the pipeline - // Implementations must deliver the base data, too - U executionDTO = executionDtoSupplier.apply(base); + var baseDto = new PyrisPipelineExecutionDTO(new PyrisPipelineExecutionSettingsDTO(jobToken, List.of(), artemisBaseUrl), List.of(preparing.done())); + var pipelineDto = dtoMapper.apply(baseDto); + try { // Send a status update that preparation is done and pipeline execution is starting - var executingPipelineStageInProgress = new PyrisStageDTO("Executing pipeline", 30, PyrisStageState.IN_PROGRESS, null); - irisChatWebsocketService.sendStatusUpdate(session, List.of(preparingRequestStageDone, executingPipelineStageInProgress)); + statusUpdater.accept(List.of(preparing.done(), executing.inProgress())); try { // Execute the pipeline using the connector service - pyrisConnectorService.executePipeline(pipelineName, variant, executionDTO); + pyrisConnectorService.executePipeline(name, variant, pipelineDto); } catch (PyrisConnectorException | IrisException e) { - log.error("Failed to execute " + pipelineName + " pipeline", e); - var executingPipelineStageFailed = new PyrisStageDTO("Executing pipeline", 30, PyrisStageState.ERROR, "An internal error occurred"); - irisChatWebsocketService.sendStatusUpdate(session, List.of(preparingRequestStageDone, executingPipelineStageFailed)); + log.error("Failed to execute {} pipeline", name, e); + statusUpdater.accept(List.of(preparing.done(), executing.error("An internal error occurred"))); } } catch (Exception e) { - log.error("Failed to prepare " + pipelineName + " pipeline execution", e); - var preparingRequestStageFailed = new PyrisStageDTO("Preparing request", 10, PyrisStageState.ERROR, "An internal error occurred"); - irisChatWebsocketService.sendStatusUpdate(session, List.of(preparingRequestStageFailed, executingPipelineStageNotStarted)); + log.error("Failed to prepare {} pipeline execution", name, e); + statusUpdater.accept(List.of(preparing.error("An internal error occurred"), executing.notStarted())); } } @@ -157,14 +134,29 @@ private void executeChatPipeline(String variant, * @param latestSubmission the latest submission of the student * @param exercise the programming exercise * @param session the chat session - * @see PyrisPipelineService#executeChatPipeline for more details on the pipeline execution process. + * @see PyrisPipelineService#executePipeline for more details on the pipeline execution process. */ public void executeExerciseChatPipeline(String variant, Optional latestSubmission, ProgrammingExercise exercise, IrisExerciseChatSession session) { - executeChatPipeline(variant, session, "tutor-chat", // TODO: Rename this to 'exercise-chat' with next breaking Pyris version - base -> new PyrisExerciseChatPipelineExecutionDTO(latestSubmission.map(pyrisDTOService::toPyrisSubmissionDTO).orElse(null), - pyrisDTOService.toPyrisProgrammingExerciseDTO(exercise), new PyrisCourseDTO(exercise.getCourseViaExerciseGroupOrCourseMember()), base.chatHistory(), - base.user(), base.settings(), base.initialStages()), - () -> pyrisJobService.addExerciseChatJob(exercise.getCourseViaExerciseGroupOrCourseMember().getId(), exercise.getId(), session.getId())); + // @formatter:off + executePipeline( + "tutor-chat", // TODO: Rename this to 'exercise-chat' with next breaking Pyris version + variant, + pyrisJobService.addExerciseChatJob(exercise.getCourseViaExerciseGroupOrCourseMember().getId(), exercise.getId(), session.getId()), + executionDto -> { + var course = exercise.getCourseViaExerciseGroupOrCourseMember(); + return new PyrisExerciseChatPipelineExecutionDTO( + latestSubmission.map(pyrisDTOService::toPyrisSubmissionDTO).orElse(null), + pyrisDTOService.toPyrisProgrammingExerciseDTO(exercise), + new PyrisCourseDTO(course), + pyrisDTOService.toPyrisMessageDTOList(session.getMessages()), + new PyrisUserDTO(session.getUser()), + executionDto.settings(), + executionDto.initialStages() + ); + }, + stages -> irisChatWebsocketService.sendStatusUpdate(session, stages) + ); + // @formatter:on } /** @@ -178,17 +170,31 @@ public void executeExerciseChatPipeline(String variant, Optional { - var fullCourse = loadCourseWithParticipationOfStudent(courseId, studentId); - return new PyrisCourseChatPipelineExecutionDTO(PyrisExtendedCourseDTO.of(fullCourse), - learningMetricsService.getStudentCourseMetrics(session.getUser().getId(), courseId), competencyJol == null ? null : CompetencyJolDTO.of(competencyJol), - base.chatHistory(), base.user(), base.settings(), base.initialStages()); - }, () -> pyrisJobService.addCourseChatJob(courseId, session.getId())); + executePipeline( + "course-chat", + variant, + pyrisJobService.addCourseChatJob(courseId, session.getId()), + executionDto -> { + var fullCourse = loadCourseWithParticipationOfStudent(courseId, studentId); + return new PyrisCourseChatPipelineExecutionDTO( + PyrisExtendedCourseDTO.of(fullCourse), + learningMetricsService.getStudentCourseMetrics(session.getUser().getId(), courseId), + competencyJol == null ? null : CompetencyJolDTO.of(competencyJol), + pyrisDTOService.toPyrisMessageDTOList(session.getMessages()), + new PyrisUserDTO(session.getUser()), + executionDto.settings(), // flatten the execution dto here + executionDto.initialStages() + ); + }, + stages -> irisChatWebsocketService.sendStatusUpdate(session, stages) + ); + // @formatter:on } /** diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisStatusUpdateService.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisStatusUpdateService.java index 759e62ca9e35..0966455b3533 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisStatusUpdateService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/PyrisStatusUpdateService.java @@ -1,17 +1,22 @@ package de.tum.in.www1.artemis.service.connectors.pyris; +import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import de.tum.in.www1.artemis.service.connectors.pyris.dto.chat.PyrisChatStatusUpdateDTO; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.competency.PyrisCompetencyStatusUpdateDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.lectureingestionwebhook.PyrisLectureIngestionStatusUpdateDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.status.PyrisStageDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.status.PyrisStageState; +import de.tum.in.www1.artemis.service.connectors.pyris.job.CompetencyExtractionJob; import de.tum.in.www1.artemis.service.connectors.pyris.job.CourseChatJob; import de.tum.in.www1.artemis.service.connectors.pyris.job.ExerciseChatJob; import de.tum.in.www1.artemis.service.connectors.pyris.job.IngestionWebhookJob; +import de.tum.in.www1.artemis.service.iris.IrisCompetencyGenerationService; import de.tum.in.www1.artemis.service.iris.session.IrisCourseChatSessionService; import de.tum.in.www1.artemis.service.iris.session.IrisExerciseChatSessionService; @@ -25,13 +30,16 @@ public class PyrisStatusUpdateService { private final IrisCourseChatSessionService courseChatSessionService; + private final IrisCompetencyGenerationService competencyGenerationService; + private static final Logger log = LoggerFactory.getLogger(PyrisStatusUpdateService.class); public PyrisStatusUpdateService(PyrisJobService pyrisJobService, IrisExerciseChatSessionService irisExerciseChatSessionService, - IrisCourseChatSessionService courseChatSessionService) { + IrisCourseChatSessionService courseChatSessionService, IrisCompetencyGenerationService competencyGenerationService) { this.pyrisJobService = pyrisJobService; this.irisExerciseChatSessionService = irisExerciseChatSessionService; this.courseChatSessionService = courseChatSessionService; + this.competencyGenerationService = competencyGenerationService; } /** @@ -43,7 +51,7 @@ public PyrisStatusUpdateService(PyrisJobService pyrisJobService, IrisExerciseCha public void handleStatusUpdate(ExerciseChatJob job, PyrisChatStatusUpdateDTO statusUpdate) { irisExerciseChatSessionService.handleStatusUpdate(job, statusUpdate); - removeJobIfTerminated(statusUpdate, job.jobId()); + removeJobIfTerminated(statusUpdate.stages(), job.jobId()); } /** @@ -56,7 +64,20 @@ public void handleStatusUpdate(ExerciseChatJob job, PyrisChatStatusUpdateDTO sta public void handleStatusUpdate(CourseChatJob job, PyrisChatStatusUpdateDTO statusUpdate) { courseChatSessionService.handleStatusUpdate(job, statusUpdate); - removeJobIfTerminated(statusUpdate, job.jobId()); + removeJobIfTerminated(statusUpdate.stages(), job.jobId()); + } + + /** + * Handles the status update of a competency extraction job and forwards it to + * {@link de.tum.in.www1.artemis.service.iris.IrisCompetencyGenerationService#handleStatusUpdate(String, long, PyrisCompetencyStatusUpdateDTO)} + * + * @param job the job that is updated + * @param statusUpdate the status update + */ + public void handleStatusUpdate(CompetencyExtractionJob job, PyrisCompetencyStatusUpdateDTO statusUpdate) { + competencyGenerationService.handleStatusUpdate(job.userLogin(), job.courseId(), statusUpdate); + + removeJobIfTerminated(statusUpdate.stages(), job.jobId()); } /** @@ -66,11 +87,11 @@ public void handleStatusUpdate(CourseChatJob job, PyrisChatStatusUpdateDTO statu * * @see PyrisStageState#isTerminal() * - * @param statusUpdate the status update - * @param job the job to remove + * @param stages the stages of the status update + * @param job the job to remove */ - private void removeJobIfTerminated(PyrisChatStatusUpdateDTO statusUpdate, String job) { - var isDone = statusUpdate.stages().stream().map(PyrisStageDTO::state).allMatch(PyrisStageState::isTerminal); + private void removeJobIfTerminated(List stages, String job) { + var isDone = stages.stream().map(PyrisStageDTO::state).allMatch(PyrisStageState::isTerminal); if (isDone) { pyrisJobService.removeJob(job); } @@ -85,10 +106,6 @@ private void removeJobIfTerminated(PyrisChatStatusUpdateDTO statusUpdate, String */ public void handleStatusUpdate(IngestionWebhookJob job, PyrisLectureIngestionStatusUpdateDTO statusUpdate) { statusUpdate.stages().forEach(stage -> log.info(stage.name() + ":" + stage.message())); - boolean isDone = statusUpdate.stages().stream().map(PyrisStageDTO::state) - .allMatch(state -> state == PyrisStageState.DONE || state == PyrisStageState.ERROR || state == PyrisStageState.SKIPPED); - if (isDone) { - pyrisJobService.removeJob(job.jobId()); - } + removeJobIfTerminated(statusUpdate.stages(), job.jobId()); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisPipelineExecutionDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisPipelineExecutionDTO.java new file mode 100644 index 000000000000..86c5f01c0479 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/PyrisPipelineExecutionDTO.java @@ -0,0 +1,11 @@ +package de.tum.in.www1.artemis.service.connectors.pyris.dto; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.in.www1.artemis.service.connectors.pyris.dto.status.PyrisStageDTO; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record PyrisPipelineExecutionDTO(PyrisPipelineExecutionSettingsDTO settings, List initialStages) { +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java new file mode 100644 index 000000000000..504fe8aa92fa --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java @@ -0,0 +1,7 @@ +package de.tum.in.www1.artemis.service.connectors.pyris.dto.competency; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record PyrisCompetencyExtractionInputDTO(String courseDescription, PyrisCompetencyRecommendationDTO[] currentCompetencies) { +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java new file mode 100644 index 000000000000..64979fa9125d --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java @@ -0,0 +1,20 @@ +package de.tum.in.www1.artemis.service.connectors.pyris.dto.competency; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.in.www1.artemis.domain.competency.CompetencyTaxonomy; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.PyrisPipelineExecutionDTO; + +/** + * DTO to execute the Iris competency extraction pipeline on Pyris + * + * @param execution The pipeline execution details + * @param courseDescription The description of the course + * @param currentCompetencies The current competencies of the course (to avoid re-extraction) + * @param taxonomyOptions The taxonomy options to use + * @param maxN The maximum number of competencies to extract + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record PyrisCompetencyExtractionPipelineExecutionDTO(PyrisPipelineExecutionDTO execution, String courseDescription, PyrisCompetencyRecommendationDTO[] currentCompetencies, + CompetencyTaxonomy[] taxonomyOptions, int maxN) { +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyRecommendationDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyRecommendationDTO.java new file mode 100644 index 000000000000..897b9b7ca3ce --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyRecommendationDTO.java @@ -0,0 +1,17 @@ +package de.tum.in.www1.artemis.service.connectors.pyris.dto.competency; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.in.www1.artemis.domain.competency.CompetencyTaxonomy; + +/** + * DTO for the Iris competency generation feature. + * A competency recommendation is just a title, description and taxonomy generated by Iris. + * + * @param title The title of the competency + * @param description The description of the competency + * @param taxonomy The taxonomy of the competency + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record PyrisCompetencyRecommendationDTO(String title, String description, CompetencyTaxonomy taxonomy) { +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyStatusUpdateDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyStatusUpdateDTO.java new file mode 100644 index 000000000000..967dfcd51906 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/competency/PyrisCompetencyStatusUpdateDTO.java @@ -0,0 +1,19 @@ +package de.tum.in.www1.artemis.service.connectors.pyris.dto.competency; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.in.www1.artemis.service.connectors.pyris.dto.status.PyrisStageDTO; + +/** + * DTO for the Iris competency generation feature. + * Pyris sends callback updates back to Artemis during generation of competencies, + * which are then forwarded to the user via Websockets. + * + * @param stages List of stages of the generation process + * @param result List of competencies recommendations that have been generated so far + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record PyrisCompetencyStatusUpdateDTO(List stages, List result) { +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageDTO.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageDTO.java index 39728eceb4a1..3bc3b2f9758a 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageDTO.java @@ -4,4 +4,25 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisStageDTO(String name, int weight, PyrisStageState state, String message) { + + public PyrisStageDTO notStarted() { + return new PyrisStageDTO(name, weight, PyrisStageState.NOT_STARTED, message); + } + + public PyrisStageDTO inProgress() { + return new PyrisStageDTO(name, weight, PyrisStageState.IN_PROGRESS, message); + } + + public PyrisStageDTO error(String message) { + return new PyrisStageDTO(name, weight, PyrisStageState.ERROR, message); + } + + public PyrisStageDTO done() { + return new PyrisStageDTO(name, weight, PyrisStageState.DONE, message); + } + + public PyrisStageDTO with(PyrisStageState state, String message) { + return new PyrisStageDTO(name, weight, state, message); + } + } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageState.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageState.java index 10648687f7f2..0f2ace2f7e40 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageState.java +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/dto/status/PyrisStageState.java @@ -5,15 +5,9 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public enum PyrisStageState { - NOT_STARTED(false), IN_PROGRESS(false), DONE(true), SKIPPED(true), ERROR(true); - - private final boolean isTerminal; - - PyrisStageState(boolean isTerminal) { - this.isTerminal = isTerminal; - } + NOT_STARTED, IN_PROGRESS, DONE, SKIPPED, ERROR; public boolean isTerminal() { - return isTerminal; + return this == DONE || this == SKIPPED || this == ERROR; } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/job/CompetencyExtractionJob.java b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/job/CompetencyExtractionJob.java new file mode 100644 index 000000000000..2a76f5a3b072 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/connectors/pyris/job/CompetencyExtractionJob.java @@ -0,0 +1,22 @@ +package de.tum.in.www1.artemis.service.connectors.pyris.job; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.tum.in.www1.artemis.domain.Course; + +/** + * A pyris job that extracts competencies from a course description. + * + * @param jobId the job id + * @param courseId the course in which the competencies are being extracted + * @param userLogin the user login of the user who started the job + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record CompetencyExtractionJob(String jobId, long courseId, String userLogin) implements PyrisJob { + + @Override + public boolean canAccess(Course course) { + return course.getId().equals(courseId); + } + +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java index 97d02ca20df7..22c2cccd6546 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/UserDTO.java @@ -74,6 +74,8 @@ public class UserDTO extends AuditingEntityDTO { private String vcsAccessToken; + private ZonedDateTime vcsAccessTokenExpiryDate; + private String sshPublicKey; private ZonedDateTime irisAccepted; @@ -250,6 +252,14 @@ public void setVcsAccessToken(String vcsAccessToken) { this.vcsAccessToken = vcsAccessToken; } + public void setVcsAccessTokenExpiryDate(ZonedDateTime zoneDateTime) { + this.vcsAccessTokenExpiryDate = zoneDateTime; + } + + public ZonedDateTime getVcsAccessTokenExpiryDate() { + return vcsAccessTokenExpiryDate; + } + public String getSshPublicKey() { return sshPublicKey; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisCompetencyGenerationService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisCompetencyGenerationService.java new file mode 100644 index 000000000000..07be63259336 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisCompetencyGenerationService.java @@ -0,0 +1,71 @@ +package de.tum.in.www1.artemis.service.iris; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import de.tum.in.www1.artemis.domain.Course; +import de.tum.in.www1.artemis.domain.User; +import de.tum.in.www1.artemis.domain.competency.CompetencyTaxonomy; +import de.tum.in.www1.artemis.service.connectors.pyris.PyrisJobService; +import de.tum.in.www1.artemis.service.connectors.pyris.PyrisPipelineService; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.competency.PyrisCompetencyExtractionPipelineExecutionDTO; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.competency.PyrisCompetencyRecommendationDTO; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.competency.PyrisCompetencyStatusUpdateDTO; +import de.tum.in.www1.artemis.service.connectors.pyris.job.CompetencyExtractionJob; +import de.tum.in.www1.artemis.service.iris.websocket.IrisWebsocketService; + +/** + * Service to handle the Competency generation subsytem of Iris. + */ +@Service +@Profile("iris") +public class IrisCompetencyGenerationService { + + private final PyrisPipelineService pyrisPipelineService; + + private final IrisWebsocketService websocketService; + + private final PyrisJobService pyrisJobService; + + public IrisCompetencyGenerationService(PyrisPipelineService pyrisPipelineService, IrisWebsocketService websocketService, PyrisJobService pyrisJobService) { + this.pyrisPipelineService = pyrisPipelineService; + this.websocketService = websocketService; + this.pyrisJobService = pyrisJobService; + } + + /** + * Executes the competency extraction pipeline on Pyris for a given course, user and course description + * + * @param user the user for which the pipeline should be executed + * @param course the course for which the pipeline should be executed + * @param courseDescription the description of the course + * @param currentCompetencies the current competencies of the course (to avoid re-extraction) + */ + public void executeCompetencyExtractionPipeline(User user, Course course, String courseDescription, PyrisCompetencyRecommendationDTO[] currentCompetencies) { + // @formatter:off + pyrisPipelineService.executePipeline( + "competency-extraction", + "default", + pyrisJobService.createTokenForJob(token -> new CompetencyExtractionJob(token, course.getId(), user.getLogin())), + executionDto -> new PyrisCompetencyExtractionPipelineExecutionDTO(executionDto, courseDescription, currentCompetencies, CompetencyTaxonomy.values(), 5), + stages -> websocketService.send(user.getLogin(), websocketTopic(course.getId()), new PyrisCompetencyStatusUpdateDTO(stages, null)) + ); + // @formatter:on + } + + /** + * Takes a status update from Pyris containing a new competency extraction result and sends it to the client via websocket + * + * @param userLogin the login of the user + * @param courseId the id of the course + * @param statusUpdate the status update containing the new competency recommendations + */ + public void handleStatusUpdate(String userLogin, long courseId, PyrisCompetencyStatusUpdateDTO statusUpdate) { + websocketService.send(userLogin, websocketTopic(courseId), statusUpdate); + } + + private static String websocketTopic(long courseId) { + return "competencies/" + courseId; + } + +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java index dded30f4e42e..f455b13979fe 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/IrisSessionService.java @@ -8,14 +8,12 @@ import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.domain.iris.message.IrisMessage; -import de.tum.in.www1.artemis.domain.iris.session.IrisCompetencyGenerationSession; import de.tum.in.www1.artemis.domain.iris.session.IrisCourseChatSession; import de.tum.in.www1.artemis.domain.iris.session.IrisExerciseChatSession; import de.tum.in.www1.artemis.domain.iris.session.IrisHestiaSession; import de.tum.in.www1.artemis.domain.iris.session.IrisSession; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.service.iris.session.IrisChatBasedFeatureInterface; -import de.tum.in.www1.artemis.service.iris.session.IrisCompetencyGenerationSessionService; import de.tum.in.www1.artemis.service.iris.session.IrisCourseChatSessionService; import de.tum.in.www1.artemis.service.iris.session.IrisExerciseChatSessionService; import de.tum.in.www1.artemis.service.iris.session.IrisHestiaSessionService; @@ -38,16 +36,12 @@ public class IrisSessionService { private final IrisHestiaSessionService irisHestiaSessionService; - private final IrisCompetencyGenerationSessionService irisCompetencyGenerationSessionService; - public IrisSessionService(UserRepository userRepository, IrisExerciseChatSessionService irisExerciseChatSessionService, - IrisCourseChatSessionService irisCourseChatSessionService, IrisHestiaSessionService irisHestiaSessionService, - IrisCompetencyGenerationSessionService irisCompetencyGenerationSessionService) { + IrisCourseChatSessionService irisCourseChatSessionService, IrisHestiaSessionService irisHestiaSessionService) { this.userRepository = userRepository; this.irisExerciseChatSessionService = irisExerciseChatSessionService; this.irisCourseChatSessionService = irisCourseChatSessionService; this.irisHestiaSessionService = irisHestiaSessionService; - this.irisCompetencyGenerationSessionService = irisCompetencyGenerationSessionService; } /** @@ -107,7 +101,7 @@ public void requestMessageFromIris(S session) { public void sendOverWebsocket(IrisMessage message, S session) { var wrapper = getIrisSessionSubService(session); if (wrapper.irisSubFeatureInterface instanceof IrisChatBasedFeatureInterface chatWrapper) { - chatWrapper.sendOverWebsocket(message); + chatWrapper.sendOverWebsocket(session, message); } else { throw new BadRequestException("Invalid Iris session type " + message.getSession().getClass().getSimpleName()); @@ -143,8 +137,6 @@ private IrisSubFeatureWrapper getIrisSessionSubServic case IrisExerciseChatSession chatSession -> (IrisSubFeatureWrapper) new IrisSubFeatureWrapper<>(irisExerciseChatSessionService, chatSession); case IrisCourseChatSession courseChatSession -> (IrisSubFeatureWrapper) new IrisSubFeatureWrapper<>(irisCourseChatSessionService, courseChatSession); case IrisHestiaSession hestiaSession -> (IrisSubFeatureWrapper) new IrisSubFeatureWrapper<>(irisHestiaSessionService, hestiaSession); - case IrisCompetencyGenerationSession irisCompetencyGenerationSession -> - (IrisSubFeatureWrapper) new IrisSubFeatureWrapper<>(irisCompetencyGenerationSessionService, irisCompetencyGenerationSession); case null, default -> throw new BadRequestException("Unknown Iris session type " + session.getClass().getSimpleName()); }; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisWebsocketDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisChatWebsocketDTO.java similarity index 84% rename from src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisWebsocketDTO.java rename to src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisChatWebsocketDTO.java index 4c5067bde279..12fa282a3012 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisWebsocketDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisChatWebsocketDTO.java @@ -1,4 +1,4 @@ -package de.tum.in.www1.artemis.service.iris.websocket; +package de.tum.in.www1.artemis.service.iris.dto; import java.util.List; import java.util.Objects; @@ -20,7 +20,7 @@ * @param stages the stages of the Pyris pipeline */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record IrisWebsocketDTO(IrisWebsocketMessageType type, IrisMessage message, IrisRateLimitService.IrisRateLimitInformation rateLimitInfo, List stages, +public record IrisChatWebsocketDTO(IrisWebsocketMessageType type, IrisMessage message, IrisRateLimitService.IrisRateLimitInformation rateLimitInfo, List stages, List suggestions) { /** @@ -31,7 +31,7 @@ public record IrisWebsocketDTO(IrisWebsocketMessageType type, IrisMessage messag * @param rateLimitInfo the rate limit information * @param stages the stages of the Pyris pipeline */ - public IrisWebsocketDTO(@Nullable IrisMessage message, IrisRateLimitService.IrisRateLimitInformation rateLimitInfo, List stages, List suggestions) { + public IrisChatWebsocketDTO(@Nullable IrisMessage message, IrisRateLimitService.IrisRateLimitInformation rateLimitInfo, List stages, List suggestions) { this(determineType(message), message, rateLimitInfo, stages, suggestions); } @@ -62,7 +62,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - IrisWebsocketDTO that = (IrisWebsocketDTO) o; + IrisChatWebsocketDTO that = (IrisChatWebsocketDTO) o; return type == that.type && Objects.equals(message, that.message) && Objects.equals(rateLimitInfo, that.rateLimitInfo) && Objects.equals(stages, that.stages); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java index 2bef37bda870..c98fdb1663a0 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedChatSubSettingsDTO.java @@ -10,6 +10,6 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record IrisCombinedChatSubSettingsDTO(boolean enabled, Integer rateLimit, Integer rateLimitTimeframeHours, @Nullable Set allowedModels, - @Nullable String preferredModel, @Nullable IrisTemplate template) implements IrisCombinedSubSettingsInterface { + @Nullable String preferredModel, @Nullable IrisTemplate template) { } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java index 7e2e399e530e..e924be92e773 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedCompetencyGenerationSubSettingsDTO.java @@ -9,7 +9,6 @@ import de.tum.in.www1.artemis.domain.iris.IrisTemplate; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record IrisCombinedCompetencyGenerationSubSettingsDTO(boolean enabled, @Nullable Set allowedModels, @Nullable String preferredModel, @Nullable IrisTemplate template) - implements IrisCombinedSubSettingsInterface { - +public record IrisCombinedCompetencyGenerationSubSettingsDTO(boolean enabled, @Nullable Set allowedModels, @Nullable String preferredModel, + @Nullable IrisTemplate template) { } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java index 47861b16ebbb..315fb572194c 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedHestiaSubSettingsDTO.java @@ -9,6 +9,5 @@ import de.tum.in.www1.artemis.domain.iris.IrisTemplate; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record IrisCombinedHestiaSubSettingsDTO(boolean enabled, @Nullable Set allowedModels, @Nullable String preferredModel, @Nullable IrisTemplate template) - implements IrisCombinedSubSettingsInterface { +public record IrisCombinedHestiaSubSettingsDTO(boolean enabled, @Nullable Set allowedModels, @Nullable String preferredModel, @Nullable IrisTemplate template) { } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedSubSettingsInterface.java b/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedSubSettingsInterface.java deleted file mode 100644 index 79d23120b4fc..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/dto/IrisCombinedSubSettingsInterface.java +++ /dev/null @@ -1,16 +0,0 @@ -package de.tum.in.www1.artemis.service.iris.dto; - -import java.util.Set; - -import jakarta.annotation.Nullable; - -public interface IrisCombinedSubSettingsInterface { - - boolean enabled(); - - @Nullable - Set allowedModels(); - - @Nullable - String preferredModel(); -} diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatBasedFeatureInterface.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatBasedFeatureInterface.java index db345e973fdb..2001cb7077b8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatBasedFeatureInterface.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisChatBasedFeatureInterface.java @@ -8,9 +8,10 @@ public interface IrisChatBasedFeatureInterface extends Ir /** * Sends a message over the websocket to a specific user * + * @param session that the message belongs to * @param message that should be sent over the websocket */ - void sendOverWebsocket(IrisMessage message); + void sendOverWebsocket(S session, IrisMessage message); /** * Sends a request to Iris to get a message for the given session. diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCompetencyGenerationSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCompetencyGenerationSessionService.java deleted file mode 100644 index d8ec1f901123..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCompetencyGenerationSessionService.java +++ /dev/null @@ -1,154 +0,0 @@ -package de.tum.in.www1.artemis.service.iris.session; - -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.JsonNode; - -import de.tum.in.www1.artemis.domain.Course; -import de.tum.in.www1.artemis.domain.User; -import de.tum.in.www1.artemis.domain.competency.Competency; -import de.tum.in.www1.artemis.domain.competency.CompetencyTaxonomy; -import de.tum.in.www1.artemis.domain.competency.CourseCompetency; -import de.tum.in.www1.artemis.domain.iris.message.IrisMessage; -import de.tum.in.www1.artemis.domain.iris.message.IrisMessageSender; -import de.tum.in.www1.artemis.domain.iris.message.IrisTextMessageContent; -import de.tum.in.www1.artemis.domain.iris.session.IrisCompetencyGenerationSession; -import de.tum.in.www1.artemis.domain.iris.settings.IrisSubSettingsType; -import de.tum.in.www1.artemis.repository.iris.IrisCompetencyGenerationSessionRepository; -import de.tum.in.www1.artemis.repository.iris.IrisMessageRepository; -import de.tum.in.www1.artemis.repository.iris.IrisSessionRepository; -import de.tum.in.www1.artemis.security.Role; -import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.connectors.pyris.PyrisConnectorService; -import de.tum.in.www1.artemis.service.iris.IrisMessageService; -import de.tum.in.www1.artemis.service.iris.settings.IrisSettingsService; - -/** - * Service to handle the Competency generation subsytem of Iris. - */ -@Service -@Profile("iris") -public class IrisCompetencyGenerationSessionService implements IrisButtonBasedFeatureInterface> { - - private static final Logger log = LoggerFactory.getLogger(IrisCompetencyGenerationSessionService.class); - - private final PyrisConnectorService pyrisConnectorService; - - private final IrisSettingsService irisSettingsService; - - private final IrisSessionRepository irisSessionRepository; - - private final AuthorizationCheckService authCheckService; - - private final IrisCompetencyGenerationSessionRepository irisCompetencyGenerationSessionRepository; - - private final IrisMessageService irisMessageService; - - private final IrisMessageRepository irisMessageRepository; - - public IrisCompetencyGenerationSessionService(PyrisConnectorService pyrisConnectorService, IrisSettingsService irisSettingsService, IrisSessionRepository irisSessionRepository, - AuthorizationCheckService authCheckService, IrisCompetencyGenerationSessionRepository irisCompetencyGenerationSessionRepository, IrisMessageService irisMessageService, - IrisMessageRepository irisMessageRepository) { - this.pyrisConnectorService = pyrisConnectorService; - this.irisSettingsService = irisSettingsService; - this.irisSessionRepository = irisSessionRepository; - this.authCheckService = authCheckService; - this.irisCompetencyGenerationSessionRepository = irisCompetencyGenerationSessionRepository; - this.irisMessageService = irisMessageService; - this.irisMessageRepository = irisMessageRepository; - } - - /** - * Creates a new Iris session for the given course and user or gets an existing one from the last hour. - * - * @param course The course to create the session for - * @param user The user to create the session for - * @return The Iris session for the course - */ - public IrisCompetencyGenerationSession getOrCreateSession(Course course, User user) { - var existingSession = irisCompetencyGenerationSessionRepository.findFirstByCourseIdAndUserIdOrderByCreationDateDesc(course.getId(), user.getId()); - // Return the newest session if there is one and it is not older than 1 hour - if (existingSession != null && existingSession.getCreationDate().plusHours(1).isAfter(ZonedDateTime.now())) { - checkHasAccessTo(user, existingSession); - checkIsFeatureActivatedFor(existingSession); - return existingSession; - } - - var irisSession = new IrisCompetencyGenerationSession(); - irisSession.setCourse(course); - irisSession.setUser(user); - checkHasAccessTo(user, irisSession); - checkIsFeatureActivatedFor(irisSession); - irisSession = irisSessionRepository.save(irisSession); - return irisSession; - } - - /** - * Adds a user text message to a given IRIS session - * - * @param session the IRIS session - * @param message the message to add - */ - public void addUserTextMessageToSession(IrisCompetencyGenerationSession session, String message) { - var userMessage = new IrisMessage(); - userMessage.setSender(IrisMessageSender.USER); - userMessage.addContent(new IrisTextMessageContent(message)); - irisMessageService.saveMessage(userMessage, session, IrisMessageSender.USER); - } - - // @formatter:off - @JsonInclude(JsonInclude.Include.NON_EMPTY) - record CompetencyGenerationDTO( - String courseDescription, - CompetencyTaxonomy[] taxonomyOptions - ) {} - // @formatter:on - - @Override - public List executeRequest(IrisCompetencyGenerationSession session) { - // TODO: Re-add in a future PR. Remember to reenable the test cases! - return null; - } - - @Override - public void checkHasAccessTo(User user, IrisCompetencyGenerationSession irisSession) { - authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, irisSession.getCourse(), user); - } - - @Override - public void checkIsFeatureActivatedFor(IrisCompetencyGenerationSession irisSession) { - irisSettingsService.isEnabledForElseThrow(IrisSubSettingsType.COMPETENCY_GENERATION, irisSession.getCourse()); - } - - private List toCompetencies(JsonNode content) { - List competencies = new ArrayList<>(); - for (JsonNode node : content.get("competencies")) { - try { - Competency competency = new Competency(); - competency.setTitle(node.required("title").asText()); - - // skip competency if IRIS only replied with a title containing the special response "!done!" - if (node.get("description") == null && node.get("title").asText().equals("!done!")) { - log.info("Received special response \"!done!\", skipping parsing of competency."); - continue; - } - competency.setDescription(node.required("description").asText()); - competency.setTaxonomy(CompetencyTaxonomy.valueOf(node.required("taxonomy").asText())); - - competencies.add(competency); - } - catch (IllegalArgumentException e) { - log.error("Missing fields, could not parse Competency: " + node.toPrettyString(), e); - } - } - return competencies; - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java index 134e47b2abc4..bd5a4c25887b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisCourseChatSessionService.java @@ -97,8 +97,8 @@ public void checkIsFeatureActivatedFor(IrisCourseChatSession session) { } @Override - public void sendOverWebsocket(IrisMessage message) { - irisChatWebsocketService.sendMessage(message); + public void sendOverWebsocket(IrisCourseChatSession session, IrisMessage message) { + irisChatWebsocketService.sendMessage(session, message, null); } @Override @@ -135,7 +135,7 @@ public void handleStatusUpdate(CourseChatJob job, PyrisChatStatusUpdateDTO statu var message = new IrisMessage(); message.addContent(new IrisTextMessageContent(statusUpdate.result())); var savedMessage = irisMessageService.saveMessage(message, session, IrisMessageSender.LLM); - irisChatWebsocketService.sendMessage(savedMessage, statusUpdate.stages()); + irisChatWebsocketService.sendMessage(session, savedMessage, statusUpdate.stages()); } else { irisChatWebsocketService.sendStatusUpdate(session, statusUpdate.stages(), statusUpdate.suggestions()); diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java index 2916a34171ab..36508d8b0934 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/session/IrisExerciseChatSessionService.java @@ -119,8 +119,8 @@ public void checkIsFeatureActivatedFor(IrisExerciseChatSession session) { } @Override - public void sendOverWebsocket(IrisMessage message) { - irisChatWebsocketService.sendMessage(message); + public void sendOverWebsocket(IrisExerciseChatSession session, IrisMessage message) { + irisChatWebsocketService.sendMessage(session, message, null); } @Override @@ -170,7 +170,7 @@ public void handleStatusUpdate(ExerciseChatJob job, PyrisChatStatusUpdateDTO sta var message = new IrisMessage(); message.addContent(new IrisTextMessageContent(statusUpdate.result())); var savedMessage = irisMessageService.saveMessage(message, session, IrisMessageSender.LLM); - irisChatWebsocketService.sendMessage(savedMessage, statusUpdate.stages()); + irisChatWebsocketService.sendMessage(session, savedMessage, statusUpdate.stages()); } else { irisChatWebsocketService.sendStatusUpdate(session, statusUpdate.stages(), statusUpdate.suggestions()); diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisChatWebsocketService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisChatWebsocketService.java index 253ba0df1e45..21bfc4449228 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisChatWebsocketService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisChatWebsocketService.java @@ -5,52 +5,42 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.domain.iris.message.IrisMessage; import de.tum.in.www1.artemis.domain.iris.session.IrisChatSession; -import de.tum.in.www1.artemis.domain.iris.session.IrisSession; -import de.tum.in.www1.artemis.service.WebsocketMessagingService; import de.tum.in.www1.artemis.service.connectors.pyris.dto.status.PyrisStageDTO; import de.tum.in.www1.artemis.service.iris.IrisRateLimitService; +import de.tum.in.www1.artemis.service.iris.dto.IrisChatWebsocketDTO; @Service @Profile("iris") -public class IrisChatWebsocketService extends IrisWebsocketService { +public class IrisChatWebsocketService { + + private final IrisWebsocketService websocketService; private final IrisRateLimitService rateLimitService; - public IrisChatWebsocketService(WebsocketMessagingService websocketMessagingService, IrisRateLimitService rateLimitService) { - super(websocketMessagingService); + public IrisChatWebsocketService(IrisWebsocketService websocketService, IrisRateLimitService rateLimitService) { + this.websocketService = websocketService; this.rateLimitService = rateLimitService; } - private User checkSessionTypeAndGetUser(IrisSession irisSession) { - if (!(irisSession instanceof IrisChatSession chatSession)) { - throw new UnsupportedOperationException("Only IrisChatSessions are supported"); - } - return chatSession.getUser(); - } - /** - * Sends a message over the websocket to a specific user + * Sends a message and/or a status update over the websocket to the user + * involved in the session. At least one of the message or the stages must be + * non-null, otherwise there is no need to send a message. + * This is currently used for both the exercise and course chat sessions, but + * this could be split up in the future. * + * @param session the session to send the message to * @param irisMessage that should be sent over the websocket * @param stages that should be sent over the websocket */ - public void sendMessage(IrisMessage irisMessage, List stages) { - var session = irisMessage.getSession(); - var user = checkSessionTypeAndGetUser(session); + public void sendMessage(IrisChatSession session, IrisMessage irisMessage, List stages) { + var user = session.getUser(); var rateLimitInfo = rateLimitService.getRateLimitInformation(user); - super.send(user, session.getId(), new IrisWebsocketDTO(irisMessage, rateLimitInfo, stages, null)); - } - - /** - * Sends a message over the websocket to a specific user without stages and suggestions - * - * @param message that should be sent over the websocket - */ - public void sendMessage(IrisMessage message) { - sendMessage(message, null); + var topic = "" + session.getId(); // Todo: add more specific topic + var payload = new IrisChatWebsocketDTO(irisMessage, rateLimitInfo, stages, null); + websocketService.send(user.getLogin(), topic, payload); } /** @@ -59,7 +49,7 @@ public void sendMessage(IrisMessage message) { * @param session the session to send the status update to * @param stages the stages to send */ - public void sendStatusUpdate(IrisSession session, List stages) { + public void sendStatusUpdate(IrisChatSession session, List stages) { this.sendStatusUpdate(session, stages, null); } @@ -70,8 +60,11 @@ public void sendStatusUpdate(IrisSession session, List stages) { * @param stages the stages to send * @param suggestions the suggestions to send */ - public void sendStatusUpdate(IrisSession session, List stages, List suggestions) { - var user = checkSessionTypeAndGetUser(session); - super.send(user, session.getId(), new IrisWebsocketDTO(null, rateLimitService.getRateLimitInformation(user), stages, suggestions)); + public void sendStatusUpdate(IrisChatSession session, List stages, List suggestions) { + var user = session.getUser(); + var rateLimitInfo = rateLimitService.getRateLimitInformation(user); + var topic = "" + session.getId(); // Todo: add more specific topic + var payload = new IrisChatWebsocketDTO(null, rateLimitInfo, stages, suggestions); + websocketService.send(user.getLogin(), topic, payload); } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisWebsocketService.java b/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisWebsocketService.java index 537d9f31ec76..3fcd949be7b8 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisWebsocketService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/iris/websocket/IrisWebsocketService.java @@ -1,19 +1,24 @@ package de.tum.in.www1.artemis.service.iris.websocket; +import java.util.concurrent.ExecutionException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; -import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.service.WebsocketMessagingService; /** * A service to send a message over the websocket to a specific user */ -public abstract class IrisWebsocketService { +@Service +@Profile("iris") +public class IrisWebsocketService { private static final Logger log = LoggerFactory.getLogger(IrisWebsocketService.class); - private static final String IRIS_WEBSOCKET_TOPIC_PREFIX = "/topic/iris"; + private static final String TOPIC_PREFIX = "/topic/iris/"; private final WebsocketMessagingService websocketMessagingService; @@ -21,10 +26,22 @@ public IrisWebsocketService(WebsocketMessagingService websocketMessagingService) this.websocketMessagingService = websocketMessagingService; } - protected void send(User user, Long sessionId, Object payload) { - String irisWebsocketTopic = String.format("%s/%s", IRIS_WEBSOCKET_TOPIC_PREFIX, sessionId); - log.debug("Sending message to user {} on topic {}: {}", user.getLogin(), irisWebsocketTopic, payload); - websocketMessagingService.sendMessageToUser(user.getLogin(), irisWebsocketTopic, payload); + /** + * Sends a message over the websocket to a specific user + * + * @param userLogin the login of the user + * @param topicSuffix the suffix of the topic, which will be appended to "/topic/iris/" + * @param payload the DTO to send, which will be serialized to JSON + */ + public void send(String userLogin, String topicSuffix, Object payload) { + String topic = TOPIC_PREFIX + topicSuffix; + try { + websocketMessagingService.sendMessageToUser(userLogin, topic, payload).get(); + log.debug("Sent message to Iris user {} on topic {}: {}", userLogin, topic, payload); + } + catch (InterruptedException | ExecutionException e) { + log.error("Error while sending message to Iris user {} on topic {}: {}", userLogin, topic, payload, e); + } } } diff --git a/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java b/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java index 16b674bb748d..68d63b1f49e7 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/plagiarism/ProgrammingPlagiarismDetectionService.java @@ -185,7 +185,7 @@ public File checkPlagiarismWithJPlagReport(long programmingExerciseId, float sim private JPlagResult computeJPlagResult(ProgrammingExercise programmingExercise, float similarityThreshold, int minimumScore, int minimumSize) { long programmingExerciseId = programmingExercise.getId(); final var targetPath = fileService.getTemporaryUniqueSubfolderPath(repoDownloadClonePath, 60); - List participations = filterStudentParticipationsForComparison(programmingExercise, minimumScore); + List participations = findStudentParticipationsForComparison(programmingExercise, minimumScore); log.info("Download repositories for JPlag for programming exercise {} to compare {} participations", programmingExerciseId, participations.size()); if (participations.size() < 2) { @@ -326,8 +326,10 @@ private Language getJPlagProgrammingLanguage(ProgrammingExercise programmingExer * @param minimumScore consider only submissions whose score is greater or equal to this value * @return an unmodifiable list containing the latest text submission for every participation */ - public List filterStudentParticipationsForComparison(ProgrammingExercise programmingExercise, int minimumScore) { + public List findStudentParticipationsForComparison(ProgrammingExercise programmingExercise, int minimumScore) { + long start = System.nanoTime(); var studentParticipations = studentParticipationRepository.findAllForPlagiarism(programmingExercise.getId()); + log.info("findAllForPlagiarism took {}", TimeLogUtil.formatDurationFrom(start)); return studentParticipations.parallelStream().filter(participation -> !participation.isPracticeMode()) .filter(participation -> participation instanceof ProgrammingExerciseStudentParticipation).filter(plagiarismService.filterForStudents()) diff --git a/src/main/java/de/tum/in/www1/artemis/service/programming/TemplateUpgradePolicyService.java b/src/main/java/de/tum/in/www1/artemis/service/programming/TemplateUpgradePolicyService.java index ad6ae2ab5744..a836f81a4982 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/programming/TemplateUpgradePolicyService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/programming/TemplateUpgradePolicyService.java @@ -32,8 +32,8 @@ public TemplateUpgradePolicyService(JavaTemplateUpgradeService javaRepositoryUpg public TemplateUpgradeService getUpgradeService(ProgrammingLanguage programmingLanguage) { return switch (programmingLanguage) { case JAVA -> javaRepositoryUpgradeService; - case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY -> defaultRepositoryUpgradeService; - case JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, RUST, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST -> defaultRepositoryUpgradeService; + case JAVASCRIPT, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + programmingLanguage); }; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/telemetry/TelemetryService.java b/src/main/java/de/tum/in/www1/artemis/service/telemetry/TelemetryService.java new file mode 100644 index 000000000000..7e49653d99d9 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/telemetry/TelemetryService.java @@ -0,0 +1,122 @@ +package de.tum.in.www1.artemis.service.telemetry; + +import static de.tum.in.www1.artemis.config.Constants.PROFILE_SCHEDULING; + +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; + +import de.tum.in.www1.artemis.service.ProfileService; + +@Service +@Profile(PROFILE_SCHEDULING) +public class TelemetryService { + + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public record TelemetryData(String version, String serverUrl, String operator, String contact, List profiles, String adminName) { + } + + private static final Logger log = LoggerFactory.getLogger(TelemetryService.class); + + private final Environment env; + + private final RestTemplate restTemplate; + + private final ProfileService profileService; + + @Value("${artemis.telemetry.enabled}") + public boolean useTelemetry; + + @Value("${artemis.telemetry.sendAdminDetails}") + private boolean sendAdminDetails; + + @Value("${artemis.telemetry.destination}") + private String destination; + + @Value("${artemis.version}") + private String version; + + @Value("${server.url}") + private String serverUrl; + + @Value("${info.operatorName}") + private String operator; + + @Value("${info.operatorAdminName}") + private String operatorAdminName; + + @Value("${info.contact}") + private String contact; + + public TelemetryService(Environment env, RestTemplate restTemplate, ProfileService profileService) { + this.env = env; + this.restTemplate = restTemplate; + this.profileService = profileService; + } + + /** + * Sends telemetry to the server specified in artemis.telemetry.destination. + * This function runs once, at the startup of the application. + * If telemetry is disabled in artemis.telemetry.enabled, no data is sent. + */ + @EventListener(ApplicationReadyEvent.class) + public void sendTelemetry() { + if (!useTelemetry || profileService.isDevActive()) { + return; + } + + log.info("Sending telemetry information"); + try { + sendTelemetryByPostRequest(); + } + catch (JsonProcessingException e) { + log.warn("JsonProcessingException in sendTelemetry.", e); + } + catch (Exception e) { + log.warn("Exception in sendTelemetry, with dst URI: {}", destination, e); + } + + } + + /** + * Assembles the telemetry data, and sends it to the external telemetry server. + * + * @throws Exception if the writing the telemetry data to a json format fails, or the connection to the telemetry server fails + */ + public void sendTelemetryByPostRequest() throws Exception { + List activeProfiles = Arrays.asList(env.getActiveProfiles()); + TelemetryData telemetryData; + if (sendAdminDetails) { + telemetryData = new TelemetryData(version, serverUrl, operator, contact, activeProfiles, operatorAdminName); + } + else { + telemetryData = new TelemetryData(version, serverUrl, operator, null, activeProfiles, null); + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + ObjectWriter objectWriter = new ObjectMapper().writer().withDefaultPrettyPrinter(); + + var telemetryJson = objectWriter.writeValueAsString(telemetryData); + HttpEntity requestEntity = new HttpEntity<>(telemetryJson, headers); + var response = restTemplate.postForEntity(destination + "/api/telemetry", requestEntity, String.class); + log.info("Successfully sent telemetry data. {}", response.getBody()); + } +} diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java index 29ba05bcd377..5734c258da9a 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/AccountResource.java @@ -2,27 +2,42 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PublicKey; +import java.time.ZonedDateTime; import java.util.Optional; import jakarta.validation.Valid; +import jakarta.ws.rs.BadRequestException; +import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import de.tum.in.www1.artemis.config.icl.ssh.HashUtils; import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.service.AccountService; +import de.tum.in.www1.artemis.service.connectors.localvc.LocalVCPersonalAccessTokenManagementService; import de.tum.in.www1.artemis.service.dto.PasswordChangeDTO; import de.tum.in.www1.artemis.service.dto.UserDTO; import de.tum.in.www1.artemis.service.user.UserCreationService; import de.tum.in.www1.artemis.service.user.UserService; import de.tum.in.www1.artemis.web.rest.errors.AccessForbiddenException; +import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; import de.tum.in.www1.artemis.web.rest.errors.EmailAlreadyUsedException; import de.tum.in.www1.artemis.web.rest.errors.PasswordViolatesRequirementsException; @@ -34,6 +49,11 @@ @RequestMapping("api/") public class AccountResource { + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private static final Logger log = LoggerFactory.getLogger(AccountResource.class); + private final UserRepository userRepository; private final UserService userService; @@ -96,4 +116,120 @@ public ResponseEntity changePassword(@RequestBody PasswordChangeDTO passwo return ResponseEntity.ok().build(); } + + /** + * PUT account/ssh-public-key : sets the ssh public key + * + * @param sshPublicKey the ssh public key to set + * + * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @PutMapping("account/ssh-public-key") + @EnforceAtLeastStudent + public ResponseEntity addSshPublicKey(@RequestBody String sshPublicKey) throws GeneralSecurityException, IOException { + + User user = userRepository.getUser(); + log.debug("REST request to add SSH key to user {}", user.getLogin()); + // Parse the public key string + AuthorizedKeyEntry keyEntry; + try { + keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(sshPublicKey); + } + catch (IllegalArgumentException e) { + throw new BadRequestAlertException("Invalid SSH key format", "SSH key", "invalidKeyFormat", true); + } + // Extract the PublicKey object + PublicKey publicKey = keyEntry.resolvePublicKey(null, null, null); + String keyHash = HashUtils.getSha512Fingerprint(publicKey); + userRepository.updateUserSshPublicKeyHash(user.getId(), keyHash, sshPublicKey); + return ResponseEntity.ok().build(); + } + + /** + * PUT account/ssh-public-key : sets the ssh public key + * + * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @DeleteMapping("account/ssh-public-key") + @EnforceAtLeastStudent + public ResponseEntity deleteSshPublicKey() { + User user = userRepository.getUser(); + log.debug("REST request to remove SSH key of user {}", user.getLogin()); + userRepository.updateUserSshPublicKeyHash(user.getId(), null, null); + + log.debug("Successfully deleted SSH key of user {}", user.getLogin()); + return ResponseEntity.ok().build(); + } + + /** + * PUT account/user-vcs-access-token : creates a vcsAccessToken for a user + * + * @param expiryDate The expiry date which should be set for the token + * @return the ResponseEntity with a userDTO containing the token: with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @PutMapping("account/user-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity createVcsAccessToken(@RequestParam("expiryDate") ZonedDateTime expiryDate) { + User user = userRepository.getUser(); + log.debug("REST request to create a new VCS access token for user {}", user.getLogin()); + if (expiryDate.isBefore(ZonedDateTime.now()) || expiryDate.isAfter(ZonedDateTime.now().plusYears(1))) { + throw new BadRequestException("Invalid expiry date provided"); + } + + userRepository.updateUserVcsAccessToken(user.getId(), LocalVCPersonalAccessTokenManagementService.generateSecureVCSAccessToken(), expiryDate); + log.debug("Successfully created a VCS access token for user {}", user.getLogin()); + user = userRepository.getUser(); + UserDTO userDTO = new UserDTO(); + userDTO.setLogin(user.getLogin()); + userDTO.setVcsAccessToken(user.getVcsAccessToken()); + userDTO.setVcsAccessTokenExpiryDate(user.getVcsAccessTokenExpiryDate()); + return ResponseEntity.ok(userDTO); + } + + /** + * DELETE account/user-vcs-access-token : deletes the vcsAccessToken of a user + * + * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) + */ + @DeleteMapping("account/user-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity deleteVcsAccessToken() { + User user = userRepository.getUser(); + log.debug("REST request to remove VCS access token key of user {}", user.getLogin()); + userRepository.updateUserVcsAccessToken(user.getId(), null, null); + log.debug("Successfully deleted VCS access token of user {}", user.getLogin()); + return ResponseEntity.ok().build(); + } + + /** + * GET account/participation-vcs-access-token : get the vcsToken for of a user for a participation + * + * @param participationId the participation for which the access token should be fetched + * + * @return the versionControlAccessToken belonging to the provided participation and user + */ + @GetMapping("account/participation-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity getVcsAccessToken(@RequestParam("participationId") Long participationId) { + User user = userRepository.getUser(); + + log.debug("REST request to get VCS access token of user {} for participation {}", user.getLogin(), participationId); + return ResponseEntity.ok(userService.getParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); + } + + /** + * PUT account/participation-vcs-access-token : get the vcsToken for of a user for a participation + * + * @param participationId the participation for which the access token should be fetched + * + * @return the versionControlAccessToken belonging to the provided participation and user + */ + @PutMapping("account/participation-vcs-access-token") + @EnforceAtLeastStudent + public ResponseEntity createVcsAccessToken(@RequestParam("participationId") Long participationId) { + User user = userRepository.getUser(); + + log.debug("REST request to create a new VCS access token for user {} for participation {}", user.getLogin(), participationId); + return ResponseEntity.ok(userService.createParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); + } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java index ad959b26096f..8627d8f701ed 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java @@ -69,6 +69,7 @@ import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastTutor; +import de.tum.in.www1.artemis.security.annotations.enforceRoleInCourse.EnforceAtLeastEditorInCourse; import de.tum.in.www1.artemis.service.AuthorizationCheckService; import de.tum.in.www1.artemis.service.FilePathService; import de.tum.in.www1.artemis.service.FileService; @@ -372,6 +373,24 @@ public ResponseEntity getLectureAttachment(@PathVariable Long lectureId, return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), false); } + /** + * GET /files/courses/{courseId}/attachments/{attachmentId} : Returns the file associated with the + * given attachment ID as a downloadable resource + * + * @param courseId The ID of the course that the Attachment belongs to + * @param attachmentId the ID of the attachment to retrieve + * @return ResponseEntity containing the file as a resource + */ + @GetMapping("files/courses/{courseId}/attachments/{attachmentId}") + @EnforceAtLeastEditorInCourse + public ResponseEntity getAttachmentFile(@PathVariable Long courseId, @PathVariable Long attachmentId) { + Attachment attachment = attachmentRepository.findByIdElseThrow(attachmentId); + Course course = courseRepository.findByIdElseThrow(courseId); + checkAttachmentExistsInCourseOrThrow(course, attachment); + + return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), false); + } + /** * GET /files/attachments/lecture/{lectureId}/merge-pdf : Get the lecture units * PDF attachments merged @@ -428,6 +447,26 @@ public ResponseEntity getAttachmentUnitAttachment(@PathVariable Long att return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), false); } + /** + * GET files/courses/{courseId}/attachment-units/{attachmenUnitId} : Returns the file associated with the + * given attachmentUnit ID as a downloadable resource + * + * @param courseId The ID of the course that the Attachment belongs to + * @param attachmentUnitId the ID of the attachment to retrieve + * @return ResponseEntity containing the file as a resource + */ + @GetMapping("files/courses/{courseId}/attachment-units/{attachmentUnitId}") + @EnforceAtLeastEditorInCourse + public ResponseEntity getAttachmentUnitFile(@PathVariable Long courseId, @PathVariable Long attachmentUnitId) { + log.debug("REST request to get file for attachment unit : {}", attachmentUnitId); + AttachmentUnit attachmentUnit = attachmentUnitRepository.findByIdElseThrow(attachmentUnitId); + Course course = courseRepository.findByIdElseThrow(courseId); + Attachment attachment = attachmentUnit.getAttachment(); + checkAttachmentUnitExistsInCourseOrThrow(course, attachmentUnit); + + return buildFileResponse(getActualPathFromPublicPathString(attachment.getLink()), false); + } + /** * GET files/attachments/slides/attachment-unit/:attachmentUnitId/slide/:slideNumber : Get the lecture unit attachment slide by slide number * @@ -557,6 +596,30 @@ private void checkAttachmentAuthorizationOrThrow(Course course, Attachment attac } } + /** + * Checks if the attachment exists in the mentioned course + * + * @param course the course to check if the attachment is part of it + * @param attachment the attachment for which the existence should be checked + */ + private void checkAttachmentExistsInCourseOrThrow(Course course, Attachment attachment) { + if (!attachment.getLecture().getCourse().equals(course)) { + throw new EntityNotFoundException("This attachment does not exist in this course."); + } + } + + /** + * Checks if the attachment exists in the mentioned course + * + * @param course the course to check if the attachment is part of it + * @param attachmentUnit the attachment unit for which the existence should be checked + */ + private void checkAttachmentUnitExistsInCourseOrThrow(Course course, AttachmentUnit attachmentUnit) { + if (!attachmentUnit.getLecture().getCourse().equals(course)) { + throw new EntityNotFoundException("This attachment unit does not exist in this course."); + } + } + /** * Reads the file and turns it into a ResponseEntity * diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/ResultResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/ResultResource.java index 37b9afb94432..bd901ccd824b 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/ResultResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/ResultResource.java @@ -45,12 +45,14 @@ import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastTutor; +import de.tum.in.www1.artemis.security.annotations.enforceRoleInExercise.EnforceAtLeastEditorInExercise; import de.tum.in.www1.artemis.service.AuthorizationCheckService; import de.tum.in.www1.artemis.service.ParticipationAuthorizationCheckService; import de.tum.in.www1.artemis.service.ParticipationService; import de.tum.in.www1.artemis.service.ResultService; import de.tum.in.www1.artemis.service.exam.ExamDateService; import de.tum.in.www1.artemis.web.rest.dto.ResultWithPointsPerGradingCriterionDTO; +import de.tum.in.www1.artemis.web.rest.dto.feedback.FeedbackDetailDTO; import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException; import de.tum.in.www1.artemis.web.rest.util.HeaderUtil; @@ -276,4 +278,18 @@ public ResponseEntity createResultForExternalSubmission(@PathVariable Lo return ResponseEntity.created(new URI("/api/results/" + savedResult.getId())) .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, savedResult.getId().toString())).body(savedResult); } + + /** + * GET /exercises/:exerciseId/feedback-details : Retrieves all aggregated feedback details for a given exercise. + * The feedback details include counts and relative counts of feedback occurrences, along with associated test case names and task numbers. + * + * @param exerciseId The ID of the exercise for which feedback details should be retrieved. + * @return A ResponseEntity containing a list of {@link FeedbackDetailDTO}s + */ + @GetMapping("exercises/{exerciseId}/feedback-details") + @EnforceAtLeastEditorInExercise + public ResponseEntity> getAllFeedbackDetailsForExercise(@PathVariable Long exerciseId) { + log.debug("REST request to get all Feedback details for Exercise {}", exerciseId); + return ResponseEntity.ok(resultService.findAggregatedFeedbackByExerciseId(exerciseId)); + } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java index 747208042837..8415cbc6f104 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/UserResource.java @@ -2,24 +2,18 @@ import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.PublicKey; import java.time.ZonedDateTime; import java.util.List; import java.util.Optional; -import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -29,7 +23,6 @@ import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import de.tum.in.www1.artemis.config.icl.ssh.HashUtils; import de.tum.in.www1.artemis.domain.User; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor; @@ -39,7 +32,6 @@ import de.tum.in.www1.artemis.service.dto.UserInitializationDTO; import de.tum.in.www1.artemis.service.user.UserCreationService; import de.tum.in.www1.artemis.service.user.UserService; -import de.tum.in.www1.artemis.web.rest.util.HeaderUtil; import tech.jhipster.web.util.PaginationUtil; /** @@ -67,9 +59,6 @@ @RequestMapping("api/") public class UserResource { - @Value("${jhipster.clientApp.name}") - private String applicationName; - private static final Logger log = LoggerFactory.getLogger(UserResource.class); private final UserService userService; @@ -180,81 +169,4 @@ public ResponseEntity setIrisAcceptedToTimestamp() { userRepository.updateIrisAcceptedToDate(user.getId(), ZonedDateTime.now()); return ResponseEntity.ok().build(); } - - /** - * PUT users/sshpublickey : sets the ssh public key - * - * @param sshPublicKey the ssh public key to set - * - * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) - */ - @PutMapping("users/sshpublickey") - @EnforceAtLeastStudent - public ResponseEntity addSshPublicKey(@RequestBody String sshPublicKey) throws GeneralSecurityException, IOException { - - User user = userRepository.getUser(); - log.debug("REST request to add SSH key to user {}", user.getLogin()); - // Parse the public key string - AuthorizedKeyEntry keyEntry; - try { - keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(sshPublicKey); - } - catch (IllegalArgumentException e) { - return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(applicationName, true, "sshUserSettings", "saveSshKeyError", "Invalid SSH key format")) - .body(null); - } - // Extract the PublicKey object - PublicKey publicKey = keyEntry.resolvePublicKey(null, null, null); - String keyHash = HashUtils.getSha512Fingerprint(publicKey); - userRepository.updateUserSshPublicKeyHash(user.getId(), keyHash, sshPublicKey); - return ResponseEntity.ok().build(); - } - - /** - * PUT users/sshpublickey : sets the ssh public key - * - * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request) - */ - @DeleteMapping("users/sshpublickey") - @EnforceAtLeastStudent - public ResponseEntity deleteSshPublicKey() { - User user = userRepository.getUser(); - log.debug("REST request to remove SSH key of user {}", user.getLogin()); - userRepository.updateUserSshPublicKeyHash(user.getId(), null, null); - - log.debug("Successfully deleted SSH key of user {}", user.getLogin()); - return ResponseEntity.ok().build(); - } - - /** - * GET users/vcsToken : get the vcsToken for of a user for a participation - * - * @param participationId the participation for which the access token should be fetched - * - * @return the versionControlAccessToken belonging to the provided participation and user - */ - @GetMapping("users/vcsToken") - @EnforceAtLeastStudent - public ResponseEntity getVcsAccessToken(@RequestParam("participationId") Long participationId) { - User user = userRepository.getUser(); - - log.debug("REST request to get VCS access token of user {} for participation {}", user.getLogin(), participationId); - return ResponseEntity.ok(userService.getParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); - } - - /** - * PUT users/vcsToken : get the vcsToken for of a user for a participation - * - * @param participationId the participation for which the access token should be fetched - * - * @return the versionControlAccessToken belonging to the provided participation and user - */ - @PutMapping("users/vcsToken") - @EnforceAtLeastStudent - public ResponseEntity createVcsAccessToken(@RequestParam("participationId") Long participationId) { - User user = userRepository.getUser(); - - log.debug("REST request to create a new VCS access token for user {} for participation {}", user.getLogin(), participationId); - return ResponseEntity.ok(userService.createParticipationVcsAccessTokenForUserAndParticipationIdOrElseThrow(user, participationId).getVcsAccessToken()); - } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/competency/CourseCompetencyResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/competency/CourseCompetencyResource.java index a3fdf0875c31..036b673d1674 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/competency/CourseCompetencyResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/competency/CourseCompetencyResource.java @@ -46,9 +46,10 @@ import de.tum.in.www1.artemis.service.competency.CompetencyProgressService; import de.tum.in.www1.artemis.service.competency.CompetencyRelationService; import de.tum.in.www1.artemis.service.competency.CourseCompetencyService; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.competency.PyrisCompetencyExtractionInputDTO; import de.tum.in.www1.artemis.service.feature.Feature; import de.tum.in.www1.artemis.service.feature.FeatureToggle; -import de.tum.in.www1.artemis.service.iris.session.IrisCompetencyGenerationSessionService; +import de.tum.in.www1.artemis.service.iris.IrisCompetencyGenerationService; import de.tum.in.www1.artemis.web.rest.dto.CourseCompetencyProgressDTO; import de.tum.in.www1.artemis.web.rest.dto.SearchResultPageDTO; import de.tum.in.www1.artemis.web.rest.dto.competency.CompetencyJolPairDTO; @@ -80,7 +81,7 @@ public class CourseCompetencyResource { private final CompetencyRelationService competencyRelationService; - private final Optional irisCompetencyGenerationSessionService; + private final Optional irisCompetencyGenerationService; private final CompetencyJolService competencyJolService; @@ -91,7 +92,7 @@ public class CourseCompetencyResource { public CourseCompetencyResource(UserRepository userRepository, CourseCompetencyService courseCompetencyService, CourseCompetencyRepository courseCompetencyRepository, CourseRepository courseRepository, CompetencyProgressService competencyProgressService, CompetencyProgressRepository competencyProgressRepository, CompetencyRelationRepository competencyRelationRepository, CompetencyRelationService competencyRelationService, - Optional irisCompetencyGenerationSessionService, CompetencyJolService competencyJolService, + Optional irisCompetencyGenerationService, CompetencyJolService competencyJolService, AuthorizationCheckService authorizationCheckService) { this.userRepository = userRepository; this.courseCompetencyService = courseCompetencyService; @@ -101,7 +102,7 @@ public CourseCompetencyResource(UserRepository userRepository, CourseCompetencyS this.competencyProgressRepository = competencyProgressRepository; this.competencyRelationRepository = competencyRelationRepository; this.competencyRelationService = competencyRelationService; - this.irisCompetencyGenerationSessionService = irisCompetencyGenerationSessionService; + this.irisCompetencyGenerationService = irisCompetencyGenerationService; this.competencyJolService = competencyJolService; this.authorizationCheckService = authorizationCheckService; } @@ -332,25 +333,25 @@ public ResponseEntity removeCompetencyRelation(@PathVariable long courseId } /** - * POST courses/:courseId/course-competencies/generate-from-description : Generates a list of course competencies from a given course description by using - * IRIS. + * POST courses/:courseId/course-competencies/:competencyId/competencies/generate-from-description + * Generates a list of competencies from a given course description with IRIS. * - * @param courseId the id of the current course - * @param courseDescription the text description of the course - * @return the ResponseEntity with status 200 (OK) and body the generated competencies + * @param courseId the id of the current course + * @param input the course description and current competencies + * @return the ResponseEntity with status 202 (Accepted) */ @PostMapping("courses/{courseId}/course-competencies/generate-from-description") @EnforceAtLeastEditorInCourse - public ResponseEntity> generateCompetenciesFromCourseDescription(@PathVariable Long courseId, @RequestBody String courseDescription) { - var irisService = irisCompetencyGenerationSessionService.orElseThrow(); + public ResponseEntity generateCompetenciesFromCourseDescription(@PathVariable Long courseId, @RequestBody PyrisCompetencyExtractionInputDTO input) { + var competencyGenerationService = irisCompetencyGenerationService.orElseThrow(); var user = userRepository.getUserWithGroupsAndAuthorities(); var course = courseRepository.findByIdElseThrow(courseId); - var session = irisService.getOrCreateSession(course, user); - irisService.addUserTextMessageToSession(session, courseDescription); - var competencies = irisService.executeRequest(session); + // Start the Iris competency generation pipeline for the given course. + // The generated competencies will be sent async over the websocket on the topic /topic/iris/competencies/{courseId} + competencyGenerationService.executeCompetencyExtractionPipeline(user, course, input.courseDescription(), input.currentCompetencies()); - return ResponseEntity.ok(competencies); + return ResponseEntity.accepted().build(); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/feedback/FeedbackDetailDTO.java b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/feedback/FeedbackDetailDTO.java new file mode 100644 index 000000000000..d9e1f86d231d --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/feedback/FeedbackDetailDTO.java @@ -0,0 +1,7 @@ +package de.tum.in.www1.artemis.web.rest.dto.feedback; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record FeedbackDetailDTO(long count, double relativeCount, String detailText, String testCaseName, int taskNumber) { +} diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java index 9480b6e77d79..7950c09cb773 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicAccountResource.java @@ -165,6 +165,7 @@ public ResponseEntity getAccount() { UserDTO userDTO = new UserDTO(user); // we set this value on purpose here: the user can only fetch their own information, make the token available for constructing the token-based clone-URL userDTO.setVcsAccessToken(user.getVcsAccessToken()); + userDTO.setVcsAccessTokenExpiryDate(user.getVcsAccessTokenExpiryDate()); userDTO.setSshPublicKey(user.getSshPublicKey()); log.info("GET /account {} took {}ms", user.getLogin(), System.currentTimeMillis() - start); return ResponseEntity.ok(userDTO); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicPyrisStatusUpdateResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicPyrisStatusUpdateResource.java index d2bf76e78491..f843fb729484 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicPyrisStatusUpdateResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/open/PublicPyrisStatusUpdateResource.java @@ -16,7 +16,9 @@ import de.tum.in.www1.artemis.service.connectors.pyris.PyrisJobService; import de.tum.in.www1.artemis.service.connectors.pyris.PyrisStatusUpdateService; import de.tum.in.www1.artemis.service.connectors.pyris.dto.chat.PyrisChatStatusUpdateDTO; +import de.tum.in.www1.artemis.service.connectors.pyris.dto.competency.PyrisCompetencyStatusUpdateDTO; import de.tum.in.www1.artemis.service.connectors.pyris.dto.lectureingestionwebhook.PyrisLectureIngestionStatusUpdateDTO; +import de.tum.in.www1.artemis.service.connectors.pyris.job.CompetencyExtractionJob; import de.tum.in.www1.artemis.service.connectors.pyris.job.CourseChatJob; import de.tum.in.www1.artemis.service.connectors.pyris.job.ExerciseChatJob; import de.tum.in.www1.artemis.service.connectors.pyris.job.IngestionWebhookJob; @@ -27,7 +29,7 @@ /** * REST controller for providing Pyris access to Artemis internal data and status updates. * All endpoints in this controller use custom token based authentication. - * See {@link PyrisJobService#getAndAuthenticateJobFromHeaderElseThrow(HttpServletRequest)} for more information. + * See {@link PyrisJobService#getAndAuthenticateJobFromHeaderElseThrow(HttpServletRequest, Class)} for more information. */ @RestController @Profile("iris") @@ -58,15 +60,12 @@ public PublicPyrisStatusUpdateResource(PyrisJobService pyrisJobService, PyrisSta @PostMapping("pipelines/tutor-chat/runs/{runId}/status") // TODO: Rename this to 'exercise-chat' with next breaking Pyris version @EnforceNothing public ResponseEntity setStatusOfJob(@PathVariable String runId, @RequestBody PyrisChatStatusUpdateDTO statusUpdateDTO, HttpServletRequest request) { - var job = pyrisJobService.getAndAuthenticateJobFromHeaderElseThrow(request); + var job = pyrisJobService.getAndAuthenticateJobFromHeaderElseThrow(request, ExerciseChatJob.class); if (!Objects.equals(job.jobId(), runId)) { throw new ConflictException("Run ID in URL does not match run ID in request body", "Job", "runIdMismatch"); } - if (!(job instanceof ExerciseChatJob exerciseChatJob)) { - throw new ConflictException("Run ID is not a exercise chat job", "Job", "invalidRunId"); - } - pyrisStatusUpdateService.handleStatusUpdate(exerciseChatJob, statusUpdateDTO); + pyrisStatusUpdateService.handleStatusUpdate(job, statusUpdateDTO); return ResponseEntity.ok().build(); } @@ -86,15 +85,38 @@ public ResponseEntity setStatusOfJob(@PathVariable String runId, @RequestB @PostMapping("pipelines/course-chat/runs/{runId}/status") @EnforceNothing public ResponseEntity setStatusOfCourseChatJob(@PathVariable String runId, @RequestBody PyrisChatStatusUpdateDTO statusUpdateDTO, HttpServletRequest request) { - var job = pyrisJobService.getAndAuthenticateJobFromHeaderElseThrow(request); + var job = pyrisJobService.getAndAuthenticateJobFromHeaderElseThrow(request, CourseChatJob.class); if (!Objects.equals(job.jobId(), runId)) { throw new ConflictException("Run ID in URL does not match run ID in request body", "Job", "runIdMismatch"); } - if (!(job instanceof CourseChatJob courseChatJob)) { - throw new ConflictException("Run ID is not a course chat job", "Job", "invalidRunId"); + + pyrisStatusUpdateService.handleStatusUpdate(job, statusUpdateDTO); + + return ResponseEntity.ok().build(); + } + + /** + * POST public/pyris/pipelines/competency-extraction/runs/:runId/status : Send the competencies extracted from a course description in a status update + *

+ * Uses custom token based authentication. + * + * @param runId the ID of the job + * @param statusUpdateDTO the status update + * @param request the HTTP request + * @throws ConflictException if the run ID in the URL does not match the run ID in the request body + * @throws AccessForbiddenException if the token is invalid + * @return a {@link ResponseEntity} with status {@code 200 (OK)} + */ + @PostMapping("pipelines/competency-extraction/runs/{runId}/status") + @EnforceNothing + public ResponseEntity setCompetencyExtractionJobStatus(@PathVariable String runId, @RequestBody PyrisCompetencyStatusUpdateDTO statusUpdateDTO, + HttpServletRequest request) { + var job = pyrisJobService.getAndAuthenticateJobFromHeaderElseThrow(request, CompetencyExtractionJob.class); + if (!Objects.equals(job.jobId(), runId)) { + throw new ConflictException("Run ID in URL does not match run ID in request body", "Job", "runIdMismatch"); } - pyrisStatusUpdateService.handleStatusUpdate(courseChatJob, statusUpdateDTO); + pyrisStatusUpdateService.handleStatusUpdate(job, statusUpdateDTO); return ResponseEntity.ok().build(); } @@ -112,7 +134,7 @@ public ResponseEntity setStatusOfCourseChatJob(@PathVariable String runId, @PostMapping("webhooks/ingestion/runs/{runId}/status") @EnforceNothing public ResponseEntity setStatusOfIngestionJob(@PathVariable String runId, @RequestBody PyrisLectureIngestionStatusUpdateDTO statusUpdateDTO, HttpServletRequest request) { - PyrisJob job = pyrisJobService.getAndAuthenticateJobFromHeaderElseThrow(request); + PyrisJob job = pyrisJobService.getAndAuthenticateJobFromHeaderElseThrow(request, PyrisJob.class); if (!job.jobId().equals(runId)) { throw new ConflictException("Run ID in URL does not match run ID in request body", "Job", "runIdMismatch"); } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java index 180d2ca8e077..2620f59f6a5d 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/programming/ProgrammingExerciseResource.java @@ -1,6 +1,7 @@ package de.tum.in.www1.artemis.web.rest.programming; import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE; +import static de.tum.in.www1.artemis.config.Constants.PROFILE_THEIA; import java.io.IOException; import java.net.URI; @@ -18,6 +19,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -89,6 +91,7 @@ import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException; import de.tum.in.www1.artemis.web.rest.util.HeaderUtil; import de.tum.in.www1.artemis.web.websocket.dto.ProgrammingExerciseTestCaseStateDTO; +import io.jsonwebtoken.lang.Arrays; /** * REST controller for managing ProgrammingExercise. @@ -151,6 +154,8 @@ public class ProgrammingExerciseResource { private final Optional athenaModuleService; + private final Environment environment; + public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, UserRepository userRepository, AuthorizationCheckService authCheckService, CourseService courseService, Optional continuousIntegrationService, Optional versionControlService, ExerciseService exerciseService, @@ -160,7 +165,8 @@ public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExer GradingCriterionRepository gradingCriterionRepository, CourseRepository courseRepository, GitService gitService, AuxiliaryRepositoryService auxiliaryRepositoryService, SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository, TemplateProgrammingExerciseParticipationRepository templateProgrammingExerciseParticipationRepository, - BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ChannelRepository channelRepository, Optional athenaModuleService) { + BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ChannelRepository channelRepository, Optional athenaModuleService, + Environment environment) { this.programmingExerciseTaskService = programmingExerciseTaskService; this.programmingExerciseRepository = programmingExerciseRepository; this.programmingExerciseTestCaseRepository = programmingExerciseTestCaseRepository; @@ -184,6 +190,7 @@ public ProgrammingExerciseResource(ProgrammingExerciseRepository programmingExer this.buildLogStatisticsEntryRepository = buildLogStatisticsEntryRepository; this.channelRepository = channelRepository; this.athenaModuleService = athenaModuleService; + this.environment = environment; } /** @@ -303,9 +310,26 @@ public ResponseEntity updateProgrammingExercise(@RequestBod updatedProgrammingExercise.getBuildConfig().isTestwiseCoverageEnabled())) { throw new BadRequestAlertException("Testwise coverage enabled flag must not be changed", ENTITY_NAME, "testwiseCoverageCannotChange"); } - if (!Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOnlineEditor()) && !Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOfflineIde())) { - return ResponseEntity.badRequest().headers(HeaderUtil.createAlert(applicationName, - "You need to allow at least one participation mode, the online editor or the offline IDE", "noParticipationModeAllowed")).body(null); + // Check if theia Profile is enabled + if (Arrays.asList(this.environment.getActiveProfiles()).contains(PROFILE_THEIA)) { + // Require 1 / 3 participation modes to be enabled + if (!Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOnlineEditor()) && !Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOfflineIde()) + && !updatedProgrammingExercise.isAllowOnlineIde()) { + throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor, the offline IDE, or the online IDE", ENTITY_NAME, + "noParticipationModeAllowed"); + } + } + else { + // Require 1 / 2 participation modes to be enabled + if (!Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOnlineEditor()) && !Boolean.TRUE.equals(updatedProgrammingExercise.isAllowOfflineIde())) { + throw new BadRequestAlertException("You need to allow at least one participation mode, the online editor or the offline IDE", ENTITY_NAME, + "noParticipationModeAllowed"); + } + } + + // Verify that a theia image is provided when the online IDE is enabled + if (updatedProgrammingExercise.isAllowOnlineIde() && updatedProgrammingExercise.getBuildConfig().getTheiaImage() == null) { + throw new BadRequestAlertException("You need to provide a Theia image when the online IDE is enabled", ENTITY_NAME, "noTheiaImageProvided"); } // Forbid changing the course the exercise belongs to. if (!Objects.equals(programmingExerciseBeforeUpdate.getCourseViaExerciseGroupOrCourseMember().getId(), diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/theia/TheiaConfigurationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/theia/TheiaConfigurationResource.java new file mode 100644 index 000000000000..24a6977afa5b --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/theia/TheiaConfigurationResource.java @@ -0,0 +1,41 @@ +package de.tum.in.www1.artemis.web.rest.theia; + +import static de.tum.in.www1.artemis.config.Constants.PROFILE_THEIA; + +import java.util.Map; + +import org.springframework.context.annotation.Profile; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.tum.in.www1.artemis.config.TheiaConfiguration; +import de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage; +import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor; + +@Profile(PROFILE_THEIA) +@RestController +@RequestMapping("api/theia/") +public class TheiaConfigurationResource { + + private final TheiaConfiguration theiaConfiguration; + + public TheiaConfigurationResource(TheiaConfiguration theiaConfiguration) { + this.theiaConfiguration = theiaConfiguration; + } + + /** + * GET /api/theia/images?language=: Get the images for a specific language + * + * @param language the language for which the images should be retrieved + * @return a map of flavor/name -> image-link + */ + @GetMapping("images") + @EnforceAtLeastInstructor + public ResponseEntity> getImagesForLanguage(@RequestParam("language") ProgrammingLanguage language) { + return ResponseEntity.ok(this.theiaConfiguration.getImagesForLanguage(language)); + } + +} diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index 242f427c6a8e..56e3edc14e78 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -113,10 +113,12 @@ info: - programming_exercise_fail_tour: 'test' - programming_exercise_success_tour: 'test' - tutor_assessment_tour: 'Patterns in Software Engineering' - contact: artemis@xcit.tum.de #default value, can be overridden if needed # Specifies whether text assessment analytics service (TextAssessmentEventResource) is enabled/disabled # default value set to true for development environment text-assessment-analytics-enabled: true + operatorName: Some Artemis Operator # Must be set before starting the application in production. Shown in the about us page and sent to the telemetry service (e.g. the name of the university "Technische Universität München") + operatorAdminName: Admin # Can be set to be shown in the about us page, and to be sent to the telemetry service + contact: admin@uni.de # The admins contact email address, shown in the about us page, and sent to the telemetry service # Eureka configuration eureka: @@ -129,4 +131,19 @@ eureka: # Theia configuration theia: - portal-url: https://theia-test.k8s.ase.cit.tum.de + portal-url: https://theia-test.k8s.ase.cit.tum.de + + images: + java: + Java-17: "ghcr.io/ls1intum/theia/java-17:latest" + Java-Test: "ghcr.io/ls1intum/theia/java-test:latest" + Java-Test2: "ghcr.io/ls1intum/theia/java-test:2" + c: + C: "ghcr.io/ls1intum/theia/c:latest" + +# Telemetry service: disabled for development +artemis: + telemetry: + enabled: false # Disable sending any telemetry information to the telemetry service by setting this to false + sendAdminDetails: false # Include the admins email and name in the telemetry data. Set to false to disable + destination: telemetry.artemis.cit.tum.de diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index 5fb8190cc112..7d86700066e4 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -15,6 +15,12 @@ artemis: push-notification-relay: https://hermes.artemis.cit.tum.de + # Artemis sends the artemis version, the university name, the universities main admin contact, (email + name), the server url, + # and used profiles (gitlab, localVC, ...) to a telemetry collection service. + telemetry: + enabled: true # Disable sending any telemetry information to the telemetry service by setting this to false + sendAdminDetails: true # Include personal identifiable information of the admin, including email and name in the telemetry data + destination: telemetry.artemis.cit.tum.de spring: devtools: @@ -119,8 +125,10 @@ info: - programming_exercise_fail_tour: 'tutorial' - programming_exercise_success_tour: 'tutorial' - tutor_assessment_tour: 'Patterns in Software Engineering' - contact: artemis@xcit.tum.de #default value, can be overridden on the server test-server: false # false --> production, true --> test server, --> empty == local # Specifies whether text assessment analytics service (TextAssessmentEventResource) is enabled/disabled # default value set to false in production text-assessment-analytics-enabled: false + operatorName: # Must be set before starting the application in production. Shown in the about us page and sent to the telemetry service (e.g. university or school) + operatorAdminName: # Can be set before starting the application in production. Shown in the about us page and used by the telemetry service + contact: # The admins contact email address, shown in the about us page, and sent to the telemetry service diff --git a/src/main/resources/config/application-theia.yml b/src/main/resources/config/application-theia.yml index 2b8e1346d008..95b058f66e20 100644 --- a/src/main/resources/config/application-theia.yml +++ b/src/main/resources/config/application-theia.yml @@ -1,2 +1,13 @@ theia: - portal-url: https://your-theia-instance.com + portal-url: https://your-theia-instance.com + + # Theia IDE images available for the different programming languages + images: + # Upper level key is the language category (must match the language key in the programming-exercise configuration) + java: + # Lower level key can be multiple flavors of the image, e.g. version, tag, or additional dependencies + Java-17: "my-registry/my-image:my-tag" + # Add more flavors here (e.g. Java-11, Java-8, etc.) + # Add more languages here (e.g. c, python, etc.) + c: + C: "my-registry/my-image:my-tag" diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index b216a43a2bf6..8c66b009451c 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -87,6 +87,8 @@ artemis: default: "ls1tum/artemis-swift-swiftlint-docker:latest" ocaml: default: "ls1tum/artemis-ocaml-docker:v1" + rust: + default: "ghcr.io/ls1intum/artemis-rust-docker:v0.9.70" management: endpoints: @@ -196,6 +198,9 @@ spring: thread-name-prefix: artemis-scheduling- pool: size: 2 + threads: + virtual: + enabled: true thymeleaf: mode: HTML output: diff --git a/src/main/resources/templates/aeolus/rust/default.sh b/src/main/resources/templates/aeolus/rust/default.sh new file mode 100644 index 000000000000..d1ba23293a58 --- /dev/null +++ b/src/main/resources/templates/aeolus/rust/default.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e +export AEOLUS_INITIAL_DIRECTORY=${PWD} +build () { + echo '⚙️ executing build' + cargo build --verbose +} + +run_all_tests () { + echo '⚙️ executing run_all_tests' + cargo nextest run --profile ci +} + +main () { + if [[ "${1}" == "aeolus_sourcing" ]]; then + return 0 # just source to use the methods in the subshell, no execution + fi + local _script_name + _script_name=${BASH_SOURCE[0]:-$0} + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; build" + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; run_all_tests" +} + +main "${@}" diff --git a/src/main/resources/templates/aeolus/rust/default.yaml b/src/main/resources/templates/aeolus/rust/default.yaml new file mode 100644 index 000000000000..4cd44c110048 --- /dev/null +++ b/src/main/resources/templates/aeolus/rust/default.yaml @@ -0,0 +1,14 @@ +api: v0.0.1 +metadata: + name: Rust + id: rust + description: Test crate using cargo +actions: + - name: build + script: cargo build --verbose + - name: run_all_tests + script: cargo nextest run --profile ci + results: + - name: junit_target/nextest/ci/junit.xml + path: target/nextest/ci/junit.xml + type: junit diff --git a/src/main/resources/templates/gitlabci/rust/regularRuns/.gitlab-ci.yml b/src/main/resources/templates/gitlabci/rust/regularRuns/.gitlab-ci.yml new file mode 100644 index 000000000000..39e416158a9c --- /dev/null +++ b/src/main/resources/templates/gitlabci/rust/regularRuns/.gitlab-ci.yml @@ -0,0 +1,47 @@ +stages: + - test + - upload + + +test-job: + image: ${ARTEMIS_BUILD_DOCKER_IMAGE} + stage: test + only: + variables: + - $CI_COMMIT_BRANCH == $ARTEMIS_SUBMISSION_GIT_BRANCH + allow_failure: true + variables: + GIT_STRATEGY: none + script: + - git clone --branch ${ARTEMIS_TEST_GIT_BRANCH} ${CI_SERVER_PROTOCOL}://${ARTEMIS_TEST_GIT_USER}:${ARTEMIS_TEST_GIT_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/${CI_PROJECT_NAMESPACE}/${ARTEMIS_TEST_GIT_REPOSITORY_SLUG} . + - git clone --branch ${ARTEMIS_SUBMISSION_GIT_BRANCH} ${CI_SERVER_PROTOCOL}://${ARTEMIS_TEST_GIT_USER}:${ARTEMIS_TEST_GIT_TOKEN}@${CI_SERVER_HOST}:${CI_SERVER_PORT}/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME} assignment + - export ARTEMIS_NOTIFICATION_SECRET=[hidden] # Workaround for overwriting the secret + - export ARTEMIS_TEST_GIT_TOKEN=[hidden] + - cargo nextest run --profile ci | tee -a "${ARTEMIS_BUILD_LOGS_FILE}" && echo "ARTEMIS_BUILD_STATUS=success" > .env || echo "ARTEMIS_BUILD_STATUS=failed" > .env + - test -e target/nextest/ci/junit.xml && sed -i 's/]*>//g ; s/<\/testsuites>/<\/testsuite>/g' target/nextest/ci/junit.xml # not supported by notification plugin + after_script: + - echo "ARTEMIS_TEST_GIT_HASH=$(git rev-parse HEAD)" >> .env + - echo "ARTEMIS_SUBMISSION_GIT_HASH=${CI_COMMIT_SHA}" >> .env + - echo "ARTEMIS_SUBMISSION_GIT_REPOSITORY_SLUG=${CI_PROJECT_NAME}" >> .env + artifacts: + paths: + - ${ARTEMIS_BUILD_LOGS_FILE} + - target/nextest/ci/*.xml + reports: + dotenv: .env + + +upload-job: + image: ${ARTEMIS_NOTIFICATION_PLUGIN_DOCKER_IMAGE} + stage: upload + dependencies: + - test-job + only: + variables: + - $CI_COMMIT_BRANCH == $ARTEMIS_SUBMISSION_GIT_BRANCH + variables: + GIT_STRATEGY: none + script: + - cp -r /notification-plugin/* . + - export ARTEMIS_TEST_RESULTS_DIR="target/nextest/ci" # override project variable + - gradle run diff --git a/src/main/resources/templates/jenkins/rust/regularRuns/pipeline.groovy b/src/main/resources/templates/jenkins/rust/regularRuns/pipeline.groovy new file mode 100644 index 000000000000..965d7423ce7c --- /dev/null +++ b/src/main/resources/templates/jenkins/rust/regularRuns/pipeline.groovy @@ -0,0 +1,56 @@ +/* + * This file configures the actual build steps for the automatic grading. + * + * !!! + * For regular exercises, there is no need to make changes to this file. + * Only this base configuration is actively supported by the Artemis maintainers + * and/or your Artemis instance administrators. + * !!! + */ + +dockerImage = '#dockerImage' +dockerFlags = '#dockerArgs' + +/** + * Main function called by Jenkins. + */ +void testRunner() { + docker.image(dockerImage).inside(dockerFlags) { c -> + runTestSteps() + } +} + +private void runTestSteps() { + test() +} + +/** + * Run unit tests + */ +private void test() { + stage('Test') { + sh 'cargo nextest run --profile ci' + } +} + +/** + * Script of the post build tasks aggregating all JUnit files in $WORKSPACE/results. + * + * Called by Jenkins. + */ +void postBuildTasks() { + sh ''' + rm -rf results + mkdir results + if [ -e target/nextest/ci/junit.xml ] + then + sed -i 's/]*>//g ; s/<\\/testsuites>/<\\/testsuite>/g' target/nextest/ci/junit.xml + fi + cp target/nextest/ci/junit.xml $WORKSPACE/results/ || true + sed -i 's/[^[:print:]\t]/�/g' $WORKSPACE/results/*.xml || true + ''' +} + +// very important, do not remove +// required so that Jenkins finds the methods defined in this script +return this diff --git a/src/main/resources/templates/rust/exercise/.gitattributes b/src/main/resources/templates/rust/exercise/.gitattributes new file mode 100644 index 000000000000..2677732ca2e9 --- /dev/null +++ b/src/main/resources/templates/rust/exercise/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf + +*.rs text eol=lf diff=rust +Cargo.toml text eol=lf +Cargo.lock text eol=lf diff --git a/src/main/resources/templates/rust/exercise/.gitignore b/src/main/resources/templates/rust/exercise/.gitignore new file mode 100644 index 000000000000..ea8c4bf7f35f --- /dev/null +++ b/src/main/resources/templates/rust/exercise/.gitignore @@ -0,0 +1 @@ +/target diff --git a/src/main/resources/templates/rust/exercise/Cargo.lock b/src/main/resources/templates/rust/exercise/Cargo.lock new file mode 100644 index 000000000000..2f5f96ba7454 --- /dev/null +++ b/src/main/resources/templates/rust/exercise/Cargo.lock @@ -0,0 +1,100 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust-template-exercise" +version = "0.1.0" +dependencies = [ + "chrono", + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/src/main/resources/templates/rust/exercise/Cargo.toml b/src/main/resources/templates/rust/exercise/Cargo.toml new file mode 100644 index 000000000000..34b160c5368d --- /dev/null +++ b/src/main/resources/templates/rust/exercise/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust-template-exercise" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { version = "0.4.38", default-features = false } +rand = "0.8.5" diff --git a/src/main/resources/templates/rust/exercise/src/bubble_sort.rs b/src/main/resources/templates/rust/exercise/src/bubble_sort.rs new file mode 100644 index 000000000000..0587d2852dcf --- /dev/null +++ b/src/main/resources/templates/rust/exercise/src/bubble_sort.rs @@ -0,0 +1,14 @@ +use chrono::NaiveDate; + +pub struct BubbleSort; + +impl BubbleSort { + /// Sorts items with the Bubble Sort algorithm. + /// + /// Arguments: + /// + /// * `input`: slice of items to be sorted + pub fn perform_sort(&self, input: &mut [NaiveDate]) { + todo!("implement"); + } +} diff --git a/src/main/resources/templates/rust/exercise/src/context.rs b/src/main/resources/templates/rust/exercise/src/context.rs new file mode 100644 index 000000000000..30940f4db7a7 --- /dev/null +++ b/src/main/resources/templates/rust/exercise/src/context.rs @@ -0,0 +1 @@ +// TODO: Create and implement a Context struct according to the UML class diagram diff --git a/src/main/resources/templates/rust/exercise/src/lib.rs b/src/main/resources/templates/rust/exercise/src/lib.rs new file mode 100644 index 000000000000..930d9899389d --- /dev/null +++ b/src/main/resources/templates/rust/exercise/src/lib.rs @@ -0,0 +1,5 @@ +pub mod bubble_sort; +pub mod context; +pub mod merge_sort; +pub mod policy; +pub mod sort_strategy; diff --git a/src/main/resources/templates/rust/exercise/src/main.rs b/src/main/resources/templates/rust/exercise/src/main.rs new file mode 100644 index 000000000000..9263a66a267e --- /dev/null +++ b/src/main/resources/templates/rust/exercise/src/main.rs @@ -0,0 +1,53 @@ +use std::ops::RangeInclusive; +use std::time::Duration; + +use chrono::{NaiveDate, TimeDelta}; +use rand::{thread_rng, Rng}; + +const ITERATIONS: usize = 10; +const LENGTH_MIN: usize = 5; +const LENGTH_MAX: usize = 15; + +/// Main function. +/// Add code to demonstrate your implementation here. +fn main() { + todo!("Init Context and Policy"); + + // Run multiple times to simulate different sorting strategies + for _ in 0..ITERATIONS { + let mut dates = create_random_dates(); + todo!("Configure context"); + println!("Unsorted Array of course dates = {dates:#?}"); + + todo!("Sort dates"); + + println!("Sorted Array of course dates = {dates:#?}"); + } +} + +/// Generates a [Vec] of random [NaiveDate] objects with a random length +/// between [LENGTH_MIN] and [LENGTH_MAX]. +fn create_random_dates() -> Vec { + let length = thread_rng().gen_range(LENGTH_MIN..=LENGTH_MAX); + + let date_format = "%Y-%m-%d"; + let low_date = NaiveDate::parse_from_str("2024-09-15", date_format).unwrap(); + let high_date = NaiveDate::parse_from_str("2025-01-15", date_format).unwrap(); + + let mut dates = Vec::new(); + dates.resize_with(length, || random_date_within(low_date..=high_date)); + dates +} + +/// Creates a random Date within the given range. +fn random_date_within(range: RangeInclusive) -> NaiveDate { + let (start, end) = range.into_inner(); + + let max_delta = end - start; + let max_duration = max_delta.to_std().unwrap(); + + let random_duration = thread_rng().gen_range(Duration::ZERO..=max_duration); + let random_delta = TimeDelta::from_std(random_duration).unwrap(); + + start + random_delta +} diff --git a/src/main/resources/templates/rust/exercise/src/merge_sort.rs b/src/main/resources/templates/rust/exercise/src/merge_sort.rs new file mode 100644 index 000000000000..78fbb6b6dd33 --- /dev/null +++ b/src/main/resources/templates/rust/exercise/src/merge_sort.rs @@ -0,0 +1,14 @@ +use chrono::NaiveDate; + +pub struct MergeSort; + +impl MergeSort { + /// Sorts items with the Merge Sort algorithm. + /// + /// Arguments: + /// + /// * `input`: slice of items to be sorted + pub fn perform_sort(&self, input: &mut [NaiveDate]) { + todo!("implement"); + } +} diff --git a/src/main/resources/templates/rust/exercise/src/policy.rs b/src/main/resources/templates/rust/exercise/src/policy.rs new file mode 100644 index 000000000000..ebaa1dfd92d3 --- /dev/null +++ b/src/main/resources/templates/rust/exercise/src/policy.rs @@ -0,0 +1 @@ +// TODO: Create and implement a Policy class as described in the problem statement diff --git a/src/main/resources/templates/rust/exercise/src/sort_strategy.rs b/src/main/resources/templates/rust/exercise/src/sort_strategy.rs new file mode 100644 index 000000000000..ccd9a65d5246 --- /dev/null +++ b/src/main/resources/templates/rust/exercise/src/sort_strategy.rs @@ -0,0 +1,2 @@ +// TODO: Create a SortStrategy interface according to the UML class diagram +// TODO: Make the sorting algorithms implement this trait. diff --git a/src/main/resources/templates/rust/readme b/src/main/resources/templates/rust/readme new file mode 100755 index 000000000000..9510a4ca0428 --- /dev/null +++ b/src/main/resources/templates/rust/readme @@ -0,0 +1,91 @@ +# Sorting with the Strategy Pattern + +In this exercise, we want to implement sorting algorithms and choose them based on runtime specific variables. + +### Part 1: Sorting + +First, we need to implement two sorting algorithms, in this case `MergeSort` and `BubbleSort`. + +**You have the following tasks:** + +1. [task][Implement Bubble Sort](test_bubble_sort) +Implement the method `perform_sort(&mut [NaiveDate])` in the struct `BubbleSort`. Make sure to follow the Bubble Sort algorithm exactly. + +2. [task][Implement Merge Sort](test_merge_sort) +Implement the method `perform_sort(&mut [NaiveDate])` in the struct `MergeSort`. Make sure to follow the Merge Sort algorithm exactly. + +### Part 2: Strategy Pattern + +We want the application to apply different algorithms for sorting a slice of `NaiveDate` objects. +Use the strategy pattern to select the right sorting algorithm at runtime. + +**You have the following tasks:** + +1. [task][SortStrategy Interface](test_sort_strategy_trait,test_sort_strategy_methods,test_sort_strategy_supertrait,test_merge_sort_struct,test_bubble_sort_struct) +Create a `SortStrategy` trait and adjust the sorting algorithms so that they implement this interface. +Also make sure to declare `std::any::Any` as a supertrait. + +2. [task][Context Class](test_context_fields,test_context_methods) +Create and implement a `Context` struct following the below class diagram. +Also add a getter for `sort_algorithm` with the signature `sort_algorithm(&self) -> &dyn SortStrategy`. + +3. [task][Context Policy](test_policy_fields,test_policy_methods) +Create and implement a `Policy` struct following the below class diagram with a simple configuration mechanism: + + 1. [task][Select MergeSort](test_merge_sort_struct,test_use_merge_sort_for_big_list) + Select `MergeSort` when the List has more than 10 dates. + + 2. [task][Select BubbleSort](test_bubble_sort_struct,test_use_bubble_sort_for_small_list) + Select `BubbleSort` when the List has less or equal 10 dates. + +4. Complete the `main()` function which demonstrates switching between two strategies at runtime. + +@startuml + +class Policy { + +new(&RefCell) + +configure(&[NaiveDate]) +} + +class Context { + +new() + +sort(&mut [NaiveDate]) +} + +interface Any { +} + +interface SortStrategy { + +perform_sort(&mut [NaiveDate]) +} + +class BubbleSort { + +perform_sort(&mut [NaiveDate]) +} + +class MergeSort { + +perform_sort(&mut [NaiveDate]) +} + +MergeSort -up-|> SortStrategy #testsColor(test_merge_sort_struct) +BubbleSort -up-|> SortStrategy #testsColor(test_bubble_sort_struct) +SortStrategy -up-|> Any #testsColor(test_sort_strategy_supertrait) +Policy -right-> Context #testsColor(test_policy_fields): context +Context -right-> SortStrategy #testsColor(test_context_fields): sort_algorithm + +hide empty fields +hide empty methods + +@enduml + + +### Part 3: Optional Challenges + +(These are not tested) + +1. Create a new struct `QuickSort` that implements `SortStrategy` and implement the Quick Sort algorithm. + +2. Make the method `perform_sort(&mut [NaiveDate])` generic, so that other types can also be sorted by the same method. +**Hint:** Have a look at Rust Generics and the trait `Ord`. + +3. Think about a useful decision in `Policy` when to use the new `QuickSort` algorithm. diff --git a/src/main/resources/templates/rust/solution/.gitattributes b/src/main/resources/templates/rust/solution/.gitattributes new file mode 100644 index 000000000000..2677732ca2e9 --- /dev/null +++ b/src/main/resources/templates/rust/solution/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf + +*.rs text eol=lf diff=rust +Cargo.toml text eol=lf +Cargo.lock text eol=lf diff --git a/src/main/resources/templates/rust/solution/.gitignore b/src/main/resources/templates/rust/solution/.gitignore new file mode 100644 index 000000000000..ea8c4bf7f35f --- /dev/null +++ b/src/main/resources/templates/rust/solution/.gitignore @@ -0,0 +1 @@ +/target diff --git a/src/main/resources/templates/rust/solution/Cargo.lock b/src/main/resources/templates/rust/solution/Cargo.lock new file mode 100644 index 000000000000..2f5f96ba7454 --- /dev/null +++ b/src/main/resources/templates/rust/solution/Cargo.lock @@ -0,0 +1,100 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust-template-exercise" +version = "0.1.0" +dependencies = [ + "chrono", + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/src/main/resources/templates/rust/solution/Cargo.toml b/src/main/resources/templates/rust/solution/Cargo.toml new file mode 100644 index 000000000000..34b160c5368d --- /dev/null +++ b/src/main/resources/templates/rust/solution/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust-template-exercise" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { version = "0.4.38", default-features = false } +rand = "0.8.5" diff --git a/src/main/resources/templates/rust/solution/src/bubble_sort.rs b/src/main/resources/templates/rust/solution/src/bubble_sort.rs new file mode 100644 index 000000000000..82c751842786 --- /dev/null +++ b/src/main/resources/templates/rust/solution/src/bubble_sort.rs @@ -0,0 +1,23 @@ +use std::cmp::Ord; + +use crate::sort_strategy::SortStrategy; + +pub struct BubbleSort; + +impl SortStrategy for BubbleSort { + /// Sorts items with the Bubble Sort algorithm. + /// + /// Arguments: + /// + /// * `input`: slice of items to be sorted + fn perform_sort(&self, input: &mut [T]) { + let len = input.len(); + for i in (0..len).rev() { + for j in 0..i { + if input[j] > input[j + 1] { + input.swap(j, j + 1); + } + } + } + } +} diff --git a/src/main/resources/templates/rust/solution/src/context.rs b/src/main/resources/templates/rust/solution/src/context.rs new file mode 100644 index 000000000000..71cd0d711753 --- /dev/null +++ b/src/main/resources/templates/rust/solution/src/context.rs @@ -0,0 +1,36 @@ +use crate::sort_strategy::SortStrategy; +use chrono::NaiveDate; +use std::ops::Deref; + +pub struct Context { + sort_algorithm: Option>>, +} + +impl Context { + pub fn new() -> Context { + Context { + sort_algorithm: None, + } + } + + /// Runs the configured sorting algorithm. + pub fn sort(&self, data: &mut [NaiveDate]) { + let sort_algorithm = self + .sort_algorithm + .as_ref() + .expect("sort_algorithm has to be set before sort() is called"); + + sort_algorithm.perform_sort(data); + } + + pub fn set_sort_algorithm(&mut self, sort_algorithm: Box>) { + self.sort_algorithm = Some(sort_algorithm); + } + + pub fn sort_algorithm(&self) -> &dyn SortStrategy { + self.sort_algorithm + .as_ref() + .expect("sort_algorithm has to be set") + .deref() + } +} diff --git a/src/main/resources/templates/rust/solution/src/lib.rs b/src/main/resources/templates/rust/solution/src/lib.rs new file mode 100644 index 000000000000..930d9899389d --- /dev/null +++ b/src/main/resources/templates/rust/solution/src/lib.rs @@ -0,0 +1,5 @@ +pub mod bubble_sort; +pub mod context; +pub mod merge_sort; +pub mod policy; +pub mod sort_strategy; diff --git a/src/main/resources/templates/rust/solution/src/main.rs b/src/main/resources/templates/rust/solution/src/main.rs new file mode 100644 index 000000000000..0d8d362fafc9 --- /dev/null +++ b/src/main/resources/templates/rust/solution/src/main.rs @@ -0,0 +1,59 @@ +use std::time::Duration; +use std::{cell::RefCell, ops::RangeInclusive}; + +use chrono::{NaiveDate, TimeDelta}; +use rand::{thread_rng, Rng}; + +use rust_template_exercise::{context::Context, policy::Policy}; + +const ITERATIONS: usize = 10; +const LENGTH_MIN: usize = 5; +const LENGTH_MAX: usize = 15; + +/// Main function. +/// Add code to demonstrate your implementation here. +fn main() { + // Init Context and Policy + let context = RefCell::new(Context::new()); + let mut policy = Policy::new(&context); + + // Run multiple times to simulate different sorting strategies + for _ in 0..ITERATIONS { + let mut dates = create_random_dates(); + // Configure context + policy.configure(&dates); + println!("Unsorted Array of course dates = {dates:#?}"); + + // Sort dates + context.borrow().sort(&mut dates); + + println!("Sorted Array of course dates = {dates:#?}"); + } +} + +/// Generates a [Vec] of random [NaiveDate] objects with a random length +/// between [LENGTH_MIN] and [LENGTH_MAX]. +fn create_random_dates() -> Vec { + let length = thread_rng().gen_range(LENGTH_MIN..=LENGTH_MAX); + + let date_format = "%Y-%m-%d"; + let low_date = NaiveDate::parse_from_str("2024-09-15", date_format).unwrap(); + let high_date = NaiveDate::parse_from_str("2025-01-15", date_format).unwrap(); + + let mut dates = Vec::new(); + dates.resize_with(length, || random_date_within(low_date..=high_date)); + dates +} + +/// Creates a random Date within the given range. +fn random_date_within(range: RangeInclusive) -> NaiveDate { + let (start, end) = range.into_inner(); + + let max_delta = end - start; + let max_duration = max_delta.to_std().unwrap(); + + let random_duration = thread_rng().gen_range(Duration::ZERO..=max_duration); + let random_delta = TimeDelta::from_std(random_duration).unwrap(); + + start + random_delta +} diff --git a/src/main/resources/templates/rust/solution/src/merge_sort.rs b/src/main/resources/templates/rust/solution/src/merge_sort.rs new file mode 100644 index 000000000000..a1887c6d38f8 --- /dev/null +++ b/src/main/resources/templates/rust/solution/src/merge_sort.rs @@ -0,0 +1,59 @@ +use std::cmp::Ord; + +use crate::sort_strategy::SortStrategy; + +pub struct MergeSort; + +impl SortStrategy for MergeSort { + /// Sorts items with the Merge Sort algorithm. + /// + /// Arguments: + /// + /// * `input`: slice of items to be sorted + fn perform_sort(&self, input: &mut [T]) { + mergesort(input); + } +} + +fn mergesort(input: &mut [T]) { + if input.len() < 2 { + return; + } + + let middle = input.len() / 2; + let (left, right) = input.split_at_mut(middle); + mergesort(left); + mergesort(right); + merge(input, middle); +} + +fn merge(input: &mut [T], middle: usize) { + let mut result = Vec::with_capacity(input.len()); + + let mut left_index = 0; + let mut right_index = middle; + + while left_index < middle && right_index < input.len() { + if input[left_index] <= input[right_index] { + result.push(input[left_index]); + left_index += 1; + } else { + result.push(input[right_index]); + right_index += 1; + } + } + + if left_index < middle { + while left_index < middle { + result.push(input[left_index]); + left_index += 1; + } + } else { + while right_index < input.len() { + result.push(input[right_index]); + right_index += 1; + } + } + + input.copy_from_slice(&result); +} diff --git a/src/main/resources/templates/rust/solution/src/policy.rs b/src/main/resources/templates/rust/solution/src/policy.rs new file mode 100644 index 000000000000..a8e7e5203162 --- /dev/null +++ b/src/main/resources/templates/rust/solution/src/policy.rs @@ -0,0 +1,28 @@ +use crate::{ + bubble_sort::BubbleSort, context::Context, merge_sort::MergeSort, sort_strategy::SortStrategy, +}; +use chrono::NaiveDate; +use std::cell::RefCell; + +const SIZE_THRESHOLD: usize = 10; + +pub struct Policy<'a> { + context: &'a RefCell, +} + +impl<'a> Policy<'a> { + pub fn new(context: &'a RefCell) -> Policy<'a> { + Policy { context } + } + + /// Chooses a strategy depending on the number of items. + pub fn configure(&mut self, data: &[NaiveDate]) { + let sort_algorithm: Box> = if data.len() > SIZE_THRESHOLD { + Box::new(MergeSort) + } else { + Box::new(BubbleSort) + }; + + self.context.borrow_mut().set_sort_algorithm(sort_algorithm); + } +} diff --git a/src/main/resources/templates/rust/solution/src/sort_strategy.rs b/src/main/resources/templates/rust/solution/src/sort_strategy.rs new file mode 100644 index 000000000000..b924206e39ba --- /dev/null +++ b/src/main/resources/templates/rust/solution/src/sort_strategy.rs @@ -0,0 +1,10 @@ +use std::any::Any; + +pub trait SortStrategy: Any { + /// Sorts a slice of `T`s. + /// + /// Arguments: + /// + /// * `input`: slice of items to be sorted + fn perform_sort(&self, input: &mut [T]); +} diff --git a/src/main/resources/templates/rust/test/.config/nextest.toml b/src/main/resources/templates/rust/test/.config/nextest.toml new file mode 100644 index 000000000000..6a109e171680 --- /dev/null +++ b/src/main/resources/templates/rust/test/.config/nextest.toml @@ -0,0 +1,5 @@ +[profile.ci] +fail-fast = false + +[profile.ci.junit] +path = "junit.xml" diff --git a/src/main/resources/templates/rust/test/.gitattributes b/src/main/resources/templates/rust/test/.gitattributes new file mode 100644 index 000000000000..2677732ca2e9 --- /dev/null +++ b/src/main/resources/templates/rust/test/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf + +*.rs text eol=lf diff=rust +Cargo.toml text eol=lf +Cargo.lock text eol=lf diff --git a/src/main/resources/templates/rust/test/.gitignore b/src/main/resources/templates/rust/test/.gitignore new file mode 100644 index 000000000000..a37c5236a444 --- /dev/null +++ b/src/main/resources/templates/rust/test/.gitignore @@ -0,0 +1,2 @@ +/target +/assignment diff --git a/src/main/resources/templates/rust/test/Cargo.lock b/src/main/resources/templates/rust/test/Cargo.lock new file mode 100644 index 000000000000..bb5595575a1a --- /dev/null +++ b/src/main/resources/templates/rust/test/Cargo.lock @@ -0,0 +1,183 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust-template-exercise" +version = "0.1.0" +dependencies = [ + "chrono", + "rand", +] + +[[package]] +name = "rust-template-test" +version = "0.1.0" +dependencies = [ + "chrono", + "rust-template-exercise", + "rust_template_test_macros", + "syn", +] + +[[package]] +name = "rust_template_test_macros" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/main/resources/templates/rust/test/Cargo.toml b/src/main/resources/templates/rust/test/Cargo.toml new file mode 100644 index 000000000000..07f82b3f09f0 --- /dev/null +++ b/src/main/resources/templates/rust/test/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rust-template-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +chrono = { version = "0.4.38", default-features = false } +rust-template-exercise = { path = "assignment" } +syn = { version = "2.0.72", features = ["full"] } +rust_template_test_macros = { path = "./rust_template_test_macros" } + +[build-dependencies] +syn = { version = "2.0.72", features = ["full"] } diff --git a/src/main/resources/templates/rust/test/build.rs b/src/main/resources/templates/rust/test/build.rs new file mode 100644 index 000000000000..850fcb846cce --- /dev/null +++ b/src/main/resources/templates/rust/test/build.rs @@ -0,0 +1,204 @@ +use std::error::Error; +use std::ffi::OsStr; +use std::fs::read_to_string; +use std::path::{Component, Path}; +use std::{fs, io}; + +use syn::{parse_file, FnArg, ImplItem, Item, TraitItem, Type, TypeParamBound}; + +const SRC_DIR: &str = "assignment/src"; + +fn main() { + println!("cargo::rerun-if-changed={SRC_DIR}"); + if let Err(err) = visit_dirs(Path::new(SRC_DIR), &process_file) { + eprintln!("Failed to analyze submission: {err}"); + } +} + +fn visit_dirs Result<(), Box>>( + dir: &Path, + cb: &F, +) -> Result<(), Box> { + for entry in fs::read_dir(dir).map_err(|e| { + io::Error::new( + e.kind(), + format!("Failed to read directory {}: {}", dir.display(), e), + ) + })? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + visit_dirs(&path, cb)?; + } else { + cb(&entry.path())?; + } + } + Ok(()) +} + +fn process_file(path: &Path) -> Result<(), Box> { + if path.extension() != Some(OsStr::new("rs")) { + return Ok(()); + } + + let file_content = read_to_string(path)?; + + let ast = parse_file(&file_content)?; + + let module = path + .strip_prefix(SRC_DIR)? + .with_extension("") + .components() + .map(Component::as_os_str) + .collect::>() + .join(OsStr::new("_")); + let module = module.to_str().ok_or("invalid UTF-8 in path")?; + + for item in ast.items { + match item { + Item::Enum(e) => { + println!("cargo::rustc-cfg=structure_{module}_enum_{}", e.ident); + + for v in e.variants { + println!( + "cargo::rustc-cfg=structure_{module}_enum_{}_variant_{}", + e.ident, v.ident + ); + } + } + Item::Fn(f) => println!("cargo::rustc-cfg=structure_{module}_fn_{}", f.sig.ident), + Item::Impl(impl_) => { + let self_path = if let Type::Path(p) = *impl_.self_ty { + p + } else { + continue; + }; + + let self_ident = if let Some(i) = self_path.path.segments.last().map(|s| &s.ident) { + i + } else { + continue; + }; + + if impl_.trait_.is_some() { + let trait_ident = if let Some(i) = impl_ + .trait_ + .as_ref() + .unwrap() + .1 + .segments + .last() + .map(|s| &s.ident) + { + i + } else { + continue; + }; + println!( + "cargo::rustc-cfg=structure_{module}_impl_{trait_ident}_for_{self_ident}" + ); + continue; + } + + for item in impl_.items { + match item { + ImplItem::Const(c) => println!( + "cargo::rustc-cfg=structure_{module}_impl_{self_ident}_const_{}", + c.ident + ), + ImplItem::Fn(f) => { + println!( + "cargo::rustc-cfg=structure_{module}_impl_{self_ident}_fn_{}", + f.sig.ident + ); + + if matches!(f.sig.inputs.first(), Some(&FnArg::Receiver(_))) { + println!( + "cargo::rustc-cfg=structure_{module}_impl_{self_ident}_method_{}", + f.sig.ident + ); + } + } + ImplItem::Type(t) => println!( + "cargo::rustc-cfg=structure_{module}_impl_{self_ident}_type_{}", + t.ident + ), + _ => continue, + } + } + } + Item::Struct(struct_) => { + println!( + "cargo::rustc-cfg=structure_{module}_struct_{}", + struct_.ident + ); + + for field in struct_.fields { + if let Some(field_ident) = field.ident { + println!( + "cargo::rustc-cfg=structure_{module}_struct_{}_field_{field_ident}", + struct_.ident + ); + } + } + } + Item::Trait(trait_) => { + println!("cargo::rustc-cfg=structure_{module}_trait_{}", trait_.ident); + + for supertrait in trait_.supertraits { + let supertrait = match supertrait { + TypeParamBound::Trait(supertrait) => supertrait, + _ => continue, + }; + let supertrait = &supertrait.path.segments.last().unwrap().ident; + println!( + "cargo::rustc-cfg=structure_{module}_trait_{}_supertrait_{supertrait}", + trait_.ident + ); + } + + for item in trait_.items { + match item { + TraitItem::Const(c) => println!( + "cargo::rustc-cfg=structure_{module}_trait_{}_const_{}", + trait_.ident, c.ident + ), + TraitItem::Fn(f) => { + println!( + "cargo::rustc-cfg=structure_{module}_trait_{}_fn_{}", + trait_.ident, f.sig.ident + ); + + if matches!(f.sig.inputs.first(), Some(&FnArg::Receiver(_))) { + println!( + "cargo::rustc-cfg=structure_{module}_trait_{}_method_{}", + trait_.ident, f.sig.ident + ); + } + } + TraitItem::Type(t) => println!( + "cargo::rustc-cfg=structure_{module}_trait_{}_type_{}", + trait_.ident, t.ident + ), + _ => continue, + } + } + } + Item::Union(union_) => { + println!("cargo::rustc-cfg=structure_{module}_union_{}", union_.ident); + + for field in union_.fields.named { + if let Some(field_ident) = field.ident { + println!( + "cargo::rustc-cfg=structure_{module}_union_{}_field_{field_ident}", + union_.ident + ); + } + } + } + _ => continue, + } + } + + Ok(()) +} diff --git a/src/main/resources/templates/rust/test/rust_template_test_macros/.gitignore b/src/main/resources/templates/rust/test/rust_template_test_macros/.gitignore new file mode 100644 index 000000000000..ea8c4bf7f35f --- /dev/null +++ b/src/main/resources/templates/rust/test/rust_template_test_macros/.gitignore @@ -0,0 +1 @@ +/target diff --git a/src/main/resources/templates/rust/test/rust_template_test_macros/Cargo.lock b/src/main/resources/templates/rust/test/rust_template_test_macros/Cargo.lock new file mode 100644 index 000000000000..a1259e414852 --- /dev/null +++ b/src/main/resources/templates/rust/test/rust_template_test_macros/Cargo.lock @@ -0,0 +1,46 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rust_template_test_macros" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/src/main/resources/templates/rust/test/rust_template_test_macros/Cargo.toml b/src/main/resources/templates/rust/test/rust_template_test_macros/Cargo.toml new file mode 100644 index 000000000000..625dee26344d --- /dev/null +++ b/src/main/resources/templates/rust/test/rust_template_test_macros/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rust_template_test_macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.36" +syn = { version = "2.0.72", features = ["full"] } diff --git a/src/main/resources/templates/rust/test/rust_template_test_macros/src/lib.rs b/src/main/resources/templates/rust/test/rust_template_test_macros/src/lib.rs new file mode 100644 index 000000000000..498fa14d54a1 --- /dev/null +++ b/src/main/resources/templates/rust/test/rust_template_test_macros/src/lib.rs @@ -0,0 +1,346 @@ +//! Compile-time source code reflection +//! +//! Use this for conditional compilation. + +use proc_macro::TokenStream; + +use quote::{format_ident, quote}; +use syn::parse::Parse; +use syn::{parse_macro_input, Ident, Item, ItemFn, Path, Token}; + +trait ToStringLocal { + fn to_string(&self) -> String; +} + +impl ToStringLocal for Path { + fn to_string(&self) -> String { + let segments: Vec<_> = self.segments.iter().map(|s| s.ident.to_string()).collect(); + segments.join("::") + } +} + +#[proc_macro_attribute] +pub fn require_struct(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_struct) +} + +#[proc_macro_attribute] +pub fn require_struct_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_struct) +} + +#[proc_macro_attribute] +pub fn require_trait(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_trait) +} + +#[proc_macro_attribute] +pub fn require_trait_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_trait) +} + +#[proc_macro_attribute] +pub fn require_enum(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_enum) +} + +#[proc_macro_attribute] +pub fn require_enum_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_enum) +} + +#[proc_macro_attribute] +pub fn require_function(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_function) +} + +#[proc_macro_attribute] +pub fn require_function_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_function) +} + +#[proc_macro_attribute] +pub fn require_impl_for_trait(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_impl_for_trait) +} + +#[proc_macro_attribute] +pub fn require_impl_for_trait_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_impl_for_trait) +} + +#[proc_macro_attribute] +pub fn require_impl_function(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_impl_function) +} + +#[proc_macro_attribute] +pub fn require_impl_function_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_impl_function) +} + +#[proc_macro_attribute] +pub fn require_impl_method(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_impl_method) +} + +#[proc_macro_attribute] +pub fn require_impl_method_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_impl_method) +} + +#[proc_macro_attribute] +pub fn require_impl_const(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_impl_const) +} + +#[proc_macro_attribute] +pub fn require_impl_const_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_impl_const) +} + +#[proc_macro_attribute] +pub fn require_impl_type(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_impl_type) +} + +#[proc_macro_attribute] +pub fn require_impl_type_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_impl_type) +} + +#[proc_macro_attribute] +pub fn require_trait_function(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_trait_function) +} + +#[proc_macro_attribute] +pub fn require_trait_function_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_trait_function) +} + +#[proc_macro_attribute] +pub fn require_trait_method(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_trait_method) +} + +#[proc_macro_attribute] +pub fn require_trait_method_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_trait_method) +} + +#[proc_macro_attribute] +pub fn require_trait_const(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_trait_const) +} + +#[proc_macro_attribute] +pub fn require_trait_const_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_trait_const) +} + +#[proc_macro_attribute] +pub fn require_trait_type(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_trait_type) +} + +#[proc_macro_attribute] +pub fn require_trait_type_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_trait_type) +} + +#[proc_macro_attribute] +pub fn require_trait_supertrait(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_item(attr, item, make_cfg_trait_supertrait) +} + +#[proc_macro_attribute] +pub fn require_trait_supertrait_or_fail(attr: TokenStream, item: TokenStream) -> TokenStream { + require_for_function(attr, item, make_cfg_trait_supertrait) +} + +struct SuperTraitSpec { + module_path: String, + trait_name: String, + supertrait: String, +} + +impl Parse for SuperTraitSpec { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let path: Path = input.parse()?; + input.parse::()?; + let supertrait: Path = input.parse()?; + + let (module_path, trait_name) = split_path(path); + let supertrait = supertrait.segments.last().unwrap().ident.to_string(); + + Ok(SuperTraitSpec { + module_path, + trait_name, + supertrait, + }) + } +} + +impl ToStringLocal for SuperTraitSpec { + fn to_string(&self) -> String { + format!( + "supertrait {} of {}::{}", + self.supertrait, self.module_path, self.trait_name + ) + } +} + +fn require_for_item Ident>( + attr: TokenStream, + item: TokenStream, + make_cfg: F, +) -> TokenStream { + let attribute = parse_macro_input!(attr as A); + let original_item = parse_macro_input!(item as Item); + + let cfg = make_cfg(attribute); + + quote! ( + #[cfg(#cfg)] + #original_item + ) + .into() +} + +fn require_for_function Ident>( + attr: TokenStream, + item: TokenStream, + make_cfg: F, +) -> TokenStream { + let attribute = parse_macro_input!(attr as A); + let original_fn = parse_macro_input!(item as ItemFn); + + let failure_message = format!("missing {}", attribute.to_string()); + + let cfg = make_cfg(attribute); + + let ItemFn { + attrs, + vis, + sig, + block, + } = original_fn; + + quote!( + #(#attrs)* + #vis #sig { + #[cfg(not(#cfg))] + panic!(#failure_message); + #[cfg(#cfg)] + #block + } + ) + .into() +} + +fn make_cfg_struct(path: Path) -> Ident { + let (module_path, struct_name) = split_path(path); + format_ident!("structure_{module_path}_struct_{struct_name}") +} + +fn make_cfg_trait(path: Path) -> Ident { + let (module_path, trait_name) = split_path(path); + format_ident!("structure_{module_path}_trait_{trait_name}") +} + +fn make_cfg_enum(path: Path) -> Ident { + let (module_path, enum_name) = split_path(path); + format_ident!("structure_{module_path}_enum_{enum_name}") +} + +fn make_cfg_function(path: Path) -> Ident { + let (module_path, function_name) = split_path(path); + format_ident!("structure_{module_path}_fn_{function_name}") +} + +fn make_cfg_impl_for_trait(path: Path) -> Ident { + let (module_path, self_type, trait_name) = split_path2(path); + format_ident!("structure_{module_path}_impl_{trait_name}_for_{self_type}") +} + +fn make_cfg_impl_function(path: Path) -> Ident { + let (module_path, self_type, function) = split_path2(path); + format_ident!("structure_{module_path}_impl_{self_type}_fn_{function}") +} + +fn make_cfg_impl_method(path: Path) -> Ident { + let (module_path, self_type, function) = split_path2(path); + format_ident!("structure_{module_path}_impl_{self_type}_method_{function}") +} + +fn make_cfg_impl_const(path: Path) -> Ident { + let (module_path, self_type, const_name) = split_path2(path); + format_ident!("structure_{module_path}_impl_{self_type}_const_{const_name}") +} + +fn make_cfg_impl_type(path: Path) -> Ident { + let (module_path, self_type, type_name) = split_path2(path); + format_ident!("structure_{module_path}_impl_{self_type}_type_{type_name}") +} + +fn make_cfg_trait_function(path: Path) -> Ident { + let (module_path, trait_type, function) = split_path2(path); + format_ident!("structure_{module_path}_trait_{trait_type}_fn_{function}") +} + +fn make_cfg_trait_method(path: Path) -> Ident { + let (module_path, trait_type, function) = split_path2(path); + format_ident!("structure_{module_path}_trait_{trait_type}_method_{function}") +} + +fn make_cfg_trait_const(path: Path) -> Ident { + let (module_path, trait_type, const_name) = split_path2(path); + format_ident!("structure_{module_path}_trait_{trait_type}_const_{const_name}") +} + +fn make_cfg_trait_type(path: Path) -> Ident { + let (module_path, trait_type, type_name) = split_path2(path); + format_ident!("structure_{module_path}_trait_{trait_type}_type_{type_name}") +} + +fn make_cfg_trait_supertrait(spec: SuperTraitSpec) -> Ident { + format_ident!( + "structure_{}_trait_{}_supertrait_{}", + spec.module_path, + spec.trait_name, + spec.supertrait + ) +} + +fn split_path(path: Path) -> (String, String) { + let path_segments: Vec<_> = path.segments.iter().map(|s| &s.ident).collect(); + let (item_name, path_segments) = path_segments.split_last().unwrap(); + + let item_name = item_name.to_string(); + let module_path = path_segments + .iter() + .copied() + .map(Ident::to_string) + .collect::>() + .join("_"); + + (module_path, item_name) +} + +fn split_path2(path: Path) -> (String, String, String) { + let path_segments: Vec<_> = path.segments.iter().map(|s| &s.ident).collect(); + let (item2_name, path_segments) = path_segments.split_last().unwrap(); + let (item1_name, path_segments) = path_segments.split_last().unwrap(); + + let item1_name = item1_name.to_string(); + let item2_name = item2_name.to_string(); + let module_path = path_segments + .iter() + .copied() + .map(Ident::to_string) + .collect::>() + .join("_"); + + (module_path, item1_name, item2_name) +} diff --git a/src/main/resources/templates/rust/test/tests/behavior.rs b/src/main/resources/templates/rust/test/tests/behavior.rs new file mode 100644 index 000000000000..7efd38c20de5 --- /dev/null +++ b/src/main/resources/templates/rust/test/tests/behavior.rs @@ -0,0 +1,90 @@ +use chrono::NaiveDate; +use rust_template_exercise::{bubble_sort::BubbleSort, merge_sort::MergeSort}; + +use rust_template_test_macros::{ + require_impl_function_or_fail, require_impl_method_or_fail, require_struct_or_fail, + require_trait, require_trait_supertrait_or_fail, +}; + +#[require_trait(sort_strategy::SortStrategy)] +use rust_template_exercise::sort_strategy::SortStrategy; + +// We can't use the opt variants because the Option methods aren't const yet +#[allow(deprecated)] +const DATES_UNORDERED: [NaiveDate; 4] = [ + NaiveDate::from_ymd(2018, 11, 8), + NaiveDate::from_ymd(2017, 4, 15), + NaiveDate::from_ymd(2016, 2, 15), + NaiveDate::from_ymd(2017, 9, 15), +]; +#[allow(deprecated)] +const DATES_ORDERED: [NaiveDate; 4] = [ + NaiveDate::from_ymd(2016, 2, 15), + NaiveDate::from_ymd(2017, 4, 15), + NaiveDate::from_ymd(2017, 9, 15), + NaiveDate::from_ymd(2018, 11, 8), +]; + +#[test] +fn test_bubble_sort() { + let bubble_sort = BubbleSort; + let mut dates = DATES_UNORDERED; + bubble_sort.perform_sort(&mut dates); + assert_eq!(dates, DATES_ORDERED, "BubbleSort does not sort correctly"); +} + +#[test] +fn test_merge_sort() { + let merge_sort = MergeSort; + let mut dates = DATES_UNORDERED; + merge_sort.perform_sort(&mut dates); + assert_eq!(dates, DATES_ORDERED, "MergeSort does not sort correctly"); +} + +#[test] +#[require_struct_or_fail(context::Context)] +#[require_struct_or_fail(policy::Policy)] +#[require_trait_supertrait_or_fail(sort_strategy::SortStrategy : Any)] +#[require_impl_method_or_fail(context::Context::sort_algorithm)] +#[require_impl_function_or_fail(context::Context::new)] +#[require_impl_function_or_fail(policy::Policy::new)] +fn test_use_merge_sort_for_big_list() { + let context = std::cell::RefCell::new(rust_template_exercise::context::Context::new()); + let mut policy = rust_template_exercise::policy::Policy::new(&context); + + let data = [NaiveDate::default(); 20]; + policy.configure(&data); + + let context = context.borrow(); + let sort_strategy = context.sort_algorithm(); + + assert_eq!( + sort_strategy.type_id(), + std::any::TypeId::of::(), + "The sort algorithm of Context was not MergeSort for a list with more than 10 dates." + ); +} + +#[test] +#[require_struct_or_fail(context::Context)] +#[require_struct_or_fail(policy::Policy)] +#[require_trait_supertrait_or_fail(sort_strategy::SortStrategy : Any)] +#[require_impl_method_or_fail(context::Context::sort_algorithm)] +#[require_impl_function_or_fail(context::Context::new)] +#[require_impl_function_or_fail(policy::Policy::new)] +fn test_use_bubble_sort_for_small_list() { + let context = std::cell::RefCell::new(rust_template_exercise::context::Context::new()); + let mut policy = rust_template_exercise::policy::Policy::new(&context); + + let data = [NaiveDate::default(); 10]; + policy.configure(&data); + + let context = context.borrow(); + let sort_strategy = context.sort_algorithm(); + + assert_eq!( + sort_strategy.type_id(), + std::any::TypeId::of::(), + "The sort algorithm of Context was not BubbleSort for a list with less or equal than 10 dates." + ); +} diff --git a/src/main/resources/templates/rust/test/tests/structural.rs b/src/main/resources/templates/rust/test/tests/structural.rs new file mode 100644 index 000000000000..70b0d19c9131 --- /dev/null +++ b/src/main/resources/templates/rust/test/tests/structural.rs @@ -0,0 +1,79 @@ +mod structural_helpers; + +use structural_helpers::*; + +#[test] +fn test_sort_strategy_trait() { + let ast = parse_file("./assignment/src/sort_strategy.rs"); + check_trait_names(&ast.items, ["SortStrategy"]) + .unwrap_or_else(|name| panic!("A trait named \"{name}\" should be defined")); +} + +#[test] +fn test_sort_strategy_supertrait() { + let ast = parse_file("./assignment/src/sort_strategy.rs"); + let sort_strategy = find_trait(&ast.items, "SortStrategy") + .expect("A trait named \"SortStrategy\" should be defined"); + check_trait_supertrait(sort_strategy, "Any") + .unwrap_or_else(|_| panic!("SortStrategy should have \"Any\" as a supertrait")); +} + +#[test] +fn test_sort_strategy_methods() { + let ast = parse_file("./assignment/src/sort_strategy.rs"); + let sort_strategy = find_trait(&ast.items, "SortStrategy") + .expect("A trait named \"SortStrategy\" should be defined"); + check_trait_function_names(&sort_strategy.items, ["perform_sort"]) + .unwrap_or_else(|name| panic!("SortStrategy should define the function \"{name}\"")); +} + +#[test] +fn test_context_fields() { + let ast = parse_file("./assignment/src/context.rs"); + let context = + find_struct(&ast.items, "Context").expect("A struct named \"Context\" should be defined"); + check_struct_field_names(&context.fields, ["sort_algorithm"]) + .unwrap_or_else(|name| panic!("Context should define the field \"{name}\"")); +} + +#[test] +fn test_context_methods() { + let ast = parse_file("./assignment/src/context.rs"); + let context_impl = + find_impl(&ast.items, "Context").expect("SortStrategy should implement functions"); + check_impl_function_names(&context_impl.items, ["new", "sort", "sort_algorithm"]) + .unwrap_or_else(|name| panic!("Context should implement the function \"{name}\"")); +} + +#[test] +fn test_policy_fields() { + let ast = parse_file("./assignment/src/policy.rs"); + let policy = + find_struct(&ast.items, "Policy").expect("A struct named \"Policy\" should be defined"); + check_struct_field_names(&policy.fields, ["context"]) + .unwrap_or_else(|name| panic!("Policy should define the field \"{name}\"")); +} + +#[test] +fn test_policy_methods() { + let ast = parse_file("./assignment/src/policy.rs"); + let policy_impl = find_impl(&ast.items, "Policy").expect("Policy should implement functions"); + check_impl_function_names(&policy_impl.items, ["new", "configure"]) + .unwrap_or_else(|name| panic!("Policy should implement the function \"{name}\"")); +} + +#[test] +fn test_bubble_sort_struct() { + let ast = parse_file("./assignment/src/bubble_sort.rs"); + find_struct(&ast.items, "BubbleSort").expect("A struct named \"BubbleSort\" should be defined"); + find_impl_for(&ast.items, "BubbleSort", "SortStrategy") + .expect("BubbleSort should implement the trait \"SortStrategy\""); +} + +#[test] +fn test_merge_sort_struct() { + let ast = parse_file("./assignment/src/merge_sort.rs"); + find_struct(&ast.items, "MergeSort").expect("A struct named \"MergeSort\" should be defined"); + find_impl_for(&ast.items, "MergeSort", "SortStrategy") + .expect("MergeSort should implement the trait \"SortStrategy\""); +} diff --git a/src/main/resources/templates/rust/test/tests/structural_helpers/mod.rs b/src/main/resources/templates/rust/test/tests/structural_helpers/mod.rs new file mode 100644 index 000000000000..493d043f6d5c --- /dev/null +++ b/src/main/resources/templates/rust/test/tests/structural_helpers/mod.rs @@ -0,0 +1,207 @@ +//! Run-time source code reflection +//! +//! Use this for flexible parsing and computed names. +#![allow(dead_code)] + +use std::io::Read; + +use syn::{ + Fields, ImplItem, ImplItemFn, + Item::{self, Impl, Struct, Trait}, + ItemImpl, ItemStruct, ItemTrait, TraitItem, TraitItemFn, TypeParamBound, +}; + +pub fn check_struct_names<'a, I: IntoIterator>( + items: &[Item], + names: I, +) -> Result<(), &'a str> { + for name in names { + find_struct(items, name).ok_or(name)?; + } + Ok(()) +} + +pub fn find_struct<'a>(items: &'a [Item], name: &str) -> Option<&'a ItemStruct> { + find_by_name( + items, + name, + |i| match i { + Struct(s) => Some(s), + _ => None, + }, + |s| &s.ident, + ) +} + +pub fn check_struct_field_names<'a, I: IntoIterator>( + fields: &Fields, + names: I, +) -> Result<(), &'a str> { + let p = match fields { + Fields::Named(f) => &f.named, + _ => panic!("The struct should have named fields"), + }; + + let field_names: Vec<_> = p.iter().map(|f| f.ident.as_ref().unwrap()).collect(); + + for name in names { + field_names + .iter() + .copied() + .find(|&n| n == name) + .ok_or(name)?; + } + Ok(()) +} + +pub fn find_impl<'a>(items: &'a [Item], name: &str) -> Option<&'a ItemImpl> { + items.iter().find_map(|i| { + let im = match i { + Impl(im) => im, + _ => return None, + }; + let self_name = match im.self_ty.as_ref() { + syn::Type::Path(p) => &p.path.segments.last().unwrap().ident, + _ => return None, + }; + if im.trait_.is_some() { + return None; + } + + if self_name != name { + return None; + } + + Some(im) + }) +} + +pub fn find_impl_for<'a>(items: &'a [Item], name: &str, for_trait: &str) -> Option<&'a ItemImpl> { + items.iter().find_map(|i| { + let im = match i { + Impl(im) => im, + _ => return None, + }; + let self_name = match im.self_ty.as_ref() { + syn::Type::Path(p) => &p.path.segments.last().unwrap().ident, + _ => return None, + }; + let trait_name = match im.trait_.as_ref() { + Some((_, path, _)) => &path.segments.last().unwrap().ident, + _ => return None, + }; + + if self_name != name || trait_name != for_trait { + return None; + } + + Some(im) + }) +} + +pub fn check_impl_function_names<'a, I: IntoIterator>( + items: &[ImplItem], + names: I, +) -> Result<(), &'a str> { + for name in names { + find_impl_function(items, name).ok_or(name)?; + } + Ok(()) +} + +pub fn find_impl_function<'a>(items: &'a [ImplItem], name: &str) -> Option<&'a ImplItemFn> { + find_by_name( + items, + name, + |i| match i { + ImplItem::Fn(f) => Some(f), + _ => None, + }, + |f| &f.sig.ident, + ) +} + +pub fn check_trait_names<'a, I: IntoIterator>( + items: &[Item], + names: I, +) -> Result<(), &'a str> { + for name in names { + find_trait(items, name).ok_or(name)?; + } + Ok(()) +} + +pub fn find_trait<'a>(items: &'a [Item], name: &str) -> Option<&'a ItemTrait> { + find_by_name( + items, + name, + |i| match i { + Trait(t) => Some(t), + _ => None, + }, + |t| &t.ident, + ) +} + +pub fn check_trait_function_names<'a, I: IntoIterator>( + items: &[TraitItem], + names: I, +) -> Result<(), &'a str> { + for name in names { + find_trait_function(items, name).ok_or(name)?; + } + Ok(()) +} + +pub fn find_trait_function<'a>(items: &'a [TraitItem], name: &str) -> Option<&'a TraitItemFn> { + find_by_name( + items, + name, + |i| match i { + TraitItem::Fn(f) => Some(f), + _ => None, + }, + |f| &f.sig.ident, + ) +} + +pub fn check_trait_supertrait(trait_: &ItemTrait, supertrait_name: &str) -> Result<(), ()> { + find_by_name( + &trait_.supertraits, + supertrait_name, + |s| match s { + TypeParamBound::Trait(t) => Some(t), + _ => None, + }, + |t| &t.path.segments.last().unwrap().ident, + ) + .ok_or(())?; + Ok(()) +} + +fn find_by_name< + 'a, + Item, + I: IntoIterator, + M: FnMut(Item) -> Option, + R, + N: FnMut(&R) -> C, + C: PartialEq<&'a str>, +>( + items: I, + name: &'a str, + item_matcher: M, + mut name_getter: N, +) -> Option { + items + .into_iter() + .filter_map(item_matcher) + .find(|i| name_getter(i) == name) +} + +pub fn parse_file>(path: P) -> syn::File { + let mut file = std::fs::File::open(path).unwrap(); + let mut content = String::new(); + file.read_to_string(&mut content).unwrap(); + syn::parse_file(&content).unwrap() +} 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..63541a0c97dc 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,13 @@ 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 { 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 { MarkdownEditorHeight, MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; @Component({ selector: 'jhi-privacy-statement-update-component', @@ -35,12 +34,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 +47,6 @@ export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked private route: ActivatedRoute, private languageHelper: JhiLanguageHelper, private changeDetectorRef: ChangeDetectorRef, - private markdownService: ArtemisMarkdownService, ) {} ngOnInit() { @@ -84,7 +82,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 +100,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 +147,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-students/complaints-student-view.component.ts b/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts index b37486509496..b469dcab432f 100644 --- a/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts +++ b/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts @@ -6,7 +6,7 @@ import { StudentParticipation } from 'app/entities/participation/student-partici import { Result } from 'app/entities/result.model'; import { Course } from 'app/entities/course.model'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { AccountService } from 'app/core/auth/account.service'; import { Submission } from 'app/entities/submission.model'; import { filter } from 'rxjs/operators'; 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/assessment-dashboard.component.ts b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts index 1f813c75104c..a39b6af80e77 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts @@ -13,7 +13,7 @@ import { Course } from 'app/entities/course.model'; import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; import { FilterProp as TeamFilterProp } from 'app/exercises/shared/team/teams.component'; import { SortService } from 'app/shared/service/sort.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; 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 }} + + jhiTranslate="artemisApp.courseStatistics.scopeButton.period" + [translateValues]="{ amount: 4 }" + > + jhiTranslate="artemisApp.courseStatistics.scopeButton.period" + [translateValues]="{ amount: 8 }" + > + jhiTranslate="artemisApp.courseStatistics.scopeButton.overview" + >

    | } @@ -85,7 +84,7 @@

    } @else { -

    {{ 'artemisApp.course.notStartedYet' | artemisTranslate }}

    +

    {{ course.startDate | artemisDate }}

    } diff --git a/src/main/webapp/app/course/manage/overview/course-management-card.component.html b/src/main/webapp/app/course/manage/overview/course-management-card.component.html index 3619d90a93d8..adf01ba9af77 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-card.component.html +++ b/src/main/webapp/app/course/manage/overview/course-management-card.component.html @@ -40,9 +40,7 @@

    {{ course.title }} ({{ } @if (courseWithUsers.numberOfStudents === undefined) { - - {{ 'artemisApp.course.students' | artemisTranslate }} - + }
    @@ -57,9 +55,7 @@

    {{ course.title }} ({{ } @if (courseWithUsers.numberOfTeachingAssistants === undefined) { - - {{ 'artemisApp.course.tutors' | artemisTranslate }} - + }

    @@ -76,9 +72,7 @@

    {{ course.title }} ({{ } @if (courseWithUsers.numberOfEditors === undefined) { - - {{ 'artemisApp.course.editors' | artemisTranslate }} - + }
    @@ -93,9 +87,7 @@

    {{ course.title }} ({{ } @if (courseWithUsers.numberOfInstructors === undefined) { - - {{ 'artemisApp.course.instructors' | artemisTranslate }} - + }

    @@ -116,7 +108,7 @@

    {{ course.title }} ({{
    - {{ 'artemisApp.course.releasedSoon' | artemisTranslate }} +
    @if (showFutureExercises) {
    @@ -139,7 +131,7 @@

    {{ course.title }} ({{
    - {{ 'artemisApp.course.currentWorking' | artemisTranslate }} +
    @if (showCurrentExercises) {
    @@ -162,7 +154,7 @@

    {{ course.title }} ({{
    - {{ 'artemisApp.course.inAssessment' | artemisTranslate }} +
    @if (showExercisesInAssessment) {
    @@ -185,7 +177,7 @@

    {{ course.title }} ({{
    - {{ 'artemisApp.course.pastExercises' | artemisTranslate: { amount: pastExercises.length, total: pastExerciseCount } }} +
    @if (showPastExercises) {
    @@ -206,7 +198,7 @@

    {{ course.title }} ({{ } @if ((futureExercises?.length || 0) + (currentExercises?.length || 0) + (exercisesInAssessment?.length || 0) + (pastExercises?.length || 0) === 0) {
    -

    {{ 'artemisApp.course.noExercises' | artemisTranslate }}

    +

    }

    @@ -240,7 +232,7 @@

    {{ 'artemisApp.course.noExer id="course-card-open-exams" > - {{ 'entity.action.exams' | artemisTranslate }} + } @if (course.isAtLeastTutor) { @@ -252,7 +244,7 @@

    {{ 'artemisApp.course.noExer id="course-card-open-exercises" > - {{ 'entity.action.exercise' | artemisTranslate }} + } @if (course.isAtLeastEditor) { @@ -263,7 +255,7 @@

    {{ 'artemisApp.course.noExer id="course-card-open-lectures" > - {{ 'entity.action.lecture' | artemisTranslate }} + } @if (course.isAtLeastTutor) { @@ -274,7 +266,7 @@

    {{ 'artemisApp.course.noExer id="course-card-open-open-statistics" > - {{ 'artemisApp.courseStatistics.statistics' | artemisTranslate }} + } @if (isCommunicationEnabled(course) && course.isAtLeastTutor) { @@ -284,7 +276,7 @@

    {{ 'artemisApp.course.noExer [ngbTooltip]="'artemisApp.courseOverview.menu.communication' | artemisTranslate" > - {{ 'artemisApp.metis.communication.label' | artemisTranslate }} + } @if (course.timeZone || course.isAtLeastInstructor) { @@ -332,7 +324,7 @@

    {{ 'artemisApp.course.noExer id="course-card-open-assessment-dashboard" > - {{ 'entity.action.assessmentDashboard' | artemisTranslate }} + } @if (course.isAtLeastInstructor) { @@ -343,7 +335,7 @@

    {{ 'artemisApp.course.noExer id="course-card-open-scores" > - {{ 'entity.action.scores' | artemisTranslate }} + }

    diff --git a/src/main/webapp/app/course/manage/overview/course-management-card.scss b/src/main/webapp/app/course/manage/overview/course-management-card.scss index 2d3561b67c33..d48a2845f0e7 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-card.scss +++ b/src/main/webapp/app/course/manage/overview/course-management-card.scss @@ -17,13 +17,14 @@ transition: 0.15s; background-color: var(--background-color-for-hover) !important; - &:hover { - background-color: color-mix(in srgb, var(--background-color-for-hover), transparent 15%) !important; - } display: flex; cursor: pointer; height: 100px; + &:hover { + background-color: color-mix(in srgb, var(--background-color-for-hover), transparent 15%) !important; + } + > div { flex: 0 1 auto; } @@ -210,7 +211,11 @@ @media screen and (max-width: 850px) { .card { + height: unset; .card-heading { + height: unset; + max-height: 135px; + .card-header-left { flex-direction: column; max-width: 110px; @@ -244,12 +249,7 @@ display: flex; align-content: center; } - - height: unset; - max-height: 135px; } - - height: unset; } } diff --git a/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.html b/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.html index f4940c20c0c4..e5f8cdd9dee2 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.html +++ b/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.html @@ -25,7 +25,7 @@
    - {{ 'artemisApp.course.releaseDate' | artemisTranslate }} + @if (!details.releaseDate) {
    @@ -51,7 +51,7 @@
    - {{ 'artemisApp.course.dueDate' | artemisTranslate }} + @if (!details.dueDate) {
    @@ -77,7 +77,7 @@
    - {{ 'artemisApp.course.assessmentDueDate' | artemisTranslate }} + @if (!details.assessmentDueDate) {
    @@ -105,7 +105,7 @@
    @if (statistic && rowType === exerciseRowType.CURRENT) {
    - {{ 'artemisApp.course.participations' | artemisTranslate }} + - {{ 'artemisApp.course.assessmentProgress' | artemisTranslate }} + - {{ 'artemisApp.course.averageScore' | artemisTranslate }} + diff --git a/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.html b/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.html index d6a8cb3f33ac..a66f7b342595 100644 --- a/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.html +++ b/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.html @@ -3,7 +3,7 @@
    -

    {{ 'artemisApp.plagiarism.plagiarismCases.plagiarismCase' | artemisTranslate }}

    +

    @@ -50,28 +50,26 @@

    {{ 'artemisApp.plagiarism.plagiarismCases.plagiarismCase' | artem

    -

    {{ 'artemisApp.plagiarism.plagiarismCases.conversation' | artemisTranslate }}

    +

    @if (posts && posts.length > 0) { } @if ((!posts || posts.length === 0) && createdPost) {
    - +
    }
    -

    {{ 'artemisApp.plagiarism.plagiarismCases.verdict.verdict' | artemisTranslate }}

    -

    {{ 'artemisApp.plagiarism.plagiarismCases.verdict.text' | artemisTranslate }}

    +

    +

    -
    - {{ 'artemisApp.pages.tutorialFreePeriodsManagement.timeZoneExplanation' | artemisTranslate: { timeZone: course.timeZone } }} -
    +
    diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.html index 70611f9df79b..7c274032f21b 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.html @@ -3,12 +3,12 @@ - - - - - - + + + + + + diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.html index b2c5a5875541..a4637425d034 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.html @@ -2,7 +2,7 @@ @if (isInitialized) {
    {{ 'global.field.id' | artemisTranslate }}{{ 'artemisApp.entities.tutorialFreePeriod.startDate' | artemisTranslate }}{{ 'artemisApp.entities.tutorialFreePeriod.endDate' | artemisTranslate }}{{ 'artemisApp.forms.tutorialFreePeriodForm.dateInput.labelStartTime' | artemisTranslate }}{{ 'artemisApp.forms.tutorialFreePeriodForm.dateInput.labelEndTime' | artemisTranslate }}{{ 'artemisApp.entities.tutorialFreePeriod.reason' | artemisTranslate }}
    - + @if (isTutorialGroupConfigurationCreated) { -  {{ 'artemisApp.pages.tutorialGroupsManagement.editConfigurationButton' | artemisTranslate }} +   } @if (!isTutorialGroupConfigurationCreated) { @@ -43,7 +37,7 @@

    {{ 'artemisApp.pages.checklist.title' | artemisTranslate }}

    class="btn btn-primary btn-md my-2" [class.disabled]="!isTimeZoneConfigured" > -  {{ 'artemisApp.pages.checklist.createConfiguration' | artemisTranslate }} +   }
    @@ -51,7 +45,7 @@

    {{ 'artemisApp.pages.checklist.title' | artemisTranslate }}


    diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.html index 8ff2c70883d0..57d838855af1 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.html @@ -1,7 +1,5 @@ -

    {{ 'artemisApp.pages.createTutorialGroupsConfiguration.title' | artemisTranslate }}

    - +

    +
    diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.html index 9bd9a4cf4b68..383e1f597c22 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.html @@ -1,4 +1,4 @@ -

    {{ 'artemisApp.pages.editTutorialGroupsConfiguration.title' | artemisTranslate }}

    +

    diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.html index 5f942391994b..981804efb514 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.html @@ -4,7 +4,7 @@
    - +
    -
    - {{ 'artemisApp.forms.configurationForm.periodInput.explanation' | artemisTranslate }} -
    +
    @if (periodControl?.invalid && (periodControl?.dirty || periodControl?.touched)) {
    @if (periodControl?.errors?.owlRequiredDateTimeRange || periodControl?.errors?.required) { -
    - {{ 'artemisApp.forms.configurationForm.periodInput.requiredValidationError' | artemisTranslate }} -
    +
    } @if (periodControl?.errors?.owlDateTimeParse) { -
    - {{ 'artemisApp.forms.configurationForm.periodInput.parseError' | artemisTranslate }} -
    +
    } @if (periodControl?.errors?.owlDateTimeRange) { -
    - {{ 'artemisApp.forms.configurationForm.periodInput.invalidRangeError' | artemisTranslate }} -
    +
    }
    } @@ -49,41 +41,46 @@
    - - {{ - 'artemisApp.forms.configurationForm.useTutorialGroupChannelsInput.explanation' | artemisTranslate - }} + + @if (showChannelDeletionWarning) { - + }
    - +
    - + - +
    - {{ - 'artemisApp.dialogs.createChannel.channelForm.isPublicInput.explanation' | artemisTranslate - }} +
    diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts index b649aa9613ee..257ea19ddd8d 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts @@ -38,6 +38,8 @@ import { TutorialGroupsChecklistComponent } from './tutorial-groups-checklist/tu import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { TutorialGroupFreePeriodsTableComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component'; import { TutorialGroupsExportButtonComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-export-button.component/tutorial-groups-export-button.component'; +import { ArtemisMarkdownEditorModule } from 'app/shared/markdown-editor/markdown-editor.module'; + @NgModule({ imports: [ RouterModule.forChild(tutorialGroupManagementRoutes), @@ -50,6 +52,7 @@ import { TutorialGroupsExportButtonComponent } from 'app/course/tutorial-groups/ ArtemisTutorialGroupsSharedModule, ArtemisSidePanelModule, ArtemisSharedComponentModule, + ArtemisMarkdownEditorModule, ], declarations: [ TutorialGroupsManagementComponent, diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.html index 25250488c664..9dd9e8d60e8e 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.html @@ -1,5 +1,5 @@ -

    {{ 'artemisApp.pages.createTutorialGroup.title' | artemisTranslate }}

    +

    @if (course) { } diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.html index a6e1ba53502d..47c46d6b6a74 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.html @@ -1,5 +1,5 @@ -

    {{ 'artemisApp.pages.editTutorialGroup.title' | artemisTranslate }}

    +

    @if (course) { } diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.html index 53c16975f8cb..997d0116c9b9 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.html @@ -1,38 +1,32 @@
    -

    {{ 'artemisApp.forms.scheduleForm.formTitle' | artemisTranslate }}

    +

    - +
    -
    {{ 'artemisApp.forms.scheduleForm.repetitionFrequencyInput.weeks' | artemisTranslate }}
    +
    @if (repetitionFrequencyControl?.invalid && (repetitionFrequencyControl?.dirty || repetitionFrequencyControl?.touched)) {
    @if (repetitionFrequencyControl?.errors?.min) { -
    - {{ 'artemisApp.forms.scheduleForm.repetitionFrequencyInput.minValidationError' | artemisTranslate: { min: 1 } }} -
    +
    } @if (repetitionFrequencyControl?.errors?.max) { -
    - {{ 'artemisApp.forms.scheduleForm.repetitionFrequencyInput.maxValidationError' | artemisTranslate: { max: 7 } }} -
    +
    } @if (repetitionFrequencyControl?.errors?.required) { -
    - {{ 'artemisApp.forms.scheduleForm.repetitionFrequencyInput.requiredValidationError' | artemisTranslate }} -
    +
    }
    }
    - +
    @for (weekDay of weekDays; track weekDay) { @@ -43,13 +37,13 @@

    {{ 'artemisApp.forms.scheduleForm.formTitle' | artemisTranslate }}

    - {{ 'artemisApp.forms.scheduleForm.timeInput.from' | artemisTranslate }} +
    - {{ 'artemisApp.forms.scheduleForm.timeInput.to' | artemisTranslate }} +
    @@ -58,27 +52,21 @@

    {{ 'artemisApp.forms.scheduleForm.formTitle' | artemisTranslate }}

    @if (startTimeControl?.invalid && (startTimeControl?.dirty || startTimeControl?.touched)) {
    @if (startTimeControl?.errors?.required) { -
    - {{ 'artemisApp.forms.scheduleForm.timeInput.invalidTimeRange' | artemisTranslate }} -
    +
    }
    } @if (endTimeControl?.invalid && (endTimeControl?.dirty || endTimeControl?.touched)) {
    @if (endTimeControl?.errors?.required) { -
    - {{ 'artemisApp.forms.scheduleForm.timeInput.invalidTimeRange' | artemisTranslate }} -
    +
    }
    } @if (formGroup?.invalid && (formGroup?.dirty || formGroup?.touched)) {
    @if (formGroup?.errors?.invalidTimeRange) { -
    - {{ 'artemisApp.forms.scheduleForm.timeInput.invalidTimeRange' | artemisTranslate }} -
    +
    }
    } @@ -87,10 +75,10 @@

    {{ 'artemisApp.forms.scheduleForm.formTitle' | artemisTranslate }}

    @if (parentIsOnlineControl!.value) { - + } @if (!parentIsOnlineControl!.value) { - + } - {{ - 'artemisApp.forms.tutorialGroupForm.notificationInput.help' | artemisTranslate - }} + @if (notificationControl?.invalid && (notificationControl?.dirty || notificationControl?.touched)) {
    @if (notificationControl?.errors?.maxlength) { -
    - {{ 'artemisApp.forms.tutorialGroupForm.notificationInput.maxLengthValidationError' | artemisTranslate: { max: '1000' } }} -
    +
    }
    } diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.html index a052ec715cf5..a8b564078cf0 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.html @@ -4,21 +4,21 @@ @if (isAtLeastInstructor || tutorialGroup.isUserTutor) { } @if (isAtLeastInstructor || tutorialGroup.isUserTutor) { } @if (isAtLeastInstructor) { - {{ 'entity.action.edit' | artemisTranslate }} + } diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.html index 7052e8afe23a..538b169cad14 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.html @@ -1,10 +1,10 @@
    -
    {{ 'artemisApp.pages.courseTutorialGroupOverview.sidePanel.numberOfGroups' | artemisTranslate }}
    +
    {{ tutorialGroups ? tutorialGroups.length : 0 }}
    -
    {{ 'artemisApp.pages.courseTutorialGroupOverview.sidePanel.numberOfRegistrations' | artemisTranslate }}
    +
    {{ totalNumberOfRegistrations }}
    diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.html index 2294f0c94397..ad2e2fa9e64f 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.html @@ -1,13 +1,13 @@ diff --git a/src/main/webapp/app/exam/manage/exam-management.component.ts b/src/main/webapp/app/exam/manage/exam-management.component.ts index 73b0b4c6ed85..bd55ca990c1c 100644 --- a/src/main/webapp/app/exam/manage/exam-management.component.ts +++ b/src/main/webapp/app/exam/manage/exam-management.component.ts @@ -3,14 +3,14 @@ import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { ActivatedRoute, Router } from '@angular/router'; import { Subject, Subscription } from 'rxjs'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { onError } from 'app/shared/util/global.utils'; import { AlertService } from 'app/core/util/alert.service'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { AccountService } from 'app/core/auth/account.service'; import { SortService } from 'app/shared/service/sort.service'; -import { ExamInformationDTO } from 'app/entities/exam-information.model'; +import { ExamInformationDTO } from 'app/entities/exam/exam-information.model'; import dayjs from 'dayjs/esm'; import { EventManager } from 'app/core/util/event-manager.service'; import { faClipboard, faEye, faFileImport, faListAlt, faPlus, faSort, faThList, faTimes, faUser, faWrench } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/main/webapp/app/exam/manage/exam-management.service.ts b/src/main/webapp/app/exam/manage/exam-management.service.ts index 351453dde320..e76db716af82 100644 --- a/src/main/webapp/app/exam/manage/exam-management.service.ts +++ b/src/main/webapp/app/exam/manage/exam-management.service.ts @@ -1,18 +1,18 @@ import { Injectable } from '@angular/core'; -import { ExamUserDTO } from 'app/entities/exam-user-dto.model'; -import { ExamUserAttendanceCheckDTO } from 'app/entities/exam-users-attendance-check-dto.model'; +import { ExamUserDTO } from 'app/entities/exam/exam-user-dto.model'; +import { ExamUserAttendanceCheckDTO } from 'app/entities/exam/exam-users-attendance-check-dto.model'; import { filter, map, tap } from 'rxjs/operators'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import dayjs from 'dayjs/esm'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { createRequestOption } from 'app/shared/util/request.util'; import { StudentDTO } from 'app/entities/student-dto.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExamScoreDTO } from 'app/exam/exam-scores/exam-score-dtos.model'; -import { ExamInformationDTO } from 'app/entities/exam-information.model'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; +import { ExamInformationDTO } from 'app/entities/exam/exam-information.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import { StatsForDashboard } from 'app/course/dashboards/stats-for-dashboard.model'; import { Submission, reconnectSubmissions } from 'app/entities/submission.model'; import { AccountService } from 'app/core/auth/account.service'; diff --git a/src/main/webapp/app/exam/manage/exam-status.component.html b/src/main/webapp/app/exam/manage/exam-status.component.html index 8e51dc8bc66b..a308dd071108 100644 --- a/src/main/webapp/app/exam/manage/exam-status.component.html +++ b/src/main/webapp/app/exam/manage/exam-status.component.html @@ -12,12 +12,13 @@ }
    -
    {{ 'artemisApp.examStatus.preparation.' + (isTestExam ? 'testExam.' : '') + 'examPreparation' | artemisTranslate }}
    +
    1. - {{ - 'artemisApp.examStatus.preparation.configureExercises' | artemisTranslate: { amount: exam.exerciseGroups ? exam.exerciseGroups.length : 0 } - }} + @if (configuredExercises) { } @@ -32,14 +33,14 @@
      {{ 'artemisApp.examStatus.preparation.' + (isTestExam ? } @if (maxPointExercises !== exam.examMaxPoints) { - {{ 'artemisApp.examStatus.preparation.testExam.maxPointsWrong' | artemisTranslate: { points: maxPointExercises } }} + }
    2. } @if (!isTestExam) {
    3. - {{ 'artemisApp.examStatus.preparation.registerStudents' | artemisTranslate: { registered: exam.numberOfExamUsers } }} + @if (registeredStudents) { } @@ -50,22 +51,19 @@
      {{ 'artemisApp.examStatus.preparation.' + (isTestExam ? } @if (!isTestExam) {
    4. - {{ 'artemisApp.examStatus.preparation.generateStudentExams' | artemisTranslate }} + @if (!registeredStudents) { - {{ 'artemisApp.examStatus.preparation.notRegistered' | artemisTranslate }} + } @if (registeredStudents) { - - {{ - 'artemisApp.examStatus.preparation.registered' - | artemisTranslate - : { - generated: numberOfGeneratedStudentExams, - total: exam.numberOfExamUsers, - } - }} - + } @if (generatedStudentExams) { @@ -77,7 +75,7 @@
      {{ 'artemisApp.examStatus.preparation.' + (isTestExam ? } @if (!isTestExam) {
    5. - {{ 'artemisApp.examStatus.preparation.prepareExerciseStart' | artemisTranslate }} + @if (preparedExerciseStart) { } @@ -115,13 +113,13 @@
      {{ 'artemisApp.examStatus.preparation.' + (isTestExam ?
    -
    {{ 'artemisApp.examStatus.conduction.' + (isTestExam ? 'testExam.' : '') + 'examConduction' | artemisTranslate }}
    +
    {{ exam.startDate | artemisDate }} - {{ exam.endDate | artemisDate }}
    @if (examConductionState === examConductionStateEnum.ERROR) {
    -
    {{ 'artemisApp.examStatus.conduction.' + (isTestExam ? 'testExam.' : '') + 'conductionSuspended' | artemisTranslate }}
    +
    } @if (course?.isAtLeastInstructor && examChecklist && examConductionState !== examConductionStateEnum.ERROR) { @@ -179,7 +177,7 @@
    {{ 'artemisApp.examStatus.conduction.' + (isTestExam ? 'testExam.' : '') + ' }
    -
    {{ 'artemisApp.examStatus.correction.examCorrection' | artemisTranslate }}
    +
    @if (examChecklist) {
      @if (examChecklist.numberOfTotalExamAssessmentsFinishedByCorrectionRound !== null && numberOfSubmitted !== 0) { @@ -215,9 +213,7 @@
      {{ 'artemisApp.examStatus.correction.examCorrection' | a } @if (!exam.publishResultsDate) { - - {{ 'artemisApp.examStatus.correction.notSet' | artemisTranslate }} - + }
      diff --git a/src/main/webapp/app/exam/manage/exam-status.component.ts b/src/main/webapp/app/exam/manage/exam-status.component.ts index 268e12d47d66..fafa2f201137 100644 --- a/src/main/webapp/app/exam/manage/exam-status.component.ts +++ b/src/main/webapp/app/exam/manage/exam-status.component.ts @@ -1,8 +1,8 @@ import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { faArrowRight, faCheckCircle, faCircleExclamation, faDotCircle, faTimes, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { ExamChecklistService } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist.service'; -import { ExamChecklist } from 'app/entities/exam-checklist.model'; +import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import dayjs from 'dayjs/esm'; import { round } from 'app/shared/util/utils'; import { Course } from 'app/entities/course.model'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts index 1abff9c1f9ea..826a85b0a83d 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts @@ -4,7 +4,7 @@ import { faBullhorn } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { Subscription, from } from 'rxjs'; -import { Exam } from 'app/entities/exam.model'; +import { Exam } from 'app/entities/exam/exam.model'; import { AlertService } from 'app/core/util/alert.service'; import { ExamLiveAnnouncementCreateModalComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component'; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.html b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.html index 0b05bacb6172..d6ec36ee35e0 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.html +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.html @@ -9,15 +9,14 @@