diff --git a/.github/workflows/master-push.yml b/.github/workflows/master-push.yml deleted file mode 100644 index 8e2b17ee24..0000000000 --- a/.github/workflows/master-push.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Master Update -on: - push: - branches: - - master - -jobs: - create-and-deploy-compodoc: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install dependencies - run: npm install @compodoc/compodoc - - name: Run compodoc - run: npm run compodoc - - name: Deploy on pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: doc/compodoc - destination_dir: documentation - force_orphan: true - run-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v2 - - name: Get time of commit - run: echo "TIME=$(git log -1 --pretty=format:%ct)" >> $GITHUB_ENV - - name: Run tests and upload coverage - uses: docker/build-push-action@v3 - with: - context: ./ - file: ./build/Dockerfile - builder: ${{ steps.buildx.outputs.name }} - target: builder - tags: test-image - build-args: | - UPLOAD_COVERAGE=${{ true }} - GIT_COMMIT_SHA=${{ github.sha }} - GIT_BRANCH=master - GIT_COMMITTED_AT=${{ env.TIME }} - CC_TEST_REPORTER_ID=${{ secrets.CODE_CLIMATE_ID }} - cache-from: type=gha - cache-to: type=gha,mode=max - check-dependencies: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/node@master - continue-on-error: true # To make sure that SARIF upload gets called - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --sarif-file-output=snyk.sarif - - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: snyk.sarif diff --git a/.github/workflows/on-master-push.yml b/.github/workflows/on-master-push.yml new file mode 100644 index 0000000000..7894fd07f3 --- /dev/null +++ b/.github/workflows/on-master-push.yml @@ -0,0 +1,99 @@ +name: Master Push + +on: + push: + branches: + - master + +jobs: + create-and-deploy-compodoc: + runs-on: ubuntu-latest + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + + - name: Install dependencies + run: npm install @compodoc/compodoc + + - name: Run compodoc + run: npm run compodoc + + - name: Deploy on pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: doc/compodoc + destination_dir: documentation + force_orphan: true + + test: + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 + os: + - ubuntu-latest + - [ self-hosted, Linux, ARM64 ] + exclude: + - platform: linux/arm64 + os: ubuntu-latest + - platform: linux/amd64 + os: [ self-hosted, Linux, ARM64 ] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Run tests in Docker image + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + context: ./ + file: ./build/Dockerfile + builder: ${{ steps.buildx.outputs.name }} + target: test + + - name: Run tests with timezone + if: ${{ matrix.platform == 'linux/amd64' }} + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + context: ./ + file: ./build/Dockerfile + builder: ${{ steps.buildx.outputs.name }} + target: test + build-args: | + TZ=America/Detroit + + pre-release: + runs-on: ubuntu-latest + needs: + - test + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v4 + + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v4 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + with: + extra_plugins: | + semantic-release-replace-plugin@1.2.6 + @semantic-release/git + @semantic-release/github + semantic-release-slack-bot diff --git a/.github/workflows/on-official-release-push.yml b/.github/workflows/on-official-release-push.yml new file mode 100644 index 0000000000..256bf2a1cc --- /dev/null +++ b/.github/workflows/on-official-release-push.yml @@ -0,0 +1,42 @@ +name: Semantic Release +on: + push: + branches: + - official-release + +jobs: + pre-releases-cleanup: + runs-on: ubuntu-latest + steps: + - name: Delete Previous Pre-Releases + # todo: not maintained anymore, needs replacement + uses: dev-drprasad/delete-older-releases@v0.3.4 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + with: + keep_latest: 0 + delete_tag_pattern: master + delete_tags: true + + release: + runs-on: ubuntu-latest + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up node + uses: actions/setup-node@v4 + + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v4 + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + with: + extra_plugins: | + semantic-release-replace-plugin@1.2.6 + @semantic-release/git + @semantic-release/github + semantic-release-slack-bot diff --git a/.github/workflows/percy.yml b/.github/workflows/percy.yml index b75a898aa4..5bbba45329 100644 --- a/.github/workflows/percy.yml +++ b/.github/workflows/percy.yml @@ -12,12 +12,16 @@ jobs: if: github.event_name == 'push' || github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - name: Checkout repository files + uses: actions/checkout@v4 + + - name: Set up node + uses: actions/setup-node@v4 with: node-version: "16" - cache: npm + - run: npm ci + - run: npm run percy-storybook env: PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} diff --git a/.github/workflows/pre-release-cleanup.yml b/.github/workflows/pre-release-cleanup.yml deleted file mode 100644 index fafae888bd..0000000000 --- a/.github/workflows/pre-release-cleanup.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Pre-Release Cleanup -on: - push: - branches: - - official-release - -jobs: - remove-pre-releases: - runs-on: ubuntu-latest - steps: - - name: Delete Previous Pre-Releases - uses: dev-drprasad/delete-older-releases@v0.2.0 - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - with: - keep_latest: 0 - delete_tag_pattern: master - delete_tags: true diff --git a/.github/workflows/project-management-workflow.yml b/.github/workflows/project-management-workflow.yml index 1d1d9dd634..f6fe83d99d 100644 --- a/.github/workflows/project-management-workflow.yml +++ b/.github/workflows/project-management-workflow.yml @@ -8,18 +8,21 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: move-assigned-issues-to-column-in-progress + # todo: not maintained anymore, replace this uses: docker://takanabe/github-actions-automate-projects:v0.0.1 if: github.event_name == 'issues' && github.event.action == 'assigned' env: GITHUB_PROJECT_URL: https://github.com/Aam-Digital/ndb-core/projects/6 GITHUB_PROJECT_COLUMN_NAME: In progress - name: move-unassigned-issues-to-column-todo + # todo: not maintained anymore, replace this uses: docker://takanabe/github-actions-automate-projects:v0.0.1 if: github.event_name == 'issues' && github.event.action == 'unassigned' env: GITHUB_PROJECT_URL: https://github.com/Aam-Digital/ndb-core/projects/6 GITHUB_PROJECT_COLUMN_NAME: To do - name: add-issues-with-current-milestone-to-project + # todo: not maintained anymore, replace this uses: docker://takanabe/github-actions-automate-projects:v0.0.1 if: github.event.action == 'milestoned' && github.issue.milestone.html_url == 'https://github.com/Aam-Digital/ndb-core/milestone/19' env: diff --git a/.github/workflows/pull-request-closed.yml b/.github/workflows/pull-request-closed.yml index 93db897975..fe3f47a38f 100644 --- a/.github/workflows/pull-request-closed.yml +++ b/.github/workflows/pull-request-closed.yml @@ -1,15 +1,22 @@ -name: CleanUp +name: Pull Request - Clean Up + on: pull_request_target: types: [closed] +env: + GH_TOKEN: ${{ github.token }} + jobs: remove-deployment: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout repository files + uses: actions/checkout@v4 + - name: Stop container and remove folder uses: appleboy/ssh-action@master + continue-on-error: true with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USERNAME }} @@ -19,9 +26,17 @@ jobs: docker compose down cd .. rm -r pr-${{ github.event.number }} + - name: Delete Docker tag + continue-on-error: true run: | token=$(curl -s -L 'https://hub.docker.com/v2/users/login' -H 'Content-Type: application/json' -d '{ "username": "${{ secrets.DOCKER_USERNAME }}", "password": "${{ secrets.DOCKER_PASSWORD }}"}') token=${token#*\"token\":\"} token=${token%%\"*} curl -s -L -X DELETE "https://hub.docker.com/v2/namespaces/aamdigital/repositories/ndb-server/tags/pr-${{ github.event.number }}" -H "Authorization: Bearer $token" + + - name: Delete github caches for current branch + run: | + gh cache list -L 200 --ref "refs/pull/${{ github.event.number }}/merge" --json id --jq '.[] | .id' | while read -r id; do + gh cache delete "$id" + done diff --git a/.github/workflows/pull-request-opened.yml b/.github/workflows/pull-request-opened.yml index d09b572571..97a9b10854 100644 --- a/.github/workflows/pull-request-opened.yml +++ b/.github/workflows/pull-request-opened.yml @@ -1,4 +1,4 @@ -name: SetUp +name: Pull Request - Set Up permissions: pull-requests: write @@ -11,7 +11,9 @@ jobs: create-deployment: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout repository files + uses: actions/checkout@v4 + - name: Prepare server deployment uses: appleboy/ssh-action@master with: @@ -22,6 +24,7 @@ jobs: cd /var/docker cp -r x-pr pr-${{ github.event.number }} sed -i "s//${{ github.event.number }}/g" pr-${{ github.event.number }}/docker-compose.yml + - name: Post comment on PR run: | curl -X POST \ diff --git a/.github/workflows/pull-request-update-or-push-tag.yml b/.github/workflows/pull-request-update-or-push-tag.yml new file mode 100644 index 0000000000..887dbb918b --- /dev/null +++ b/.github/workflows/pull-request-update-or-push-tag.yml @@ -0,0 +1,387 @@ +name: pull-request-update-or-push-tag + +on: + pull_request: + push: + tags: + - "*" + +env: + REGISTRY_IMAGE: aamdigital/ndb-server + +jobs: + prepare-code-coverage: + runs-on: ubuntu-latest + env: + GIT_COMMIT_SHA: ${{ github.sha }} + CC_TEST_REPORTER_ID: ${{ secrets.CODE_CLIMATE_ID }} + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + + - name: Extract tag (release) + if: ${{ github.event.ref != '' }} + run: echo "GIT_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Extract tag (pull request) + if: ${{ github.event.ref == '' }} + run: echo "GIT_BRANCH=pr-${{ github.event.number }}" >> $GITHUB_ENV + + - name: Install Code Climate Test Reporter + run: | + curl -Lo cc-test-reporter https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 + chmod +x ./cc-test-reporter + + - name: Get time of commit + run: echo "GIT_COMMITTED_AT=$(git log -1 --pretty=format:%ct)" >> $GITHUB_ENV + + - name: Run CC before-build + run: | + ./cc-test-reporter before-build + + test: + needs: + - prepare-code-coverage + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 + os: + - ubuntu-latest + - [ self-hosted, Linux, ARM64 ] + exclude: + - platform: linux/arm64 + os: ubuntu-latest + - platform: linux/amd64 + os: [ self-hosted, Linux, ARM64 ] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + + - name: Prepare Platform + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Get time of commit + run: echo "TIME=$(git log -1 --pretty=format:%ct)" >> $GITHUB_ENV + + - name: Create export directories + run: | + mkdir -p ./tmp/coverage + mkdir -p ./tmp/coverage/${{ env.PLATFORM_PAIR }} + + - name: Run tests in Docker image + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + context: ./ + file: ./build/Dockerfile + builder: ${{ steps.buildx.outputs.name }} + outputs: type=local,dest=./tmp/coverage/${{ env.PLATFORM_PAIR }} + target: test-coverage + cache-from: type=gha + + - name: Run tests with timezone + if: ${{ matrix.platform == 'linux/amd64' }} + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + context: ./ + file: ./build/Dockerfile + builder: ${{ steps.buildx.outputs.name }} + target: test + cache-from: type=gha + build-args: | + TZ=America/Detroit + + - name: Upload coverage reports + uses: actions/upload-artifact@v4 + with: + name: coverage-${{ env.PLATFORM_PAIR }} + path: ./tmp/coverage/* + if-no-files-found: error + retention-days: 1 + + upload-code-coverage: + runs-on: ubuntu-latest + needs: + - test + env: + GIT_COMMIT_SHA: ${{ github.sha }} + CC_TEST_REPORTER_ID: ${{ secrets.CODE_CLIMATE_ID }} + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + + - name: Extract tag (release) + if: ${{ github.event.ref != '' }} + run: echo "GIT_BRANCH=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Extract tag (pull request) + if: ${{ github.event.ref == '' }} + run: echo "GIT_BRANCH=pr-${{ github.event.number }}" >> $GITHUB_ENV + + - name: Download coverage reports + uses: actions/download-artifact@v4 + with: + path: ./tmp/coverage + pattern: coverage-* + merge-multiple: true + + - name: Install Code Climate Test Reporter + run: | + curl -Lo cc-test-reporter https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 + chmod +x ./cc-test-reporter + + - name: Run CC format-coverage + run: | + ./cc-test-reporter format-coverage "./tmp/coverage/linux-amd64/coverage/lcov.info" -t lcov + + - name: Run CC upload-coverage + run: | + ./cc-test-reporter upload-coverage + + build: + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 + os: + - ubuntu-latest + - [self-hosted, Linux, ARM64] + exclude: + - platform: linux/arm64 + os: ubuntu-latest + - platform: linux/amd64 + os: [self-hosted, Linux, ARM64] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository files + uses: actions/checkout@v4 + + - name: Extract tag (release) + if: ${{ github.event.ref != '' }} + run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Extract tag (pull request) + if: ${{ github.event.ref == '' }} + run: echo "TAG=pr-${{ github.event.number }}" >> $GITHUB_ENV + + - name: Prepare Platform + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Create export directories + run: | + mkdir -p ./tmp/build + mkdir -p ./tmp/build/${{ env.PLATFORM_PAIR }} + mkdir -p ./tmp/digests + + - name: Build + id: build + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + context: ./ + file: ./build/Dockerfile + target: dist-build + outputs: type=local,dest=./tmp/build/${{ env.PLATFORM_PAIR }} + cache-from: type=gha + cache-to: type=gha,mode=min + build-args: | + APP_VERSION=${{ env.TAG }} + env: + SOURCE_DATE_EPOCH: 0 + + - name: Install Sentry CLI + run: | + npm install -g @sentry/cli + + - name: Inject Sentry debug information + run: | + sentry-cli sourcemaps inject ./tmp/build/${{ env.PLATFORM_PAIR }}/dist + + - name: Build Image and push by digest + id: build-image + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + context: ./ + file: ./build/Dockerfile + target: app + labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true + cache-from: type=gha + cache-to: type=gha,mode=min + build-args: | + DIST_TYPE=local + LOCAL_DIST_PATH=./tmp/build/${{ env.PLATFORM_PAIR }}/dist + env: + SOURCE_DATE_EPOCH: 0 + + - name: Export digest + run: | + digest="${{ steps.build-image.outputs.digest }}" + touch "./tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ./tmp/digests/* + if-no-files-found: error + retention-days: 1 + + - name: Upload build archive + uses: actions/upload-artifact@v4 + with: + name: build-${{ env.PLATFORM_PAIR }} + path: ./tmp/build/${{ env.PLATFORM_PAIR }} + if-no-files-found: error + retention-days: 1 + + upload-sourcemaps: + runs-on: ubuntu-latest + needs: + - build + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + steps: + - name: Download builds + uses: actions/download-artifact@v4 + with: + path: ./tmp/build + pattern: build-* + + - name: Extract tag (release) + if: ${{ github.event.ref != '' }} + run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Extract tag (pull request) + if: ${{ github.event.ref == '' }} + run: echo "TAG=pr-${{ github.event.number }}" >> $GITHUB_ENV + + - name: Install Sentry CLI + run: | + npm install -g @sentry/cli + + - name: Upload sourcemaps + run: | + ls -lsa ./tmp/build + ls -lsa ./tmp/build/build-linux-amd64 + # todo + sentry-cli sourcemaps upload --release="ndb-core@${{ env.TAG }}" ./tmp/build/build-linux-amd64/dist + sentry-cli sourcemaps upload --release="ndb-core@${{ env.TAG }}" ./tmp/build/build-linux-arm64/dist + + merge: + runs-on: ubuntu-latest + needs: + - build + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ./tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Create manifest list and push + working-directory: ./tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} + + release: + runs-on: ubuntu-latest + needs: + - merge + - upload-sourcemaps + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + steps: + - name: Extract tag (release) + if: ${{ github.event.ref != '' }} + run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Extract tag (pull request) + if: ${{ github.event.ref == '' }} + run: echo "TAG=pr-${{ github.event.number }}" >> $GITHUB_ENV + + - name: Install Sentry CLI + run: | + npm install -g @sentry/cli + + - name: Create Release + run: | + sentry-cli releases new "ndb-core@${{ env.TAG }}" + + deploy: + runs-on: ubuntu-latest + needs: + - release + steps: + - name: Deploy updated image + if: ${{ github.event.ref == '' }} + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USERNAME }} + key: ${{ secrets.SSH_KEY }} + script: | + cd /var/docker/pr-${{ github.event.number }} + docker compose pull + docker compose up -d diff --git a/.github/workflows/pull-request-update.yml b/.github/workflows/pull-request-update.yml deleted file mode 100644 index 17cd9ad246..0000000000 --- a/.github/workflows/pull-request-update.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Pipeline -on: pull_request - -jobs: - run-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v2 - - name: Get time of commit - run: echo "TIME=$(git log -1 --pretty=format:%ct)" >> $GITHUB_ENV - - name: Run tests in Docker image - uses: docker/build-push-action@v3 - with: - context: ./ - file: ./build/Dockerfile - builder: ${{ steps.buildx.outputs.name }} - target: builder - tags: test-image - build-args: | - UPLOAD_COVERAGE=${{ true }} - GIT_COMMIT_SHA=${{ github.event.pull_request.head.sha }} - GIT_BRANCH=${{ github.head_ref }} - GIT_COMMITTED_AT=${{ env.TIME }} - CC_TEST_REPORTER_ID=${{ secrets.CODE_CLIMATE_ID }} - BUILD=${{ false }} - cache-from: type=gha - cache-to: type=gha,mode=max - - name: Run tests with timezone - uses: docker/build-push-action@v3 - if: false # disable additional timezone tests for now - with: - context: ./ - file: ./build/Dockerfile - builder: ${{ steps.buildx.outputs.name }} - target: builder - build-args: | - RUN_TESTS=${{ true }} - TZ=America/Detroit - BUILD=${{ false }} - cache-from: type=gha - cache-to: type=gha,mode=max - deploy-prod-image: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push PR image - uses: docker/build-push-action@v5 - with: - context: ./ - file: ./build/Dockerfile - platforms: linux/amd64,linux/arm64 - push: true - tags: aamdigital/ndb-server:pr-${{ github.event.number }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Deploy updated image - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USERNAME }} - key: ${{ secrets.SSH_KEY }} - script: | - cd /var/docker/pr-${{ github.event.number }} - docker compose pull - docker compose up -d diff --git a/.github/workflows/security-code-scan.yml b/.github/workflows/security-code-scan.yml new file mode 100644 index 0000000000..5ef6ed0e1f --- /dev/null +++ b/.github/workflows/security-code-scan.yml @@ -0,0 +1,30 @@ +name: "Security Scan" + +on: + push: + branches: + - master + schedule: + - cron: '30 6 * * 2' + +jobs: + security-scan: + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/node@master + continue-on-error: true # To make sure that SARIF upload gets called + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --sarif-file-output=results.sarif + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml deleted file mode 100644 index cb69fdcdae..0000000000 --- a/.github/workflows/semantic-release.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Semantic Release -on: - push: - branches: - - master - - official-release - -jobs: - run-semantic-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - persist-credentials: false - # Only required temporary: https://github.com/cycjimmy/semantic-release-action/issues/159 - - uses: actions/setup-node@v3 - with: - node-version: 14 - - name: Semantic Release - uses: cycjimmy/semantic-release-action@v3 - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} - with: - # Latest release-replace plugin fails https://github.com/jpoehnelt/semantic-release-replace-plugin/issues/223 - extra_plugins: | - semantic-release-replace-plugin@1.2.0 - @semantic-release/git - @semantic-release/github - semantic-release-slack-bot diff --git a/.github/workflows/tagged-commit.yml b/.github/workflows/tagged-commit.yml deleted file mode 100644 index f5fe8d823a..0000000000 --- a/.github/workflows/tagged-commit.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: New Tag -on: - push: - tags: - - "*" - -jobs: - publish-image: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v2 - - name: Extract tag - run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - name: Build and push prod image - uses: docker/build-push-action@v3 - with: - context: ./ - file: ./build/Dockerfile - builder: ${{ steps.buildx.outputs.name }} - push: true - tags: aamdigital/ndb-server:${{ env.TAG }} - cache-from: type=gha - cache-to: type=gha,mode=max - build-args: | - APP_VERSION=${{ env.TAG }} - SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_TOKEN }} - SENTRY_ORG=${{ secrets.SENTRY_ORG }} - SENTRY_PROJECT=${{ secrets.SENTRY_PROJECT }} - - name: If new release, delete pre-release tags from Docker Hub - if: ${{ ! contains(env.TAG, '-master.') }} - run: | - token=$(curl -s -L 'https://hub.docker.com/v2/users/login' -H 'Content-Type: application/json' -d '{ "username": "${{ secrets.DOCKER_USERNAME }}", "password": "${{ secrets.DOCKER_PASSWORD }}"}') - token=${token#*\"token\":\"} - token=${token%%\"*} - curl -s -L "https://hub.docker.com/v2/namespaces/aamdigital/repositories/ndb-server/tags?page_size=100" -H "Authorization: Bearer $token" | jq '.results[].name | select(test("-master.")?)' | xargs -n1 -I %TAG curl -s -L -X DELETE "https://hub.docker.com/v2/namespaces/aamdigital/repositories/ndb-server/tags/%TAG" -H "Authorization: Bearer $token" diff --git a/angular.json b/angular.json index f39f78efcb..b55ae9bd8a 100644 --- a/angular.json +++ b/angular.json @@ -17,7 +17,7 @@ "outputPath": "dist", "index": "src/index.html", "main": "src/main.ts", - "tsConfig": "src/tsconfig.app.json", + "tsConfig": "tsconfig.app.json", "polyfills": "src/polyfills.ts", "i18nMissingTranslation": "warning", "assets": [ @@ -109,7 +109,7 @@ "src/polyfills.ts", "zone.js/testing" ], - "tsConfig": "src/tsconfig.spec.json", + "tsConfig": "tsconfig.spec.json", "styles": [ "src/styles/styles.scss", "src/styles/themes/ndb-theme.scss" @@ -198,9 +198,12 @@ } }, "cli": { - "analytics": "0bd2cce5-bfb0-4375-af96-a8222d782810", + "cache": { + "enabled": true + }, "schematicCollections": [ "@angular-eslint/schematics" - ] + ], + "analytics": "3a23cbc9-8780-4947-bdf1-57719d16d60d" } } diff --git a/build/Dockerfile b/build/Dockerfile index 411e9e7873..df9e1f162e 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,78 +1,127 @@ # This docker image can be used to run the application locally. -# To use it only Docker needs to be installed locally -# Run the following commands from the root folder to build, run and kill the application -# >> docker build -f build/Dockerfile -t aam-digital . +# To use it only Docker needs to be installed locally. + +# Run the following commands from the ndb-core root folder: +## Run tests: +# >> docker build --target test -f build/Dockerfile -t aam-digital . + +## Build the application: +# >> docker build --target dist-build -f build/Dockerfile -o . -t aam-digital . + +## Build and start application container: +# >> docker build --target app -f build/Dockerfile -t aam-digital . # >> docker run -p=80:80 aam-digital -FROM node:18.20.4-alpine3.20 AS builder -WORKDIR /app -COPY package*.json ./ +# DIST_TYPE +# --build-arg DIST_TYPE=build +# -> build (default): will build the application +# -> local: will use existing $LOCAL_DIST_PATH (default ./dist/) folder from file system +# Will be used for packaging the application into a web container. +# Just relevant for the stage "app" +ARG DIST_TYPE="build" + +FROM node:lts-slim AS base +COPY package.json ./opt/app/package.json +COPY package-lock.json ./opt/app/package-lock.json + + +FROM base AS base-with-dependencies +WORKDIR /opt/app RUN npm ci --no-progress -RUN npm run-script ng version -COPY . . +FROM debian:bookworm-slim AS base-with-dependencies-and-testsuite +COPY --from=base-with-dependencies /opt/app /opt/app + +RUN apt-get update && apt-get install -y curl +WORKDIR /tmp +# 121.0.6167.139-1~deb12u1 -> 2024-02-01 +ARG CHROMIUM_VERSION=121.0.6167.139-1~deb12u1 +RUN curl -o chromium-common.deb https://ftp.debian.org/debian/pool/main/c/chromium/chromium-common_${CHROMIUM_VERSION}_$(dpkg --print-architecture).deb +RUN curl -o chromium.deb https://ftp.debian.org/debian/pool/main/c/chromium/chromium_${CHROMIUM_VERSION}_$(dpkg --print-architecture).deb +RUN apt-get install -y ./chromium-common.deb ./chromium.deb build-essential git libssl-dev + +SHELL ["/bin/bash", "--login", "-c"] +RUN curl -o- install_nvm.sh https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash +RUN nvm install 20 + +WORKDIR /opt/app +RUN curl -o cc-test-reporter https://codeclimate.com/downloads/test-reporter/test-reporter-0.11.1-linux-$(dpkg --print-architecture) && \ + chmod +x ./cc-test-reporter + + +FROM base-with-dependencies-and-testsuite AS test +WORKDIR /opt/app +COPY ./src /opt/app/src/ +COPY ./e2e /opt/app/e2e/ +COPY ./build /opt/app/build/ +COPY ./.browserslistrc /opt/app/ +COPY ./.codeclimate.yml /opt/app/ +COPY ./.eslintrc.json /opt/app/ +COPY ./.prettierignore /opt/app/ +COPY ./.prettierrc.json /opt/app/ +COPY ./angular.json /opt/app/ +COPY ./cypress.config.ts /opt/app/ +COPY ./karma.conf.js /opt/app/ +COPY ./tsconfig.app.json /opt/app/ +COPY ./tsconfig.spec.json /opt/app/ +COPY ./tsconfig.json /opt/app/ +COPY ./ngsw-config.json /opt/app/ + +ARG TZ +RUN if [ -n "${TZ}" ] ; then \ + apt-get install -y tzdata && \ + cp /usr/share/zoneinfo/Europe/Brussels /etc/localtime && \ + echo "$TZ" > /etc/timezone ; fi + +RUN npm run lint +ARG CHROME_BIN=/usr/lib/chromium/chromium +RUN npm run test-ci + + +FROM scratch AS test-coverage +COPY --from=test /opt/app/coverage/ ./coverage/ + + +FROM base-with-dependencies AS build +WORKDIR /opt/app +COPY ./src /opt/app/src/ +COPY ./build /opt/app/build/ +COPY ./angular.json /opt/app/ +COPY ./tsconfig.json /opt/app/ +COPY ./tsconfig.app.json /opt/app/ +COPY ./tsconfig.spec.json /opt/app/ +COPY ./ngsw-config.json /opt/app/ ARG APP_VERSION="UNKNOWN" RUN sed -i "s/appVersion: \".*\"/appVersion: \"$APP_VERSION\"/g" src/environments/environment*.ts +RUN node build/prepare-translation-files.js +RUN npm run build -# When set to false, no production build for the app is done -ARG BUILD=true -RUN if [ "$BUILD" = true ] ; then \ - node build/prepare-translation-files.js &&\ - npm run build ; fi +RUN if [ "$INJECT_DEBUG_INFORMATION" = true ] ; then \ + npm install -g @sentry/cli && \ + sentry-cli sourcemaps inject ./dist ; fi -# When set to true, tests are run and coverage will be uploaded to CodeClimate -ARG UPLOAD_COVERAGE=false -RUN if [ "$UPLOAD_COVERAGE" = true ] ; then \ - apk --no-cache add curl &&\ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter &&\ - chmod +x ./cc-test-reporter &&\ - ./cc-test-reporter before-build ; fi +FROM scratch AS dist-build +COPY --from=build /opt/app/dist/ ./dist/ -ARG TZ -RUN if [ -n "${TZ}" ] ; then \ - apk --no-cache add tzdata && \ - cp /usr/share/zoneinfo/Europe/Brussels /etc/localtime && \ - echo "$TZ" > /etc/timezone ; fi - -# When set to true, chromium is installed an tests are executed -ARG RUN_TESTS=false -ARG CHROME_BIN=/usr/bin/chromium-browser -# Install chromium for karma, lint code and run tests -RUN if [ "$RUN_TESTS" = true ] || [ "$UPLOAD_COVERAGE" = true ] ; then \ - apk --no-cache add chromium &&\ - npm run lint &&\ - npm run test-ci ; fi - -# The following arguments need to be provided for the code climate test reporter to work correctly -# The commit sha -ARG GIT_COMMIT_SHA -# The branch -ARG GIT_BRANCH -# The time of the commit, can be extracted with `git log -1 --pretty=format:%ct` -ARG GIT_COMMITTED_AT -# The ID for the test reporter, can be found on CodeCoverage -ARG CC_TEST_REPORTER_ID -RUN if [ "$UPLOAD_COVERAGE" = true ] ; then ./cc-test-reporter after-build --debug ; fi - -# Information required to upload the sourcemap to sentry -# If not set, nothing is uploaded -ARG SENTRY_AUTH_TOKEN -ARG SENTRY_ORG -ARG SENTRY_PROJECT -RUN if [ "$SENTRY_AUTH_TOKEN" != "" ] ; then \ - npm install -g @sentry/cli &&\ - sentry-cli --auth-token="$SENTRY_AUTH_TOKEN" releases --org="$SENTRY_ORG" --project="$SENTRY_PROJECT" files "ndb-core@$APP_VERSION" upload-sourcemaps dist && \ - rm dist/*.map ; fi -### PROD image +FROM scratch AS dist-local +ARG LOCAL_DIST_PATH=./dist/ +COPY $LOCAL_DIST_PATH ./dist/ + -FROM nginx:1.26.1-alpine +FROM dist-${DIST_TYPE} AS dist + + +### PROD image +FROM nginxinc/nginx-unprivileged:alpine AS app COPY ./build/default.conf /etc/nginx/templates/default.conf -COPY --from=builder /app/dist/ /usr/share/nginx/html + +COPY --from=dist /dist/ /usr/share/nginx/html + # The port on which the app will run in the Docker container ENV PORT=80 # The url to the CouchDB database @@ -88,10 +137,10 @@ ENV CSP_REPORT_URI="https://o167951.ingest.sentry.io/api/1242399/security/" # overwrite the Content-Security-Policy rules (report-uri is added automatically) # default includes all required whitelists for production server # to disable any CSP blocking, set to "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'" -ENV CSP="default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob: https://*.tile.openstreetmap.org/ https://matomo.aam-digital.org https://*.aam-digital.com https://api.github.com/repos/Aam-Digital/ https://sentry.io $CSP_REPORT_URI; style-src 'self' 'unsafe-inline'" +ENV CSP="default-src 'self' 'unsafe-eval' data: blob: https://*.tile.openstreetmap.org/ https://matomo.aam-digital.org https://*.aam-digital.com https://api.github.com/repos/Aam-Digital/ https://sentry.io $CSP_REPORT_URI; style-src 'self' 'unsafe-inline'" # 'unsafe-eval' required for pouchdb https://github.com/pouchdb/pouchdb/issues/7853#issuecomment-535020600 -# TODO remove 'unsave-inline' and fix the reported issues # variables are inserted into the nginx config -CMD envsubst '$$PORT $$COUCHDB_URL $$QUERY_URL $$NOMINATIM_URL $$CSP $$CSP_REPORT_URI' < /etc/nginx/templates/default.conf > /etc/nginx/conf.d/default.conf &&\ - nginx -g 'daemon off;' +RUN envsubst '$$PORT $$COUCHDB_URL $$QUERY_URL $$NOMINATIM_URL $$CSP $$CSP_REPORT_URI' < /etc/nginx/templates/default.conf > /etc/nginx/conf.d/default.conf + +CMD ["nginx", "-g", "daemon off;"] diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json index 23544f7352..627f03a522 100644 --- a/e2e/tsconfig.json +++ b/e2e/tsconfig.json @@ -1,8 +1,10 @@ { "extends": "../tsconfig.json", "include": [ - "integration" + "**/*.ts", + "../cypress.config.ts" ], + "exclude": [], "compilerOptions": { "sourceMap": false, "types": [ diff --git a/karma.conf.js b/karma.conf.js index 033d25a75d..a0b61baa63 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,20 +1,3 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - // Karma configuration file, see link for more information // https://karma-runner.github.io/0.13/config/configuration-file.html diff --git a/package-lock.json b/package-lock.json index d3976353ed..727d331c61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,42 +9,42 @@ "version": "0.0.0", "license": "GPL-3.0", "dependencies": { - "@angular/animations": "^17.3.12", - "@angular/cdk": "^17.3.10", - "@angular/common": "^17.3.12", - "@angular/compiler": "^17.3.12", - "@angular/core": "^17.3.12", - "@angular/forms": "^17.3.12", - "@angular/localize": "^17.3.12", - "@angular/material": "^17.3.10", - "@angular/material-moment-adapter": "^17.3.10", - "@angular/platform-browser": "^17.3.12", - "@angular/platform-browser-dynamic": "^17.3.12", - "@angular/router": "^17.3.12", - "@angular/service-worker": "^17.3.12", + "@angular/animations": "^18.2.0", + "@angular/cdk": "^18.2.0", + "@angular/common": "^18.2.0", + "@angular/compiler": "^18.2.0", + "@angular/core": "^18.2.0", + "@angular/forms": "^18.2.0", + "@angular/localize": "^18.2.0", + "@angular/material": "^18.2.0", + "@angular/material-moment-adapter": "^18.2.0", + "@angular/platform-browser": "^18.2.0", + "@angular/platform-browser-dynamic": "^18.2.0", + "@angular/router": "^18.2.0", + "@angular/service-worker": "^18.2.0", "@aytek/material-color-picker": "^1.0.4", "@casl/ability": "^6.7.1", "@casl/angular": "^8.2.7", "@faker-js/faker": "^8.4.1", - "@fortawesome/angular-fontawesome": "^0.14.1", + "@fortawesome/angular-fontawesome": "^0.15.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@ngneat/until-destroy": "^10.0.0", - "@sentry/angular": "^8.20.0", + "@sentry/angular": "^8.26.0", "angulartics2": "^14.0.0", "assert": "^2.1.0", "crypto-es": "^2.1.0", "deep-object-diff": "^1.1.9", "hammerjs": "^2.0.8", "json-query": "^2.2.2", - "keycloak-angular": "^15.3.0", - "keycloak-js": "^25.0.2", + "keycloak-angular": "^16.0.1", + "keycloak-js": "^25.0.4", "leaflet": "^1.9.4", "lodash-es": "^4.17.21", "md5": "^2.3.0", "moment": "2.29.4", - "ngx-markdown": "^17.2.1", + "ngx-markdown": "^18.0.0", "ngx-papaparse": "^8.0.0", "pouchdb-adapter-memory": "^9.0.0", "pouchdb-browser": "^9.0.0", @@ -55,24 +55,24 @@ "tslib": "^2.6.3", "util": "^0.12.5", "uuid": "^10.0.0", - "webpack": "5.89.0", - "zone.js": "^0.14.8" + "webpack": "^5.94.0", + "zone.js": "^0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.3.8", - "@angular-eslint/builder": "^17.5.2", - "@angular-eslint/eslint-plugin": "^17.5.2", - "@angular-eslint/eslint-plugin-template": "^17.5.2", - "@angular-eslint/schematics": "^17.5.2", - "@angular-eslint/template-parser": "^17.5.2", - "@angular/cli": "^17.3.8", - "@angular/compiler-cli": "^17.3.12", + "@angular-devkit/build-angular": "^18.2.0", + "@angular-eslint/builder": "^18.3.0", + "@angular-eslint/eslint-plugin": "^18.3.0", + "@angular-eslint/eslint-plugin-template": "^18.3.0", + "@angular-eslint/schematics": "^18.3.0", + "@angular-eslint/template-parser": "^18.3.0", + "@angular/cli": "^18.2.0", + "@angular/compiler-cli": "^18.2.0", "@babel/core": "^7.24.9", "@compodoc/compodoc": "^1.1.25", "@cypress/schematic": "~2.5.2", "@percy/cli": "^1.29.0", "@percy/storybook": "^5.0.3", - "@schematics/angular": "^17.3.8", + "@schematics/angular": "^18.2.1", "@storybook/addon-actions": "^7.6.20", "@storybook/addon-essentials": "^7.6.20", "@storybook/angular": "^7.6.20", @@ -84,15 +84,15 @@ "@types/lodash-es": "^4.17.12", "@types/md5": "^2.3.5", "@types/pouchdb": "^6.4.2", - "@types/uuid": "^9.0.8", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "babel-loader": "^9.1.3", - "cypress": "13.6.0", + "cypress": "~13.14.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-storybook": "^0.7.0", + "eslint-plugin-storybook": "^0.8.0", "jasmine-core": "^5.2.0", "jasmine-spec-reporter": "^7.0.0", "karma": "^6.4.3", @@ -102,16 +102,24 @@ "karma-jasmine": "^5.1.0", "karma-jasmine-html-reporter": "^2.1.0", "mockdate": "^3.0.5", - "ngx-i18nsupport": "^0.17.1", "prettier": "^3.3.3", "react": "^18.3.1", "react-dom": "^18.3.1", "storybook": "^7.6.20", "ts-node": "^10.9.2", - "typescript": "~5.4.5", + "typescript": "~5.5.4", "xliff": "^6.2.1" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@aduh95/viz.js": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.4.0.tgz", @@ -131,13 +139,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1801.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1801.2.tgz", - "integrity": "sha512-y2rV8wRwTnmCH/dUo632wHi6r41Gs9XucyGm/ybzB/5tN3x6dS+O3c3zajRpdqTUr8YcD6os6sT+Ay6zS31tOw==", + "version": "0.1802.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.0.tgz", + "integrity": "sha512-s1atTSL98XLUUxfWEQAnhFaOFIJZDLWjSqec+Sb+f4iZFj+hOFejzJxPVnHMIJudOzn0Lqjk3t987KG/zNEGdg==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.1.2", + "@angular-devkit/core": "18.2.0", "rxjs": "7.8.1" }, "engines": { @@ -147,97 +155,97 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.3.8.tgz", - "integrity": "sha512-ixsdXggWaFRP7Jvxd0AMukImnePuGflT9Yy7NJ9/y0cL/k//S/3RnkQv5i411KzN+7D4RIbNkRGGTYeqH24zlg==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.0.tgz", + "integrity": "sha512-V0XKT7xt6d6duXqmDAQEQgEJNXuWAekpHEDxafvBT0MTxcEhu0ozQVwI4oAekiKOz+APIcAZyMzvw3Tlzog5cw==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1703.8", - "@angular-devkit/build-webpack": "0.1703.8", - "@angular-devkit/core": "17.3.8", - "@babel/core": "7.24.0", - "@babel/generator": "7.23.6", - "@babel/helper-annotate-as-pure": "7.22.5", - "@babel/helper-split-export-declaration": "7.22.6", - "@babel/plugin-transform-async-generator-functions": "7.23.9", - "@babel/plugin-transform-async-to-generator": "7.23.3", - "@babel/plugin-transform-runtime": "7.24.0", - "@babel/preset-env": "7.24.0", - "@babel/runtime": "7.24.0", - "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "17.3.8", + "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/build-webpack": "0.1802.0", + "@angular-devkit/core": "18.2.0", + "@angular/build": "18.2.0", + "@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.25.0", + "@babel/plugin-transform-async-to-generator": "7.24.7", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", + "@babel/runtime": "7.25.0", + "@discoveryjs/json-ext": "0.6.1", + "@ngtools/webpack": "18.2.0", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.18", + "autoprefixer": "10.4.20", "babel-loader": "9.1.3", - "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", - "copy-webpack-plugin": "11.0.0", - "critters": "0.0.22", - "css-loader": "6.10.0", - "esbuild-wasm": "0.20.1", + "copy-webpack-plugin": "12.0.2", + "critters": "0.0.24", + "css-loader": "7.1.2", + "esbuild-wasm": "0.23.0", "fast-glob": "3.3.2", - "http-proxy-middleware": "2.0.6", - "https-proxy-agent": "7.0.4", - "inquirer": "9.2.15", - "jsonc-parser": "3.2.1", + "http-proxy-middleware": "3.0.0", + "https-proxy-agent": "7.0.5", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", "less": "4.2.0", - "less-loader": "11.1.0", + "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.1", - "magic-string": "0.30.8", - "mini-css-extract-plugin": "2.8.1", + "loader-utils": "3.3.1", + "magic-string": "0.30.11", + "mini-css-extract-plugin": "2.9.0", "mrmime": "2.0.0", - "open": "8.4.2", + "open": "10.1.0", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "4.0.1", - "piscina": "4.4.0", - "postcss": "8.4.35", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "postcss": "8.4.41", "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.71.1", - "sass-loader": "14.1.1", - "semver": "7.6.0", + "sass": "1.77.8", + "sass-loader": "16.0.0", + "semver": "7.6.3", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.29.1", + "terser": "5.31.6", "tree-kill": "1.2.2", - "tslib": "2.6.2", - "undici": "6.11.1", - "vite": "5.1.7", - "watchpack": "2.4.0", - "webpack": "5.90.3", - "webpack-dev-middleware": "6.1.2", - "webpack-dev-server": "4.15.1", - "webpack-merge": "5.10.0", + "tslib": "2.6.3", + "vite": "5.4.0", + "watchpack": "2.4.1", + "webpack": "5.93.0", + "webpack-dev-middleware": "7.3.0", + "webpack-dev-server": "5.0.4", + "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.20.1" + "esbuild": "0.23.0" }, "peerDependencies": { - "@angular/compiler-cli": "^17.0.0", - "@angular/localize": "^17.0.0", - "@angular/platform-server": "^17.0.0", - "@angular/service-worker": "^17.0.0", + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", "@web/test-runner": "^0.18.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", - "ng-packagr": "^17.0.0", + "ng-packagr": "^18.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.2 <5.5" + "typescript": ">=5.4 <5.6" }, "peerDependenciesMeta": { "@angular/localize": { @@ -275,250 +283,326 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.1703.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1703.8.tgz", - "integrity": "sha512-lKxwG4/QABXZvJpqeSIn/kAwnY6MM9HdHZUV+o5o3UiTi+vO8rZApG4CCaITH3Bxebm7Nam7Xbk8RuukC5rq6g==", + "node_modules/@angular-devkit/build-angular/node_modules/@discoveryjs/json-ext": { + "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": ">=14.17.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.3.8", - "rxjs": "7.8.1" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "node_modules/@angular-devkit/build-angular/node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "chokidar": "^3.5.2" + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" }, "peerDependenciesMeta": { - "chokidar": { + "@rspack/core": { + "optional": true + }, + "webpack": { "optional": true } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "node_modules/@angular-devkit/build-angular/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, + "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@angular-devkit/build-angular/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.8.tgz", - "integrity": "sha512-CjSVVa/9fzMpEDQP01SC4colKCbZwj7vUq0H2bivp8jVsmd21x9Fu0gDBH0Y9NdfAIm4eGZvmiZKMII3vIOaYQ==", + "node_modules/@angular-devkit/build-angular/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^17.0.0", - "typescript": ">=5.2 <5.5", - "webpack": "^5.54.0" + "node": ">=4.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/@angular-devkit/build-angular/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/@angular-devkit/build-angular/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "engines": { + "node": ">=10" } }, - "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-angular/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/@angular-devkit/build-angular/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1703.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1703.8.tgz", - "integrity": "sha512-9u6fl8VVOxcLOEMzrUeaybSvi9hSLSRucHnybneYrabsgreDo32tuy/4G8p6YAHQjpWEj9jvF9Um13ertdni5Q==", + "node_modules/@angular-devkit/build-angular/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/memfs": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", + "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@angular-devkit/architect": "0.1703.8", - "rxjs": "7.8.1" + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">= 4.0.0" }, - "peerDependencies": { - "webpack": "^5.30.0", - "webpack-dev-server": "^4.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.1703.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1703.8.tgz", - "integrity": "sha512-lKxwG4/QABXZvJpqeSIn/kAwnY6MM9HdHZUV+o5o3UiTi+vO8rZApG4CCaITH3Bxebm7Nam7Xbk8RuukC5rq6g==", + "node_modules/@angular-devkit/build-angular/node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "17.3.8", - "rxjs": "7.8.1" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "node_modules/@angular-devkit/build-angular/node_modules/webpack": { + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.6.0", + "mime-types": "^2.1.31", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "chokidar": "^3.5.2" + "webpack": "^5.0.0" }, "peerDependenciesMeta": { - "chokidar": { + "webpack": { "optional": true } } }, - "node_modules/@angular-devkit/build-webpack/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/@angular-devkit/build-angular/node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1802.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.0.tgz", + "integrity": "sha512-bU7AxlI/avnlOLrgE195cokrOA1ayT6JjRv8Hxzh1bIOa1jE87HsyjxvQhOLcdEb97zwHqMqbntcgUNBgsegJQ==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^8.0.0" + "@angular-devkit/architect": "0.1802.0", + "rxjs": "7.8.1" }, - "peerDependencies": { - "ajv": "^8.0.0" + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^5.0.2" } }, "node_modules/@angular-devkit/core": { - "version": "18.1.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.1.2.tgz", - "integrity": "sha512-WYkdKT/Ime5seBX7S7S4aWQbgCG5U3otCvAg/XiMn6scexTo3EZe2jrJl8nxGGFHNWrePoD86LvJOxhnCkEKEA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.0.tgz", + "integrity": "sha512-8SOopyUKUMqAq2rj32XkTIfr79Y274k4uglxxRtzHYoWwHlLdG0KrA+wCcsh/FU9PyR4dA+5dcDAApn358ZF+Q==", "dev": true, - "peer": 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", @@ -543,217 +627,511 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "peer": true + "dev": true }, - "node_modules/@angular-devkit/core/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@angular-devkit/schematics": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.8.tgz", - "integrity": "sha512-QRVEYpIfgkprNHc916JlPuNbLzOgrm9DZalHasnLUz4P6g7pR21olb8YCyM2OTJjombNhya9ZpckcADU5Qyvlg==", + "node_modules/@angular-devkit/schematics": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.0.tgz", + "integrity": "sha512-WWKwz2RKMVI6T25JFwOSSfRLB+anNzabVIRwf85R/YMIo34BUk777f2JU/7cCKlxSpQu39TqIfMQZAyzAD1z0A==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "17.3.8", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", + "@angular-devkit/core": "18.2.0", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.11", "ora": "5.4.1", "rxjs": "7.8.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, - "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "node_modules/@angular-devkit/schematics/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-eslint/builder": { + "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", + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/bundled-angular-compiler": { + "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.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": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" + "@angular-eslint/bundled-angular-compiler": "18.3.0", + "@angular-eslint/utils": "18.3.0" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template": { + "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.3.0", + "@angular-eslint/utils": "18.3.0", + "aria-query": "5.3.0", + "axobject-query": "4.1.0" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/schematics": { + "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.3.0", + "@angular-eslint/eslint-plugin-template": "18.3.0", + "ignore": "5.3.2", + "semver": "7.6.3", + "strip-json-comments": "3.1.1" + }, + "peerDependencies": { + "@angular-devkit/core": ">= 18.0.0 < 19.0.0", + "@angular-devkit/schematics": ">= 18.0.0 < 19.0.0" + } + }, + "node_modules/@angular-eslint/template-parser": { + "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.3.0", + "eslint-scope": "^8.0.2" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/utils": { + "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.3.0" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*" + } + }, + "node_modules/@angular/animations": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.0.tgz", + "integrity": "sha512-BFAfqDDjsa0Q91F4s33pFcBG+ydFDurEmwYGG1gmO7UXZJI6ZbRVdULaZHz75M+Bf3hJkzVB05pesvfbK+Fg/g==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.0" + } + }, + "node_modules/@angular/build": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.0.tgz", + "integrity": "sha512-LvNJ2VOEVy3N1tGzt+xnKyweRBuUE1NsnuEEWAb9Y+V1cyRgj0s7FX2+IQZZQhP+W/pXfbsKaByOAbJ5KWV85Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.0", + "@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.22", + "@vitejs/plugin-basic-ssl": "1.1.0", + "browserslist": "^4.23.0", + "critters": "0.0.24", + "esbuild": "0.23.0", + "fast-glob": "3.3.2", + "https-proxy-agent": "7.0.5", + "listr2": "8.2.4", + "lmdb": "3.0.13", + "magic-string": "0.30.11", + "mrmime": "2.0.0", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "rollup": "4.20.0", + "sass": "1.77.8", + "semver": "7.6.3", + "vite": "5.4.0", + "watchpack": "2.4.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { - "chokidar": "^3.5.2" + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "less": "^4.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" }, "peerDependenciesMeta": { - "chokidar": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { "optional": true } } }, - "node_modules/@angular-devkit/schematics/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/@angular/build/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/schematics/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/@angular/build/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/@angular/build/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/@angular/build/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^8.0.0" + "restore-cursor": "^5.0.0" }, - "peerDependencies": { - "ajv": "^8.0.0" + "engines": { + "node": ">=18" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-eslint/builder": { - "version": "17.5.2", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-17.5.2.tgz", - "integrity": "sha512-bvegxJuocWeHdvISPfCXeLQPSjrMCEVzxXPg16JJKksKWSeRA1JnbXnfnb7eoLdq1+riMHKqbH6Fs4rORAvUiA==", + "node_modules/@angular/build/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, + "license": "MIT", "dependencies": { - "@nx/devkit": "^17.2.8 || ^18.0.0 || ^19.0.0", - "nx": "^17.2.8 || ^18.0.0 || ^19.0.0" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "17.5.2", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-17.5.2.tgz", - "integrity": "sha512-K4hVnMyI98faMJmsA4EOBkD0tapDjWV5gy0j/wJ2uSL46d3JgZPZNJSO1zStf/b3kT4gLOlQ/ulWFiUf1DxgIw==", - "dev": true + "node_modules/@angular/build/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==", + "dev": true, + "license": "MIT" }, - "node_modules/@angular-eslint/eslint-plugin": { - "version": "17.5.2", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-17.5.2.tgz", - "integrity": "sha512-kzPALKyT5XIEbgoNmY/hEZWdMWKTX56Pap9fVLJSC/b+Nd+MXc7TNly2s0XoC0Ru1/kMiVzbmSGPheu/rw+9aA==", + "node_modules/@angular/build/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/@angular/build/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/build/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": { - "@angular-eslint/bundled-angular-compiler": "17.5.2", - "@angular-eslint/utils": "17.5.2", - "@typescript-eslint/utils": "7.11.0" + "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" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "17.5.2", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-17.5.2.tgz", - "integrity": "sha512-6sxVwrJ7yElZxcjxHSA0Ujs29bCD/cMubd9n6TDFI9e3v+ktpoMW4Nv/TCHv0OuYatIOZ7bcJxi38cAO8Vhfhw==", + "node_modules/@angular/build/node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.5.2", - "@angular-eslint/utils": "17.5.2", - "@typescript-eslint/type-utils": "7.11.0", - "@typescript-eslint/utils": "7.11.0", - "aria-query": "5.3.0", - "axobject-query": "4.0.0" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-eslint/schematics": { - "version": "17.5.2", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-17.5.2.tgz", - "integrity": "sha512-HcvqrBEJfYMTc+fZ6YdRkb+9YcXsy2XSv59Yhd0bBqZ4ZdM4QuuVFxWlLNvhV6TF1DtO24CzpN4OyE+AO5EWBA==", + "node_modules/@angular/build/node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-eslint/eslint-plugin": "17.5.2", - "@angular-eslint/eslint-plugin-template": "17.5.2", - "@nx/devkit": "^17.2.8 || ^18.0.0 || ^19.0.0", - "ignore": "5.3.1", - "nx": "^17.2.8 || ^18.0.0 || ^19.0.0", - "strip-json-comments": "3.1.1", - "tmp": "0.2.3" + "get-east-asian-width": "^1.0.0" }, - "peerDependencies": { - "@angular/cli": ">= 17.0.0 < 18.0.0" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-eslint/template-parser": { - "version": "17.5.2", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-17.5.2.tgz", - "integrity": "sha512-46emLElmnIUzW0bpEpSf0u05ofRVUwlfttDOMLedhi700peUKbB9Y6iyz3GzAtQCMklBbJC9nR87LQRH9aSlog==", + "node_modules/@angular/build/node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.5.2", - "eslint-scope": "^8.0.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/@angular-eslint/utils": { - "version": "17.5.2", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-17.5.2.tgz", - "integrity": "sha512-bTMPFqtoetBJsYR/xqREEOCy/CdsKGf2gZkRdH73gG6pOpskWt8J/PbRcMZsC349paV4HFixByVm89inqA0TNg==", + "node_modules/@angular/build/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.5.2", - "@typescript-eslint/utils": "7.11.0" + "mimic-function": "^5.0.0" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular/animations": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.3.12.tgz", - "integrity": "sha512-9hsdWF4gRRcVJtPcCcYLaX1CIyM9wUu6r+xRl6zU5hq8qhl35hig6ounz7CXFAzLf0WDBdM16bPHouVGaG76lg==", + "node_modules/@angular/build/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.3.0" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": ">=18" }, - "peerDependencies": { - "@angular/core": "17.3.12" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/build/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@angular/build/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@angular/build/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/build/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/@angular/build/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/@angular/cdk": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.3.10.tgz", - "integrity": "sha512-b1qktT2c1TTTe5nTji/kFAVW92fULK0YhYAvJ+BjZTPKu2FniZNe8o4qqQ0pUuvtMu+ZQxp/QqFYoidIVCjScg==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.0.tgz", + "integrity": "sha512-hjuUWNhxU48WB2i1j4NLwnPTaCnucRElfp7DBX5Io0rY5Lwl3HXafvyi7/A1D0Ah+YsJpktKOWPDGv8r7vxymg==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -761,33 +1139,33 @@ "parse5": "^7.1.2" }, "peerDependencies": { - "@angular/common": "^17.0.0 || ^18.0.0", - "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/cli": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.3.8.tgz", - "integrity": "sha512-X5ZOQ6ZTKVHjhIsfl32ZRqbs+FUoeHLbT7x4fh2Os/8ObDDwrUcCJPqxe2b2RB5E2d0vepYigknHeLE7gwzlNQ==", - "dev": true, - "dependencies": { - "@angular-devkit/architect": "0.1703.8", - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", - "@schematics/angular": "17.3.8", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.0.tgz", + "integrity": "sha512-hA60QIA7Dh8LQxM42wqd7WrhwQjbjB8oIRH5Slgbiv8iocAo76scp1/qyZo2SSzjfkB7jxUiao5L4ckiJ/hvZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/core": "18.2.0", + "@angular-devkit/schematics": "18.2.0", + "@inquirer/prompts": "5.3.8", + "@listr2/prompt-adapter-inquirer": "2.0.15", + "@schematics/angular": "18.2.0", "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.3", - "ini": "4.1.2", - "inquirer": "9.2.15", - "jsonc-parser": "3.2.1", - "npm-package-arg": "11.0.1", - "npm-pick-manifest": "9.0.0", - "open": "8.4.2", - "ora": "5.4.1", - "pacote": "17.0.6", + "ini": "4.1.3", + "jsonc-parser": "3.3.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.0", + "semver": "7.6.3", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, @@ -795,113 +1173,352 @@ "ng": "bin/ng.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/@schematics/angular": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.0.tgz", + "integrity": "sha512-XePvx2ZnxCcAQw5lHVMUrJvm8MXqAWGcMyJDAuQUqNZrPCk3GpCaplWx2n+nPkinYVX2Q2v/DqtvWStQwgU4nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "18.2.0", + "@angular-devkit/schematics": "18.2.0", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, - "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { - "version": "0.1703.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1703.8.tgz", - "integrity": "sha512-lKxwG4/QABXZvJpqeSIn/kAwnY6MM9HdHZUV+o5o3UiTi+vO8rZApG4CCaITH3Bxebm7Nam7Xbk8RuukC5rq6g==", + "node_modules/@angular/cli/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/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/@angular/cli/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/@angular/cli/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular/cli/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/@angular/cli/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular/cli/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/@angular/cli/node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@angular/cli/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@angular/cli/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "17.3.8", - "rxjs": "7.8.1" + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/@angular/cli/node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "node_modules/@angular/cli/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" + "node": ">=18" }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular/cli/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/@angular/cli/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": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@angular/cli/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/@angular/cli/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": { - "ajv": "^8.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, - "peerDependencies": { - "ajv": "^8.0.0" + "engines": { + "node": ">=18" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/@angular/common": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.3.12.tgz", - "integrity": "sha512-vabJzvrx76XXFrm1RJZ6o/CyG32piTB/1sfFfKHdlH1QrmArb8It4gyk9oEjZ1IkAD0HvBWlfWmn+T6Vx3pdUw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.0.tgz", + "integrity": "sha512-DELx/QYNqqjmiM+kE5PoVmyG4gPw5WB1bDDeg3hEuBCK3eS2KosgQH0/MQo3OSBZHOcAMFjfHMGqKgxndmYixQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "17.3.12", + "@angular/core": "18.2.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.3.12.tgz", - "integrity": "sha512-vwI8oOL/gM+wPnptOVeBbMfZYwzRxQsovojZf+Zol9szl0k3SZ3FycWlxxXZGFu3VIEfrP6pXplTmyODS/Lt1w==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.0.tgz", + "integrity": "sha512-RmGwQ7jRzotUKKMk0CwxTcIXFr5mRxSbJf9o5S3ujuIOo1lYop8SQZvjq67a5BuoYyD+1pX6iMmxZqlbKoihBQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "17.3.12" + "@angular/core": "18.2.0" }, "peerDependenciesMeta": { "@angular/core": { @@ -910,11 +1527,12 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.3.12.tgz", - "integrity": "sha512-1F8M7nWfChzurb7obbvuE7mJXlHtY1UG58pcwcomVtpPb+kPavgAO8OEvJHYBMV+bzSxkXt5UIwL9lt9jHUxZA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.0.tgz", + "integrity": "sha512-pPBFjMqNTNettrleLtEc6a/ysOZjG3F0Z5lyKYePcyNQmn2rpa9XmD2y6PhmzTmIhxeXrogWA84Xgg/vK5wBNw==", + "license": "MIT", "dependencies": { - "@babel/core": "7.23.9", + "@babel/core": "7.25.2", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", @@ -929,93 +1547,54 @@ "ngcc": "bundles/ngcc/index.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "17.3.12", - "typescript": ">=5.2 <5.5" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.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==" - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "@angular/compiler": "18.2.0", + "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.3.12.tgz", - "integrity": "sha512-MuFt5yKi161JmauUta4Dh0m8ofwoq6Ino+KoOtkYMBGsSx+A7dSm+DUxxNwdj7+DNyg3LjVGCFgBFnq4g8z06A==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.0.tgz", + "integrity": "sha512-7+4wXfeAk1TnG3MGho2gpBI5XHxeSRWzLK2rC5qyyRbmMV+GrIgf1HqFjQ4S02rydkQvGpjqQHtO1PYJnyn4bg==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.14.0" + "zone.js": "~0.14.10" } }, "node_modules/@angular/forms": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.3.12.tgz", - "integrity": "sha512-tV6r12Q3yEUlXwpVko4E+XscunTIpPkLbaiDn/MTL3Vxi2LZnsLgHyd/i38HaHN+e/H3B0a1ToSOhV5wf3ay4Q==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.0.tgz", + "integrity": "sha512-G+4BjNCUo4cRwg9NwisGLbtG/1AbIJNOO3RUejPJJbCcAkYMSzmDWSQ+LQEGW4vC/1xaDKO8AT71DI/I09bOxA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "17.3.12", - "@angular/core": "17.3.12", - "@angular/platform-browser": "17.3.12", + "@angular/common": "18.2.0", + "@angular/core": "18.2.0", + "@angular/platform-browser": "18.2.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/localize": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.3.12.tgz", - "integrity": "sha512-b7J7zY/CgJhFVPtmu/pEjefU5SHuTy7lQgX6kTrJPaUSJ5i578R17xr4SwrWe7G4jzQwO6GXZZd17a62uNRyOA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.0.tgz", + "integrity": "sha512-ul8yGmimiHkhUU87isDCst0790jTBHP1zPBMI2q7QHv7iDzSN5brV8zUMcRypxhh4mQOCnq2LtP84o5ybn4Sig==", + "license": "MIT", "dependencies": { - "@babel/core": "7.23.9", + "@babel/core": "7.25.2", "@types/babel__core": "7.20.5", "fast-glob": "3.3.2", "yargs": "^17.2.1" @@ -1026,146 +1605,60 @@ "localize-translate": "tools/bundles/src/translate/cli.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "17.3.12", - "@angular/compiler-cli": "17.3.12" - } - }, - "node_modules/@angular/localize/node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.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/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==" - }, - "node_modules/@angular/localize/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "@angular/compiler": "18.2.0", + "@angular/compiler-cli": "18.2.0" } }, "node_modules/@angular/material": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.3.10.tgz", - "integrity": "sha512-hHMQES0tQPH5JW33W+mpBPuM8ybsloDTqFPuRV8cboDjosAWfJhzAKF3ozICpNlUrs62La/2Wu/756GcQrxebg==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/auto-init": "15.0.0-canary.7f224ddd4.0", - "@material/banner": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/button": "15.0.0-canary.7f224ddd4.0", - "@material/card": "15.0.0-canary.7f224ddd4.0", - "@material/checkbox": "15.0.0-canary.7f224ddd4.0", - "@material/chips": "15.0.0-canary.7f224ddd4.0", - "@material/circular-progress": "15.0.0-canary.7f224ddd4.0", - "@material/data-table": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dialog": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/drawer": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/fab": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/floating-label": "15.0.0-canary.7f224ddd4.0", - "@material/form-field": "15.0.0-canary.7f224ddd4.0", - "@material/icon-button": "15.0.0-canary.7f224ddd4.0", - "@material/image-list": "15.0.0-canary.7f224ddd4.0", - "@material/layout-grid": "15.0.0-canary.7f224ddd4.0", - "@material/line-ripple": "15.0.0-canary.7f224ddd4.0", - "@material/linear-progress": "15.0.0-canary.7f224ddd4.0", - "@material/list": "15.0.0-canary.7f224ddd4.0", - "@material/menu": "15.0.0-canary.7f224ddd4.0", - "@material/menu-surface": "15.0.0-canary.7f224ddd4.0", - "@material/notched-outline": "15.0.0-canary.7f224ddd4.0", - "@material/radio": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/segmented-button": "15.0.0-canary.7f224ddd4.0", - "@material/select": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/slider": "15.0.0-canary.7f224ddd4.0", - "@material/snackbar": "15.0.0-canary.7f224ddd4.0", - "@material/switch": "15.0.0-canary.7f224ddd4.0", - "@material/tab": "15.0.0-canary.7f224ddd4.0", - "@material/tab-bar": "15.0.0-canary.7f224ddd4.0", - "@material/tab-indicator": "15.0.0-canary.7f224ddd4.0", - "@material/tab-scroller": "15.0.0-canary.7f224ddd4.0", - "@material/textfield": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tooltip": "15.0.0-canary.7f224ddd4.0", - "@material/top-app-bar": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.0.tgz", + "integrity": "sha512-lOXk8pAVP4Mr0/Q6YrNnVYQVTnR8aEG5D9QSEWjs+607gONuh/9n7ERBdzX7xO9D0vYyXq+Vil32zcF41/Q8Cg==", + "license": "MIT", + "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/animations": "^17.0.0 || ^18.0.0", - "@angular/cdk": "17.3.10", - "@angular/common": "^17.0.0 || ^18.0.0", - "@angular/core": "^17.0.0 || ^18.0.0", - "@angular/forms": "^17.0.0 || ^18.0.0", - "@angular/platform-browser": "^17.0.0 || ^18.0.0", + "@angular/animations": "^18.0.0 || ^19.0.0", + "@angular/cdk": "18.2.0", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/forms": "^18.0.0 || ^19.0.0", + "@angular/platform-browser": "^18.0.0 || ^19.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/material-moment-adapter": { - "version": "17.3.10", - "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-17.3.10.tgz", - "integrity": "sha512-R68ssdGMSmVIfpOGaB9vjW5lBh6zH9GboBuaIAqizC/ZAzdEgpmT7qdr3PBCmRPTLTx8Yx9K3rhgRekO79ympw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-18.2.0.tgz", + "integrity": "sha512-nyQl8ktWQvEIziiG35F6ObflSpsPVGeV3n6kemvmkOl6Q7T1r1B01B+b7ouHkd47Fq+YFgfkxHl2eDUt9XwJCA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/core": "^17.0.0 || ^18.0.0", - "@angular/material": "17.3.10", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/material": "18.2.0", "moment": "^2.18.1" } }, "node_modules/@angular/platform-browser": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.3.12.tgz", - "integrity": "sha512-DYY04ptWh/ulMHzd+y52WCE8QnEYGeIiW3hEIFjCN8z0kbIdFdUtEB0IK5vjNL3ejyhUmphcpeT5PYf3YXtqWQ==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.0.tgz", + "integrity": "sha512-yhj281TuPz5a8CehwucwIVl29Oqte9KS4X/VQkMV++GpYQE2KKKcoff4FXSdF5RUcUYkK2li4IvawIqPmUSagg==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "17.3.12", - "@angular/common": "17.3.12", - "@angular/core": "17.3.12" + "@angular/animations": "18.2.0", + "@angular/common": "18.2.0", + "@angular/core": "18.2.0" }, "peerDependenciesMeta": { "@angular/animations": { @@ -1174,43 +1667,46 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.3.12.tgz", - "integrity": "sha512-DQwV7B2x/DRLRDSisngZRdLqHdYbbrqZv2Hmu4ZbnNYaWPC8qvzgE/0CvY+UkDat3nCcsfwsMnlDeB6TL7/IaA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.0.tgz", + "integrity": "sha512-izfaXKNC/kqOEzJG8eTnFu39VLI3vv3dTKoYOdEKRB7MTI0t0x66oZmABnHcihtkTSvXs/is+7lA5HmH2ZuIEQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "17.3.12", - "@angular/compiler": "17.3.12", - "@angular/core": "17.3.12", - "@angular/platform-browser": "17.3.12" + "@angular/common": "18.2.0", + "@angular/compiler": "18.2.0", + "@angular/core": "18.2.0", + "@angular/platform-browser": "18.2.0" } }, "node_modules/@angular/router": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.3.12.tgz", - "integrity": "sha512-dg7PHBSW9fmPKTVzwvHEeHZPZdpnUqW/U7kj8D29HTP9ur8zZnx9QcnbplwPeYb8yYa62JMnZSEel2X4PxdYBg==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.0.tgz", + "integrity": "sha512-6/462hvC3HSwbps8VItECHpkdkiFqRmTKdn1Trik+FjnvdupYrKB6kBsveM3eP+gZD4zyMBMKzBWB9N/xA1clw==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "17.3.12", - "@angular/core": "17.3.12", - "@angular/platform-browser": "17.3.12", + "@angular/common": "18.2.0", + "@angular/core": "18.2.0", + "@angular/platform-browser": "18.2.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "17.3.12", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-17.3.12.tgz", - "integrity": "sha512-Y83+oTZ2XPO7P2Yok78JNlXDDXbP7Qr+HN6ifpPXWmUS4MwFEyXByCl3Hlz9VMxnrKvPYWvzHKWfT0S20XZsvA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.0.tgz", + "integrity": "sha512-ngcALrgqMuAeIo5dgou6eBzdtgLvmVg5zwmZuTyrnNPZENEaKTj7u5pm9++gl62797sUWlMbL+fa/BOhntGs5A==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -1218,11 +1714,11 @@ "ngsw-config": "ngsw-config.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "17.3.12", - "@angular/core": "17.3.12" + "@angular/common": "18.2.0", + "@angular/core": "18.2.0" } }, "node_modules/@aw-web-design/x-default-browser": { @@ -1255,28 +1751,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.0.tgz", - "integrity": "sha512-P4fwKI2mjEb3ZU5cnMJzvRsRKGBUcs8jvxIoRmr6ufAY9Xk2Bz7JubRTTivkw55c7WQJfTECeqYVa+HZ0FzREg==", + "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==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "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==", + "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.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", + "@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", @@ -1291,20 +1789,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/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==", - "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/@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", @@ -1319,13 +1803,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "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.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -1333,12 +1818,13 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1358,11 +1844,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.8", + "@babel/compat-data": "^7.25.2", "@babel/helper-validator-option": "^7.24.8", "browserslist": "^4.23.1", "lru-cache": "^5.1.1", @@ -1401,18 +1888,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1439,18 +1914,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -1476,18 +1939,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, - "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", @@ -1514,14 +1965,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.0.tgz", - "integrity": "sha512-bIkOa2ZJYn7FHnepzr5iX9Kmz8FjIz4UKzJ9zhX3dnYuVW0xul9RuR3skBfoLu+FPTQw90EHW9rJsSZhyLQ3fQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -1568,18 +2020,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-replace-supers": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", @@ -1623,12 +2063,13 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1699,9 +2140,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.0.tgz", - "integrity": "sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1710,13 +2155,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.0.tgz", - "integrity": "sha512-dG0aApncVQwAUJa8tP1VHTnmU67BeIQvKafd3raEx315H54FfkZSz3B/TT+33ZQAjatGJA79gZqTtqL5QZUKXw==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" + "@babel/traverse": "^7.25.3" }, "engines": { "node": ">=6.9.0" @@ -2096,15 +2542,16 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "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.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@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" @@ -2114,14 +2561,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2213,18 +2661,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", @@ -2384,14 +2820,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.0.tgz", - "integrity": "sha512-CQmfSnK14eYu82fu6GlCwRciHB7mp7oLN+DeyGDDwUr9cMwuSVviJKPXw/YcRYZdB1TdlLJWHHwXwnwD1WnCmQ==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.24.8", "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -2417,12 +2854,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2708,18 +3146,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", @@ -2767,16 +3193,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz", - "integrity": "sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, "engines": { @@ -2791,6 +3218,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -2879,27 +3307,15 @@ "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-create-class-features-plugin": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-typescript": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/plugin-syntax-typescript": "^7.24.7" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-escapes": { @@ -2966,26 +3382,29 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", - "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "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.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.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", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2997,59 +3416,60 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.24.0", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/plugin-transform-arrow-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.25.0", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", + "@babel/plugin-transform-computed-properties": "^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.25.1", + "@babel/plugin-transform-json-strings": "^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.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", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.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.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", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.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.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", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "core-js-compat": "^3.31.0", + "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.37.1", "semver": "^6.3.1" }, "engines": { @@ -3262,10 +3682,11 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "dev": true, + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -3287,15 +3708,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.0.tgz", - "integrity": "sha512-ubALThHQy4GCf6mbb+5ZRNmLLCI7bJ3f8Q6LHBSRlSKSWj5a7dSUzJBLv3VuIhFrFPgjF4IzPF567YG/HSCdZA==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.0", + "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -3303,24 +3725,11 @@ "node": ">=6.9.0" } }, - "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==", - "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/@babel/types": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.0.tgz", - "integrity": "sha512-LcnxQSsd9aXOIgmmSpvZ/1yo46ra2ESYyqLcryaBZOghxy5qqOBjvCWP5JfkI8yl9rlxRgdLTTMCQQRcN2hdCg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", @@ -3468,147 +3877,6 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-generator-functions": { - "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, - "dependencies": { - "@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" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.0.tgz", - "integrity": "sha512-vYAA8PrCOeZfG4D87hmw1KJ1BPubghXP1e2MacRFwECGNKL76dkA38JEwYllbvQCpf/kLxsTtir0b8MtxKoVCw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.0", - "@babel/helper-compilation-targets": "^7.24.8", - "@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.0", - "@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.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", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-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.25.0", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.0", - "@babel/plugin-transform-computed-properties": "^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.25.0", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", - "@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.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", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.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.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", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.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.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", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "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.37.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@compodoc/compodoc/node_modules/ajv": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", @@ -3640,31 +3908,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@compodoc/compodoc/node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", - "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/@compodoc/compodoc/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3708,31 +3951,7 @@ "marked": "bin/marked.js" }, "engines": { - "node": ">= 16" - } - }, - "node_modules/@compodoc/compodoc/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@compodoc/compodoc/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "node": ">= 16" } }, "node_modules/@compodoc/compodoc/node_modules/supports-color": { @@ -3983,34 +4202,6 @@ "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, - "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, - "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, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", @@ -4021,394 +4212,411 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", - "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", - "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", - "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", - "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", - "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", - "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", - "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", - "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", - "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", - "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", - "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", - "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", - "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", - "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", - "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", - "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", - "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", - "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], - "peer": true, "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.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", - "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", - "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", - "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", - "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", - "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -4440,6 +4648,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -4463,6 +4672,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4478,13 +4688,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4495,6 +4707,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -4510,6 +4723,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -4521,13 +4735,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4540,6 +4756,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -4552,6 +4769,7 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -4666,15 +4884,16 @@ "dev": true }, "node_modules/@fortawesome/angular-fontawesome": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.14.1.tgz", - "integrity": "sha512-Yb5HLiEOAxjSLEcaOM51CKIrzdfvoDafXVJERm9vufxfZkVZPZJgrZRgqwLVpejgq4/Ez6TqHZ6SqmJwdtRF6g==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.15.0.tgz", + "integrity": "sha512-oxmJDYGNSym5ycFR0LX4ZOPAU+wWmMAznYpkm5DNAtWWkhMLcrZl15eZQmVIEE+qruQ7JiVrg3tpo8bEkFlDgw==", + "license": "MIT", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.5.2", "tslib": "^2.6.2" }, "peerDependencies": { - "@angular/core": "^17.0.0", - "@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2 || ^6.1.0" + "@angular/core": "^18.0.0" } }, "node_modules/@fortawesome/fontawesome-common-types": { @@ -4724,6 +4943,7 @@ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", "debug": "^4.3.1", @@ -4738,6 +4958,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4748,6 +4969,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4768,12 +4990,261 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "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/confirm": { + "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" + }, + "engines": { + "node": ">=18" + } + }, + "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/core/node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/editor": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.1.22.tgz", + "integrity": "sha512-K1QwTu7GCK+nKOVRBp5HY9jt3DXOfPGPr6WRDrPImkcJRelG9UTx2cAtK1liXmibRrzJlTWOwqgWT3k2XnS62w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "external-editor": "^3.1.0" + }, + "engines": { + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz", + "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.0.10.tgz", + "integrity": "sha512-kWTxRF8zHjQOn2TJs+XttLioBih6bdc5CcosXIzZsrTY383PXI35DuhIllZKu7CdXFi2rz2BWPN9l0dPsvrQOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "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.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.0.7.tgz", + "integrity": "sha512-p1wpV+3gd1eST/o5N3yQpYEdFNCzSP0Klrl+5bfD3cTTz8BGG6nf4Z07aBW0xjlKIj1Rp0y3x/X4cZYi6TfcLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "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/type": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.2.tgz", + "integrity": "sha512-w9qFkumYDCNyDZmNQjf/n6qQuvQ4dMC3BJesY4oF+yr0CxR5vxujflAVeIcS6U336uzi9GM0kAfZlLrZ9UTkpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -5165,6 +5636,63 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", + "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.3.0.tgz", + "integrity": "sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/@juggle/resize-observer": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", @@ -5175,771 +5703,108 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/@ljharb/through": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", - "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "node_modules/@listr2/prompt-adapter-inquirer": { + "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": { - "call-bind": "^1.0.7" + "@inquirer/type": "^1.5.1" }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@material/animation": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-1GSJaPKef+7HRuV+HusVZHps64cmZuOItDbt40tjJVaikcaZvwmHlcTxRIqzcRoCdt5ZKHh3NoO7GB9Khg4Jnw==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@material/auto-init": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-t7ZGpRJ3ec0QDUO0nJu/SMgLW7qcuG2KqIsEYD1Ej8qhI2xpdR2ydSDQOkVEitXmKoGol1oq4nYSBjTlB65GqA==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/banner": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-g9wBUZzYBizyBcBQXTIafnRUUPi7efU9gPJfzeGgkynXiccP/vh5XMmH+PBxl5v+4MlP/d4cZ2NUYoAN7UTqSA==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/button": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/base": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-I9KQOKXpLfJkP8MqZyr8wZIzdPHrwPjFvGd9zSK91/vPyE4hzHRJc/0njsh9g8Lm9PRYLbifXX+719uTbHxx+A==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@material/button": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-BHB7iyHgRVH+JF16+iscR+Qaic+p7LU1FOLgP8KucRlpF9tTwIxQA6mJwGRi5gUtcG+vyCmzVS+hIQ6DqT/7BA==", - "dependencies": { - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/card": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-kt7y9/IWOtJTr3Z/AoWJT3ZLN7CLlzXhx2udCLP9ootZU2bfGK0lzNwmo80bv/pJfrY9ihQKCtuGTtNxUy+vIw==", - "dependencies": { - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/checkbox": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-rURcrL5O1u6hzWR+dNgiQ/n89vk6tdmdP3mZgnxJx61q4I/k1yijKqNJSLrkXH7Rto3bM5NRKMOlgvMvVd7UMQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/chips": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-AYAivV3GSk/T/nRIpH27sOHFPaSMrE3L0WYbnb5Wa93FgY8a0fbsFYtSH2QmtwnzXveg+B1zGTt7/xIIcynKdQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/checkbox": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "safevalues": "^0.3.4", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/circular-progress": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-DJrqCKb+LuGtjNvKl8XigvyK02y36GRkfhMUYTcJEi3PrOE00bwXtyj7ilhzEVshQiXg6AHGWXtf5UqwNrx3Ow==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/progress-indicator": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/data-table": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-/2WZsuBIq9z9RWYF5Jo6b7P6u0fwit+29/mN7rmAZ6akqUR54nXyNfoSNiyydMkzPlZZsep5KrSHododDhBZbA==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/checkbox": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/icon-button": "15.0.0-canary.7f224ddd4.0", - "@material/linear-progress": "15.0.0-canary.7f224ddd4.0", - "@material/list": "15.0.0-canary.7f224ddd4.0", - "@material/menu": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/select": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/density": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-o9EXmGKVpiQ6mHhyV3oDDzc78Ow3E7v8dlaOhgaDSXgmqaE8v5sIlLNa/LKSyUga83/fpGk3QViSGXotpQx0jA==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@material/dialog": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-u0XpTlv1JqWC/bQ3DavJ1JguofTelLT2wloj59l3/1b60jv42JQ6Am7jU3I8/SIUB1MKaW7dYocXjDWtWJakLA==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/button": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/icon-button": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/dom": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-mQ1HT186GPQSkRg5S18i70typ5ZytfjL09R0gJ2Qg5/G+MLCGi7TAjZZSH65tuD/QGOjel4rDdWOTmYbPYV6HA==", - "dependencies": { - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/drawer": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-qyO0W0KBftfH8dlLR0gVAgv7ZHNvU8ae11Ao6zJif/YxcvK4+gph1z8AO4H410YmC2kZiwpSKyxM1iQCCzbb4g==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/list": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/elevation": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-tV6s4/pUBECedaI36Yj18KmRCk1vfue/JP/5yYRlFNnLMRVISePbZaKkn/BHXVf+26I3W879+XqIGlDVdmOoMA==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/fab": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-4h76QrzfZTcPdd+awDPZ4Q0YdSqsXQnS540TPtyXUJ/5G99V6VwGpjMPIxAsW0y+pmI9UkLL/srrMaJec+7r4Q==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/feature-targeting": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-SAjtxYh6YlKZriU83diDEQ7jNSP2MnxKsER0TvFeyG1vX/DWsUyYDOIJTOEa9K1N+fgJEBkNK8hY55QhQaspew==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@material/floating-label": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-0KMo5ijjYaEHPiZ2pCVIcbaTS2LycvH9zEhEMKwPPGssBCX7iz5ffYQFk7e5yrQand1r3jnQQgYfHAwtykArnQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/focus-ring": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-Jmg1nltq4J6S6A10EGMZnvufrvU3YTi+8R8ZD9lkSbun0Fm2TVdICQt/Auyi6An9zP66oQN6c31eqO6KfIPsDg==", - "dependencies": { - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0" - } - }, - "node_modules/@material/form-field": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-fEPWgDQEPJ6WF7hNnIStxucHR9LE4DoDSMqCsGWS2Yu+NLZYLuCEecgR0UqQsl1EQdNRaFh8VH93KuxGd2hiPg==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/icon-button": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-DcK7IL4ICY/DW+48YQZZs9g0U1kRaW0Wb0BxhvppDMYziHo/CTpFdle4gjyuTyRxPOdHQz5a97ru48Z9O4muTw==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/image-list": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-voMjG2p80XbjL1B2lmF65zO5gEgJOVKClLdqh4wbYzYfwY/SR9c8eLvlYG7DLdFaFBl/7gGxD8TvvZ329HUFPw==", - "dependencies": { - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/layout-grid": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-veDABLxMn2RmvfnUO2RUmC1OFfWr4cU+MrxKPoDD2hl3l3eDYv5fxws6r5T1JoSyXoaN+oEZpheS0+M9Ure8Pg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@material/line-ripple": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-f60hVJhIU6I3/17Tqqzch1emUKEcfVVgHVqADbU14JD+oEIz429ZX9ksZ3VChoU3+eejFl+jVdZMLE/LrAuwpg==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/linear-progress": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-pRDEwPQielDiC9Sc5XhCXrGxP8wWOnAO8sQlMebfBYHYqy5hhiIzibezS8CSaW4MFQFyXmCmpmqWlbqGYRmiyg==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/progress-indicator": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/list": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-Is0NV91sJlXF5pOebYAtWLF4wU2MJDbYqztML/zQNENkQxDOvEXu3nWNb3YScMIYJJXvARO0Liur5K4yPagS1Q==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/menu": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-D11QU1dXqLbh5X1zKlEhS3QWh0b5BPNXlafc5MXfkdJHhOiieb7LC9hMJhbrHtj24FadJ7evaFW/T2ugJbJNnQ==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/list": "15.0.0-canary.7f224ddd4.0", - "@material/menu-surface": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/menu-surface": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-7RZHvw0gbwppaAJ/Oh5SWmfAKJ62aw1IMB3+3MRwsb5PLoV666wInYa+zJfE4i7qBeOn904xqT2Nko5hY0ssrg==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/notched-outline": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-Yg2usuKB2DKlKIBISbie9BFsOVuffF71xjbxPbybvqemxqUBd+bD5/t6H1fLE+F8/NCu5JMigho4ewUU+0RCiw==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/floating-label": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/progress-indicator": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-UPbDjE5CqT+SqTs0mNFG6uFEw7wBlgYmh+noSkQ6ty/EURm8lF125dmi4dv4kW0+octonMXqkGtAoZwLIHKf/w==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@material/radio": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-wR1X0Sr0KmQLu6+YOFKAI84G3L6psqd7Kys5kfb8WKBM36zxO5HQXC5nJm/Y0rdn22ixzsIz2GBo0MNU4V4k1A==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/ripple": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-JqOsWM1f4aGdotP0rh1vZlPZTg6lZgh39FIYHFMfOwfhR+LAikUJ+37ciqZuewgzXB6iiRO6a8aUH6HR5SJYPg==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/rtl": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-UVf14qAtmPiaaZjuJtmN36HETyoKWmsZM/qn1L5ciR2URb8O035dFWnz4ZWFMmAYBno/L7JiZaCkPurv2ZNrGA==", - "dependencies": { - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/segmented-button": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-LCnVRUSAhELTKI/9hSvyvIvQIpPpqF29BV+O9yM4WoNNmNWqTulvuiv7grHZl6Z+kJuxSg4BGbsPxxb9dXozPg==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/touch-target": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/select": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-WioZtQEXRpglum0cMSzSqocnhsGRr+ZIhvKb3FlaNrTaK8H3Y4QA7rVjv3emRtrLOOjaT6/RiIaUMTo9AGzWQQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/floating-label": "15.0.0-canary.7f224ddd4.0", - "@material/line-ripple": "15.0.0-canary.7f224ddd4.0", - "@material/list": "15.0.0-canary.7f224ddd4.0", - "@material/menu": "15.0.0-canary.7f224ddd4.0", - "@material/menu-surface": "15.0.0-canary.7f224ddd4.0", - "@material/notched-outline": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/shape": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-8z8l1W3+cymObunJoRhwFPKZ+FyECfJ4MJykNiaZq7XJFZkV6xNmqAVrrbQj93FtLsECn9g4PjjIomguVn/OEw==", - "dependencies": { - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/slider": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-QU/WSaSWlLKQRqOhJrPgm29wqvvzRusMqwAcrCh1JTrCl+xwJ43q5WLDfjYhubeKtrEEgGu9tekkAiYfMG7EBw==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/snackbar": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-sm7EbVKddaXpT/aXAYBdPoN0k8yeg9+dprgBUkrdqGzWJAeCkxb4fv2B3He88YiCtvkTz2KLY4CThPQBSEsMFQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/button": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/icon-button": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/switch": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-lEDJfRvkVyyeHWIBfoxYjJVl+WlEAE2kZ/+6OqB1FW0OV8ftTODZGhHRSzjVBA1/p4FPuhAtKtoK9jTpa4AZjA==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "safevalues": "^0.3.4", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/tab": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-E1xGACImyCLurhnizyOTCgOiVezce4HlBFAI6YhJo/AyVwjN2Dtas4ZLQMvvWWqpyhITNkeYdOchwCC1mrz3AQ==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/focus-ring": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/tab-indicator": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/tab-bar": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-p1Asb2NzrcECvAQU3b2SYrpyJGyJLQWR+nXTYzDKE8WOpLIRCXap2audNqD7fvN/A20UJ1J8U01ptrvCkwJ4eA==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/tab": "15.0.0-canary.7f224ddd4.0", - "@material/tab-indicator": "15.0.0-canary.7f224ddd4.0", - "@material/tab-scroller": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/tab-indicator": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-h9Td3MPqbs33spcPS7ecByRHraYgU4tNCZpZzZXw31RypjKvISDv/PS5wcA4RmWqNGih78T7xg4QIGsZg4Pk4w==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/tab-scroller": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-LFeYNjQpdXecwECd8UaqHYbhscDCwhGln5Yh+3ctvcEgvmDPNjhKn/DL3sWprWvG8NAhP6sHMrsGhQFVdCWtTg==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/tab": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/textfield": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-AExmFvgE5nNF0UA4l2cSzPghtxSUQeeoyRjFLHLy+oAaE4eKZFrSy0zEpqPeWPQpEMDZk+6Y+6T3cOFYBeSvsw==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/density": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/floating-label": "15.0.0-canary.7f224ddd4.0", - "@material/line-ripple": "15.0.0-canary.7f224ddd4.0", - "@material/notched-outline": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 6" } }, - "node_modules/@material/theme": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-hs45hJoE9yVnoVOcsN1jklyOa51U4lzWsEnQEuJTPOk2+0HqCQ0yv/q0InpSnm2i69fNSyZC60+8HADZGF8ugQ==", - "dependencies": { - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } + "node_modules/@lmdb/lmdb-darwin-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@material/tokens": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-r9TDoicmcT7FhUXC4eYMFnt9TZsz0G8T3wXvkKncLppYvZ517gPyD/1+yhuGfGOxAzxTrM66S/oEc1fFE2q4hw==", - "dependencies": { - "@material/elevation": "15.0.0-canary.7f224ddd4.0" - } + "node_modules/@lmdb/lmdb-darwin-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@material/tooltip": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-8qNk3pmPLTnam3XYC1sZuplQXW9xLn4Z4MI3D+U17Q7pfNZfoOugGr+d2cLA9yWAEjVJYB0mj8Yu86+udo4N9w==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/button": "15.0.0-canary.7f224ddd4.0", - "@material/dom": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/tokens": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "safevalues": "^0.3.4", - "tslib": "^2.1.0" - } + "node_modules/@lmdb/lmdb-linux-arm": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@material/top-app-bar": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-SARR5/ClYT4CLe9qAXakbr0i0cMY0V3V4pe3ElIJPfL2Z2c4wGR1mTR8m2LxU1MfGKK8aRoUdtfKaxWejp+eNA==", - "dependencies": { - "@material/animation": "15.0.0-canary.7f224ddd4.0", - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/elevation": "15.0.0-canary.7f224ddd4.0", - "@material/ripple": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/shape": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "@material/typography": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } + "node_modules/@lmdb/lmdb-linux-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@material/touch-target": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-BJo/wFKHPYLGsRaIpd7vsQwKr02LtO2e89Psv0on/p0OephlNIgeB9dD9W+bQmaeZsZ6liKSKRl6wJWDiK71PA==", - "dependencies": { - "@material/base": "15.0.0-canary.7f224ddd4.0", - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/rtl": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } + "node_modules/@lmdb/lmdb-linux-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@material/typography": { - "version": "15.0.0-canary.7f224ddd4.0", - "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.7f224ddd4.0.tgz", - "integrity": "sha512-kBaZeCGD50iq1DeRRH5OM5Jl7Gdk+/NOfKArkY4ksBZvJiStJ7ACAhpvb8MEGm4s3jvDInQFLsDq3hL+SA79sQ==", - "dependencies": { - "@material/feature-targeting": "15.0.0-canary.7f224ddd4.0", - "@material/theme": "15.0.0-canary.7f224ddd4.0", - "tslib": "^2.1.0" - } + "node_modules/@lmdb/lmdb-win32-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@mdx-js/react": { "version": "2.3.0", @@ -5958,16 +5823,89 @@ "react": ">=16" } }, - "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==", + "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", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@emnapi/core": "^1.1.0", - "@emnapi/runtime": "^1.1.0", - "@tybys/wasm-util": "^0.9.0" - } + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@ndelangen/get-tarball": { "version": "3.0.9", @@ -5992,6 +5930,23 @@ "rxjs": "^6.4.0 || ^7.0.0" } }, + "node_modules/@ngtools/webpack": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.0.tgz", + "integrity": "sha512-a6hbkYzh/KUlI52huiU4vztqIuxzyddg6kJGcelUJx3Ju6MJeziu+XmJ6wqFRvfH89zmJeaSADKsGFQaBHtJLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6029,6 +5984,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", "dev": true, + "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", @@ -6044,13 +6000,15 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@npmcli/fs": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", "dev": true, + "license": "ISC", "dependencies": { "semver": "^7.3.5" }, @@ -6063,6 +6021,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^7.0.0", "ini": "^4.1.3", @@ -6078,20 +6037,12 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/git/node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@npmcli/git/node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=16" } @@ -6100,22 +6051,15 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/@npmcli/git/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "ISC" }, "node_modules/@npmcli/git/node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -6131,6 +6075,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", "dev": true, + "license": "ISC", "dependencies": { "npm-bundled": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" @@ -6147,6 +6092,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -6156,6 +6102,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.0.tgz", "integrity": "sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", "glob": "^10.2.2", @@ -6169,20 +6116,12 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/package-json/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@npmcli/promise-spawn": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", "dev": true, + "license": "ISC", "dependencies": { "which": "^4.0.0" }, @@ -6195,6 +6134,7 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=16" } @@ -6204,6 +6144,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -6215,24 +6156,27 @@ } }, "node_modules/@npmcli/redact": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-1.1.0.tgz", - "integrity": "sha512-PfnWuOkQgu7gCbnSsAisaX7hKOdZ4wSAhAzH3/ph5dSGau52kCRrMMGbiSQLwyTZpgldkZ49b0brkOr1AzGBHQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", "dev": true, + "license": "ISC", "engines": { "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@npmcli/run-script": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.4.tgz", - "integrity": "sha512-9ApYM/3+rBt9V80aYg6tZfzj3UWdiYyCt7gJUD1VJKvWF5nwKDSICXbYIQbspFTq6TOpbsEtIC0LArB8d9PFmg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", "which": "^4.0.0" }, "engines": { @@ -6244,6 +6188,7 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=16" } @@ -6253,6 +6198,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -6263,208 +6209,6 @@ "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/devkit": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-19.5.3.tgz", - "integrity": "sha512-kd6eIQjWuFHdO14wVu0rzGtoPbO3EdYM/3gATOupxBzlqD+7dmkvv1Olbri9v598mDApXQNo8q81L2masTAhvg==", - "dev": true, - "dependencies": { - "@nx/devkit": "19.5.3" - } - }, - "node_modules/@nrwl/tao": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-19.5.3.tgz", - "integrity": "sha512-SHtPlQi7zofDdbFjqcrTb/A0Mo9tT8S88H8nJa1+GzhKpGUB9rykHtq0qoYdiRBnQfmfR5LoKfe/jft61Ktvdg==", - "dev": true, - "dependencies": { - "nx": "19.5.3", - "tslib": "^2.3.0" - }, - "bin": { - "tao": "index.js" - } - }, - "node_modules/@nx/devkit": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-19.5.3.tgz", - "integrity": "sha512-OUi8OJkoT+y3LwXACO6ugF9l6QppUyHrBIZYOTffBa1ZrnkpJrw03smy+GhAt+BDoeNGEuOPHGvOSV4AmRxnmg==", - "dev": true, - "dependencies": { - "@nrwl/devkit": "19.5.3", - "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" - }, - "peerDependencies": { - "nx": ">= 17 <= 20" - } - }, - "node_modules/@nx/nx-darwin-arm64": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-19.5.3.tgz", - "integrity": "sha512-DacVfnhx7wiglDXRAdbrmaP4s3ZQXMs8Mk0fGoQYjv1uwWajDOPxMYJUZH0CGysIDADSrku4AIqogGX/CZjSuQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-darwin-x64": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-19.5.3.tgz", - "integrity": "sha512-AfY1g8nYJbBGiR2SDt/Q8YcQyuwtRmGxfJIrzCu+2+hFFds7RF9iaqeKedWeHN9wAsaTbDnBuDwwojT9LMOxaA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-freebsd-x64": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-19.5.3.tgz", - "integrity": "sha512-dWwxFs9bp67n/l1QhI41pSJk+mpwDNh7RY+WQBUldWbIyh8c4/wYk3VaqjALPCcGUky/RCME6rdLkqxFRAIs4A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-19.5.3.tgz", - "integrity": "sha512-7l79OXwKVqnTr6/85mVPU+h3nnxGDAWgY6kTJNdmuaFlDgbHKbcNo9FFSu2srdqr1x84UsU49w8ZBJbdwA5YSg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-19.5.3.tgz", - "integrity": "sha512-aFCuoUiEI20tGCxdUDO0JWCWli3RH0LPCXjnQ4H4pNMzT8zpvjvu+Js7FtwFG+NZWOdlmtiDlthnVAd+5ex6Wg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-linux-arm64-musl": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-19.5.3.tgz", - "integrity": "sha512-gcjdlGvgQ4ahSfPpMw32cr7GrCYhr/58D1R/bbyem0QQg+EdLbLlhhdS2pAHBCoENfpSnknQZhMrUN1LR8Qmpw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-linux-x64-gnu": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-19.5.3.tgz", - "integrity": "sha512-Jwu6peOyaV9WTR1ihzfIIqEBYsbOSy0cH8H36ce17zpemq6l/Cz5EJ7blVXut1qksMFvC/QbkTWqTlfO5XEHIw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-linux-x64-musl": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-19.5.3.tgz", - "integrity": "sha512-84KnkghjbInJAoWvCJB34lHq9iGCgo5KjcxUFZJFNDYTQh/VBTp/OhH8bFyPRwQTPVSToLeBhoFvGB1bqBekrA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-19.5.3.tgz", - "integrity": "sha512-q19m59cm+VTZzlHh+/dSHism7hgKfGHR+nW5xtxIF00rZQpJpv0ve7GVvyXPFw7NXvceYRK1THes1MljYXyslQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nx/nx-win32-x64-msvc": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-19.5.3.tgz", - "integrity": "sha512-DOdO7K6ySiwrXsnJNjJXxng427n5+nXIDt4L81ltCdr6oE8wUiUpRTt1dfl65rHknojB/b1at3V6+x450F0/2A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@percy/cli": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@percy/cli/-/cli-1.29.0.tgz", @@ -7981,244 +7725,262 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", - "integrity": "sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==", + "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": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz", - "integrity": "sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz", - "integrity": "sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==", + "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": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz", - "integrity": "sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz", - "integrity": "sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz", - "integrity": "sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==", + "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": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz", - "integrity": "sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz", - "integrity": "sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz", - "integrity": "sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz", - "integrity": "sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz", - "integrity": "sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz", - "integrity": "sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz", - "integrity": "sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz", - "integrity": "sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz", - "integrity": "sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz", - "integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==", + "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" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@schematics/angular": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.3.8.tgz", - "integrity": "sha512-2g4OmSyE9YGq50Uj7fNI26P/TSAFJ7ZuirwTF2O7Xc4XRQ29/tYIIqhezpNlTb6rlYblcQuMcUZBrMfWJHcqJw==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.1.tgz", + "integrity": "sha512-bBV7I+MCbdQmBPUFF4ECg37VReM0+AdQsxgwkjBBSYExmkErkDoDgKquwL/tH7stDCc5IfTd0g9BMeosRgDMug==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "17.3.8", - "@angular-devkit/schematics": "17.3.8", - "jsonc-parser": "3.2.1" + "@angular-devkit/core": "18.2.1", + "@angular-devkit/schematics": "18.2.1", + "jsonc-parser": "3.3.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.8.tgz", - "integrity": "sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.1.tgz", + "integrity": "sha512-fSuGj6CxiTFR+yjuVcaWqaVb5Wts39CSBYRO1BlsOlbuWFZ2NKC/BAb5bdxpB31heCBJi7e3XbPvcMMJIcnKlA==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -8231,102 +7993,100 @@ } } }, - "node_modules/@schematics/angular/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/@schematics/angular/node_modules/@angular-devkit/schematics": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.1.tgz", + "integrity": "sha512-2t/q0Jcv7yqhAzEdNgsxoGSCmPgD4qfnVOJ7EJw3LNIA+kX1CmtN4FESUS0i49kN4AyNJFAI5O2pV8iJiliKaw==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "@angular-devkit/core": "18.2.1", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.11", + "ora": "5.4.1", + "rxjs": "7.8.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@schematics/angular/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/@schematics/angular/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } + "license": "MIT" }, "node_modules/@sentry-internal/browser-utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.20.0.tgz", - "integrity": "sha512-GGYNiELnT4ByidHyS4/M8UF8Oxagm5R13QyTncQGq8nZcQhcFZ9mdxLnf1/R4+j44Fph2Cgzafe8jGP/AMA9zw==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.26.0.tgz", + "integrity": "sha512-O2Tj+WK33/ZVp5STnz6ZL0OO+/Idk2KqsH0ITQkQmyZ2z0kdzWOeqK7s7q3/My6rB1GfPcyqPcBBv4dVv92FYQ==", + "license": "MIT", "dependencies": { - "@sentry/core": "8.20.0", - "@sentry/types": "8.20.0", - "@sentry/utils": "8.20.0" + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/feedback": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.20.0.tgz", - "integrity": "sha512-mFvAoVpVShkDB2AgEr/dE96NSTPKI/lGMBznZMg7ZEcwZhLfH7HvLYCadIskRfzqFTLOUpbm9ciIO4SyR/4bDA==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.26.0.tgz", + "integrity": "sha512-hQtw1gg8n6ERK1UH47F7ZI1zOsbhu0J2VX+TrnkpaQR2FgxDW1oe9Ja6oCV4CQKuR4w+1ZI/Kj4imSt0K33kEw==", + "license": "MIT", "dependencies": { - "@sentry/core": "8.20.0", - "@sentry/types": "8.20.0", - "@sentry/utils": "8.20.0" + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.20.0.tgz", - "integrity": "sha512-sCiI7SOAHq5XsxkixtoMofeSyKd/hVgDV+4145f6nN9m7nLzig4PBQwh2SgK2piJ2mfaXfqcdzA1pShPYldaJA==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.26.0.tgz", + "integrity": "sha512-JDY7W2bswlp5c3483lKP4kcb75fHNwGNfwD8x8FsY9xMjv7nxeXjLpR5cCEk1XqPq2+n6w4j7mJOXhEXGiUIKg==", + "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.20.0", - "@sentry/core": "8.20.0", - "@sentry/types": "8.20.0", - "@sentry/utils": "8.20.0" + "@sentry-internal/browser-utils": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.20.0.tgz", - "integrity": "sha512-LXV/pMH9KMw6CtImenMsiBkYIFIc97pDJ/rC7mVImKIROQ45fxGp/JBXM4Id0GENyA2+SySMWVQCAAapSfHZTw==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.26.0.tgz", + "integrity": "sha512-2CFQW6f9aJHIo/DqmqYa9PaYoLn1o36ywc0h8oyGrD4oPCbrnE5F++PmTdc71GBODu41HBn/yoCTLmxOD+UjpA==", + "license": "MIT", "dependencies": { - "@sentry-internal/replay": "8.20.0", - "@sentry/core": "8.20.0", - "@sentry/types": "8.20.0", - "@sentry/utils": "8.20.0" + "@sentry-internal/replay": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/angular": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.20.0.tgz", - "integrity": "sha512-rKfipjebEh14DatN1CfKylg+rDJRA4LFlmb4SCrFTNmnOouWflu1qTDtcny9rNQHPm3v/7CTlqmNVGlHV/W/Aw==", - "dependencies": { - "@sentry/browser": "8.20.0", - "@sentry/core": "8.20.0", - "@sentry/types": "8.20.0", - "@sentry/utils": "8.20.0", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.26.0.tgz", + "integrity": "sha512-9YolcJMdEzS6hbImal3jrAbzGZGM7DpmfSOfzt1Cs4bYTD9gCxKRkLyUgiNRIlrIBO7CkdpMrCSY+nEohvCw7A==", + "license": "MIT", + "dependencies": { + "@sentry/browser": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0", "tslib": "^2.4.1" }, "engines": { @@ -8340,48 +8100,52 @@ } }, "node_modules/@sentry/browser": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.20.0.tgz", - "integrity": "sha512-JDZbCreY44/fHYN28QzsAwEHXa2rc1hzM6GE4RSlXCdAhNfrjVxyYDxhw/50pVEHZg1WXxf7ZmERjocV5VJHsw==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.26.0.tgz", + "integrity": "sha512-e5s6eKlwLZWzTwQcBwqyAGZMMuQROW9Z677VzwkSyREWAIkKjfH2VBxHATnNGc0IVkNHjD7iH3ixo3C0rLKM3w==", + "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.20.0", - "@sentry-internal/feedback": "8.20.0", - "@sentry-internal/replay": "8.20.0", - "@sentry-internal/replay-canvas": "8.20.0", - "@sentry/core": "8.20.0", - "@sentry/types": "8.20.0", - "@sentry/utils": "8.20.0" + "@sentry-internal/browser-utils": "8.26.0", + "@sentry-internal/feedback": "8.26.0", + "@sentry-internal/replay": "8.26.0", + "@sentry-internal/replay-canvas": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/core": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.20.0.tgz", - "integrity": "sha512-R81snuw+67VT4aCxr6ShST/s0Y6FlwN2YczhDwaGyzumn5rlvA6A4JtQDeExduNoDDyv4T3LrmW8wlYZn3CJJw==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.26.0.tgz", + "integrity": "sha512-g/tVmTZD4GNbLFf++hKJfBpcCAtduFEMLnbfa9iT/QEZjlmP+EzY+GsH9bafM5VsNe8DiOUp+kJKWtShzlVdBA==", + "license": "MIT", "dependencies": { - "@sentry/types": "8.20.0", - "@sentry/utils": "8.20.0" + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/types": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.20.0.tgz", - "integrity": "sha512-6IP278KojOpiAA7vrd1hjhUyn26cl0n0nGsShzic5ztCVs92sTeVRnh7MTB9irDVtAbOEyt/YH6go3h+Jia1pA==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.26.0.tgz", + "integrity": "sha512-zKmh6SWsJh630rpt7a9vP4Cm4m1C2gDTUqUiH565CajCL/4cePpNWYrNwalSqsOSL7B9OrczA1+n6a6XvND+ng==", + "license": "MIT", "engines": { "node": ">=14.18" } }, "node_modules/@sentry/utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.20.0.tgz", - "integrity": "sha512-+1I5H8dojURiEUGPliDwheQk8dhjp8uV1sMccR/W/zjFrt4wZyPs+Ttp/V7gzm9LDJoNek9tmELert/jQqWTgg==", + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.26.0.tgz", + "integrity": "sha512-xvlPU9Hd2BlyT+FhWHGNwnxWqdVRk2AHnDtVcW4Ma0Ri5EwS+uy4Jeik5UkSv8C5RVb9VlxFmS8LN3I1MPJsLw==", + "license": "MIT", "dependencies": { - "@sentry/types": "8.20.0" + "@sentry/types": "8.26.0" }, "engines": { "node": ">=14.18" @@ -8392,6 +8156,7 @@ "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.2" }, @@ -8404,6 +8169,7 @@ "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^16.14.0 || >=18.0.0" } @@ -8413,6 +8179,7 @@ "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^16.14.0 || >=18.0.0" } @@ -8422,6 +8189,7 @@ "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.0.0", @@ -8434,20 +8202,12 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@sigstore/sign/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@sigstore/tuf": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.2", "tuf-js": "^2.2.1" @@ -8461,6 +8221,7 @@ "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.1.0", @@ -8476,6 +8237,19 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -8500,6 +8274,13 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/addon-actions/node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true, + "license": "MIT" + }, "node_modules/@storybook/addon-actions/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -10890,6 +10671,7 @@ "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", "dev": true, + "license": "MIT", "engines": { "node": "^16.14.0 || >=18.0.0" } @@ -10899,6 +10681,7 @@ "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", "dev": true, + "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^9.0.4" @@ -10907,30 +10690,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, - "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, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -10989,6 +10748,7 @@ "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -11007,6 +10767,7 @@ "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, + "license": "MIT", "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" @@ -11094,6 +10855,7 @@ "version": "9.6.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", + "dev": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -11103,6 +10865,7 @@ "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, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -11177,10 +10940,11 @@ "dev": true }, "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -11289,12 +11053,23 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", "devOptional": true }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { - "version": "20.14.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", - "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", + "version": "22.4.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.2.tgz", + "integrity": "sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/node-fetch": { @@ -11312,10 +11087,17 @@ "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/node/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -11563,10 +11345,11 @@ } }, "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true, + "license": "MIT" }, "node_modules/@types/scheduler": { "version": "0.16.8", @@ -11595,6 +11378,7 @@ "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, + "license": "MIT", "dependencies": { "@types/express": "*" } @@ -11627,6 +11411,7 @@ "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -11638,10 +11423,11 @@ "devOptional": true }, "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/webpack-env": { "version": "1.18.5", @@ -11649,21 +11435,23 @@ "integrity": "sha512-wz7kjjRRj8/Lty4B+Kr0LN6Ypc/3SymeCCGSbaXp2leH0ZVg/PriNiOwNj4bD4uphI7A8NXS4b6Gl373sfO5mA==", "dev": true }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ws": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", - "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/xmldom": { - "version": "0.1.34", - "resolved": "https://registry.npmjs.org/@types/xmldom/-/xmldom-0.1.34.tgz", - "integrity": "sha512-7eZFfxI9XHYjJJuugddV6N5YNeXgQE1lArWOcd1eCOKWb/FGs5SIjacSYuEJuwhsGS3gy4RuZ5EUIcqYscuPDA==", - "dev": true - }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -11681,347 +11469,143 @@ }, "node_modules/@types/yauzl": { "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.11.0.tgz", - "integrity": "sha512-WmppUEgYy+y1NTseNMJ6mCFxt03/7jTOy08bcg7bxJJdsM4nuhnchyBbE8vryveaJUf62noH7LodPSo5Z0WUCg==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.11.0", - "@typescript-eslint/utils": "7.11.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.11.0.tgz", - "integrity": "sha512-MPEsDRZTyCiXkD4vd3zywDCifi7tatc4K37KqTprCvaXptP7Xlpdw0NR2hRJTetG5TxbWDB79Ys4kLmHliEo/w==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.11.0.tgz", - "integrity": "sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.11.0.tgz", - "integrity": "sha512-7syYk4MzjxTEk0g/w3iqtgxnFQspDJfn6QKD36xMuuhTzjcxY7F8EmBLnALjVyaOF1/bVocu3bS/2/F7rXrveQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.11.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, + "optional": true, "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@types/node": "*" } }, - "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.2.0.tgz", + "integrity": "sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==", "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/type-utils": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "node_modules/@typescript-eslint/parser": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.2.0.tgz", + "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", + "debug": "^4.3.4" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, - "node_modules/@typescript-eslint/utils": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.11.0.tgz", - "integrity": "sha512-xlAWwPleNRHwF37AhrZurOxA1wyXowW4PqVXZVUNCLjB48CqdPJoJWkrpH2nij9Q3Lb7rtWindtoXwxjxlKKCA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz", + "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.11.0", - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/typescript-estree": "7.11.0" + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.11.0.tgz", - "integrity": "sha512-27tGdVEiutD4POirLZX4YzT180vevUURJl4wJGmm6TrQoiYwuxTIY98PBp6L2oN+JQxzE0URvYlzJaBHIekXAw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.2.0.tgz", + "integrity": "sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0" + "@typescript-eslint/typescript-estree": "8.2.0", + "@typescript-eslint/utils": "8.2.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.11.0.tgz", - "integrity": "sha512-MPEsDRZTyCiXkD4vd3zywDCifi7tatc4K37KqTprCvaXptP7Xlpdw0NR2hRJTetG5TxbWDB79Ys4kLmHliEo/w==", + "node_modules/@typescript-eslint/types": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz", + "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.11.0.tgz", - "integrity": "sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz", + "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/visitor-keys": "8.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -12030,7 +11614,7 @@ "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -12042,49 +11626,41 @@ } } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.11.0.tgz", - "integrity": "sha512-7syYk4MzjxTEk0g/w3iqtgxnFQspDJfn6QKD36xMuuhTzjcxY7F8EmBLnALjVyaOF1/bVocu3bS/2/F7rXrveQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz", + "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.11.0", - "eslint-visitor-keys": "^3.4.3" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.2.0", + "@typescript-eslint/types": "8.2.0", + "@typescript-eslint/typescript-estree": "8.2.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/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, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz", + "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "8.2.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -12126,7 +11702,8 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@vitejs/plugin-basic-ssl": { "version": "1.1.0", @@ -12340,54 +11917,16 @@ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true }, - "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, - "dependencies": { - "js-yaml": "^3.10.0", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.15.0" - } - }, - "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, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@zkochan/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "dependencies": { - "xtend": "~4.0.0" - } - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -12412,10 +11951,11 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", "peerDependencies": { "acorn": "^8" } @@ -12425,6 +11965,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -12482,6 +12023,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -12503,15 +12045,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", @@ -12742,6 +12285,7 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, + "license": "Apache-2.0", "dependencies": { "dequal": "^2.0.3" } @@ -12878,9 +12422,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -12896,12 +12440,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "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": { @@ -12943,30 +12488,14 @@ "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", "dev": true }, - "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios/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 - }, "node_modules/axobject-query": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", - "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "dependencies": { - "dequal": "^2.0.3" + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" } }, "node_modules/babel-core": { @@ -13148,57 +12677,27 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0", - "core-js-compat": "^3.34.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -13323,6 +12822,21 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bl/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/blob-util": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", @@ -13403,6 +12917,7 @@ "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" @@ -13491,9 +13006,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "funding": [ { "type": "opencollective", @@ -13508,10 +13023,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -13567,6 +13083,22 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -13581,6 +13113,7 @@ "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", @@ -13603,7 +13136,8 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/cachedir": { "version": "2.4.0", @@ -13670,9 +13204,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001643", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", - "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", "funding": [ { "type": "opencollective", @@ -13686,7 +13220,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -13730,7 +13265,8 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/charenc": { "version": "0.0.2", @@ -13947,6 +13483,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "license": "ISC", "engines": { "node": ">= 12" } @@ -14182,34 +13719,19 @@ "typedarray": "^0.0.6" } }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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": { - "safe-buffer": "~5.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/confbox": { @@ -14238,6 +13760,7 @@ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -14346,20 +13869,21 @@ } }, "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", "dev": true, + "license": "MIT", "dependencies": { - "fast-glob": "^3.2.11", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.1", - "globby": "^13.1.1", + "globby": "^14.0.0", "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -14374,6 +13898,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -14382,29 +13907,32 @@ } }, "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "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": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "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" }, @@ -14412,13 +13940,27 @@ "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-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", + "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": { - "browserslist": "^4.23.0" + "browserslist": "^4.23.3" }, "funding": { "type": "opencollective", @@ -14503,10 +14045,11 @@ "dev": true }, "node_modules/critters": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.22.tgz", - "integrity": "sha512-NU7DEcQZM2Dy8XTKFHxtdnIM/drE312j2T4PCVaSUcS0oBeyT/NImpRw/Ap0zOr/1SE7SgPK9tGPg1WK/sVakw==", + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", + "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { "chalk": "^4.1.0", "css-select": "^5.1.0", @@ -14522,6 +14065,7 @@ "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" }, @@ -14537,6 +14081,7 @@ "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" @@ -14553,6 +14098,7 @@ "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" } @@ -14562,6 +14108,7 @@ "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" }, @@ -14699,21 +14246,21 @@ "dev": true }, "node_modules/cypress": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.0.tgz", - "integrity": "sha512-quIsnFmtj4dBUEJYU4OH0H12bABJpSujvWexC24Ju1gTlKMJbeT6tTO0vh7WNfiBPPjoIXLN+OUqVtiKFs6SGw==", + "version": "13.14.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.14.0.tgz", + "integrity": "sha512-r0+nhd033x883YL6068futewUsl02Q7rWiinyAAIBDW/OOTn+UMILWgNuCiY3vtJjd53efOqq5R9dctQk/rKiw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.0", + "@cypress/request": "^3.0.1", "@cypress/xvfb": "^1.2.4", - "@types/node": "^18.17.5", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -14731,7 +14278,7 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -14745,24 +14292,15 @@ "request-progress": "^3.0.0", "semver": "^7.5.3", "supports-color": "^8.1.1", - "tmp": "~0.2.1", + "tmp": "~0.2.3", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, "bin": { - "cypress": "bin/cypress" - }, - "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" - } - }, - "node_modules/cypress/node_modules/@types/node": { - "version": "18.19.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.42.tgz", - "integrity": "sha512-d2ZFc/3lnK2YCYhos8iaNIYu9Vfhr92nHiyJHRltXWjXUBjEE+A4I58Tdbnw4VhggSW+2j5y5gTrLs4biNnubg==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" + "cypress": "bin/cypress" + }, + "engines": { + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, "node_modules/cypress/node_modules/ansi-styles": { @@ -15552,6 +15090,23 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-browser-id": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", @@ -15568,11 +15123,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/default-browser/node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "execa": "^5.0.0" }, @@ -15749,11 +15318,22 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/detect-node-es": { "version": "1.1.0", @@ -15811,15 +15391,6 @@ "node": ">=0.3.1" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -15837,6 +15408,7 @@ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, + "license": "MIT", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -16004,34 +15576,19 @@ "stream-shift": "^1.0.0" } }, - "node_modules/duplexify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/duplexify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/duplexify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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": { - "safe-buffer": "~5.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/eastasianwidth": { @@ -16078,9 +15635,10 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.2.tgz", - "integrity": "sha512-kc4r3U3V3WLaaZqThjYz/Y6z8tJe+7K0bbjUVo3i+LWIypVdMx5nXCkwRe6SWbY6ILqLdc1rKcKmr3HoH7wjSQ==" + "version": "1.5.12", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.12.tgz", + "integrity": "sha512-tIhPkdlEoCL1Y+PToq3zRNehUaKp3wBX/sr7aclAWdIWjvqAe/Im/H0SiCM4c1Q8BLPHCdoJTol+ZblflydehA==", + "license": "ISC" }, "node_modules/elkjs": { "version": "0.9.3", @@ -16285,11 +15843,25 @@ "node": ">=4" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/errno": { "version": "0.1.8", @@ -16465,41 +16037,43 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.1.tgz", - "integrity": "sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==", + "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", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.1", - "@esbuild/android-arm": "0.20.1", - "@esbuild/android-arm64": "0.20.1", - "@esbuild/android-x64": "0.20.1", - "@esbuild/darwin-arm64": "0.20.1", - "@esbuild/darwin-x64": "0.20.1", - "@esbuild/freebsd-arm64": "0.20.1", - "@esbuild/freebsd-x64": "0.20.1", - "@esbuild/linux-arm": "0.20.1", - "@esbuild/linux-arm64": "0.20.1", - "@esbuild/linux-ia32": "0.20.1", - "@esbuild/linux-loong64": "0.20.1", - "@esbuild/linux-mips64el": "0.20.1", - "@esbuild/linux-ppc64": "0.20.1", - "@esbuild/linux-riscv64": "0.20.1", - "@esbuild/linux-s390x": "0.20.1", - "@esbuild/linux-x64": "0.20.1", - "@esbuild/netbsd-x64": "0.20.1", - "@esbuild/openbsd-x64": "0.20.1", - "@esbuild/sunos-x64": "0.20.1", - "@esbuild/win32-arm64": "0.20.1", - "@esbuild/win32-ia32": "0.20.1", - "@esbuild/win32-x64": "0.20.1" + "@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-plugin-alias": { @@ -16521,15 +16095,16 @@ } }, "node_modules/esbuild-wasm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.1.tgz", - "integrity": "sha512-6v/WJubRsjxBbQdz6izgvx7LsVFvVaGmSdwrFHmEzoVgfXL89hkKPoQHsnVI2ngOkcBUQT9kmAM1hVL1k/Av4A==", + "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": { @@ -16559,6 +16134,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -16652,18 +16228,19 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.7.0.tgz", - "integrity": "sha512-lmvnszv3Xyd0TcxFLoDfLOaVN5v0vKwZh7ZDDY9xCeT/+knHn1ZnktHVO4tun/xVj8aBMYynXpBk0DIiWWRnww==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.8.0.tgz", + "integrity": "sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==", "dev": true, + "license": "MIT", "dependencies": { "@storybook/csf": "^0.0.1", - "@typescript-eslint/utils": "^5.45.0", - "requireindex": "^1.1.0", + "@typescript-eslint/utils": "^5.62.0", + "requireindex": "^1.2.0", "ts-dedent": "^2.2.0" }, "engines": { - "node": "12.x || 14.x || >= 16" + "node": ">= 18" }, "peerDependencies": { "eslint": ">=6" @@ -16863,7 +16440,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", @@ -16908,6 +16486,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -16936,6 +16515,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -16960,6 +16540,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -17002,6 +16583,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -17014,6 +16596,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -17162,7 +16745,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/express": { "version": "4.19.2", @@ -17291,6 +16875,7 @@ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, + "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -17305,6 +16890,7 @@ "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" }, @@ -17405,6 +16991,13 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "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", @@ -17478,6 +17071,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -17706,6 +17300,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -18013,6 +17608,7 @@ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, @@ -18036,15 +17632,6 @@ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, - "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, - "dependencies": { - "js-yaml": "^3.13.1" - } - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -18070,6 +17657,7 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -18158,6 +17746,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -18323,21 +17924,6 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, - "node_modules/glob/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, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -18466,7 +18052,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/handlebars": { "version": "4.7.8", @@ -18498,51 +18085,6 @@ "node": ">=0.10.0" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/har-validator/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -18632,6 +18174,7 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" }, @@ -18643,13 +18186,15 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -18657,34 +18202,19 @@ "wbuf": "^1.1.0" } }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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": { - "safe-buffer": "~5.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/html-entities": { @@ -18827,13 +18357,15 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/http-errors": { "version": "2.0.0", @@ -18876,6 +18408,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -18885,27 +18418,21 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", + "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/http-proxy": "^1.17.8", + "@types/http-proxy": "^1.17.10", + "debug": "^4.3.4", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "micromatch": "^4.0.5" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/http-signature": { @@ -18923,10 +18450,11 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -18944,6 +18472,16 @@ "node": ">=10.17.0" } }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, "node_modules/i18next": { "version": "23.12.2", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.12.2.tgz", @@ -19011,10 +18549,11 @@ ] }, "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": { "node": ">= 4" } @@ -19024,6 +18563,7 @@ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", "dev": true, + "license": "ISC", "dependencies": { "minimatch": "^9.0.0" }, @@ -19117,50 +18657,13 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", - "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/inquirer": { - "version": "9.2.15", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", - "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", - "dev": true, - "dependencies": { - "@ljharb/through": "^2.3.12", - "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "figures": "^3.2.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/internal-slot": { @@ -19200,6 +18703,7 @@ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, + "license": "MIT", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -19212,7 +18716,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -19452,6 +18957,41 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-installed-globally": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", @@ -19481,7 +19021,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-map": { "version": "2.0.3", @@ -19522,6 +19063,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -19568,6 +19122,7 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -20043,114 +19598,6 @@ "colors": "1.4.0" } }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", @@ -20341,7 +19788,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jscodeshift": { "version": "0.15.2", @@ -20460,13 +19908,15 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "dev": true, + "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -20539,7 +19989,8 @@ "dev": true, "engines": [ "node >= 0.2.0" - ] + ], + "license": "MIT" }, "node_modules/jsprim": { "version": "2.0.2", @@ -20872,23 +20323,25 @@ "dev": true }, "node_modules/keycloak-angular": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/keycloak-angular/-/keycloak-angular-15.3.0.tgz", - "integrity": "sha512-fejYO3bqiXOYsvV7vBh+2pj8xYDi0mL8SWo97vxM/ofx8PXFA6Ug/ujPZnuN/S2ZP759Wf1NCpznvYoWpZ2jcA==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/keycloak-angular/-/keycloak-angular-16.0.1.tgz", + "integrity": "sha512-ytkL32R/tfHEyZ3txQtgH1y0WofW/D36zTbo2agDCYUtZETq0wAQ3E/4bVDUAr6ZKwotgAnIyOORfErnvDkXng==", + "license": "MIT", "dependencies": { "tslib": "^2.3.1" }, "peerDependencies": { - "@angular/common": "^17", - "@angular/core": "^17", - "@angular/router": "^17", + "@angular/common": "^18", + "@angular/core": "^18", + "@angular/router": "^18", "keycloak-js": "^18 || ^19 || ^20 || ^21 || ^22 || ^23 || ^24 || ^25" } }, "node_modules/keycloak-js": { - "version": "25.0.2", - "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-25.0.2.tgz", - "integrity": "sha512-ACLf5O5PqzfDJwGqvLpqM0kflYWmyl3+T7M2C23gztJYccDxdfNP54+B9OkXz2GnDpLUId0ceoA+lbHw9t4Wng==", + "version": "25.0.4", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-25.0.4.tgz", + "integrity": "sha512-LW7dVgqcBxMnnJTdmh7Zgd0NpStJnX2sCMrJGqcGtm4zmk4Rwlqk2o2uOvY7PaRHHYePXfbIwrqVhlN3GAnRCg==", + "license": "Apache-2.0", "dependencies": { "js-sha256": "^0.11.0", "jwt-decode": "^4.0.0" @@ -20899,6 +20352,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -20927,20 +20381,12 @@ "node": ">=6" } }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/launch-editor": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz", - "integrity": "sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.1.tgz", + "integrity": "sha512-elBx2l/tp9z99X5H/qev8uyDywVh0VXAwEbjk8kJhnc5grOFkGh7aW6q55me9xnYbss261XtnUrysZ+XvGbhQA==", "dev": true, + "license": "MIT", "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" @@ -21007,23 +20453,30 @@ } }, "node_modules/less-loader": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", - "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", "dev": true, - "dependencies": { - "klona": "^2.0.4" - }, + "license": "MIT", "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "@rspack/core": "0.x || 1.x", "less": "^3.5.0 || ^4.0.0", "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/less/node_modules/image-size": { @@ -21139,6 +20592,20 @@ "node": ">=6" } }, + "node_modules/level-iterator-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==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/level-supports": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz", @@ -21204,15 +20671,6 @@ } } }, - "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==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, "node_modules/listr2": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", @@ -21272,6 +20730,32 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/lmdb": { + "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", + "dependencies": { + "msgpackr": "^1.10.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@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": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -21281,10 +20765,11 @@ } }, "node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12.13.0" } @@ -21548,15 +21033,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "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" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/make-dir": { @@ -21594,6 +21077,7 @@ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/agent": "^2.0.0", "cacache": "^18.0.0", @@ -21612,15 +21096,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -21735,19 +21210,6 @@ "node": ">= 0.6" } }, - "node_modules/memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha512-iVrGHZB8i4OQfM155xx8akvG9FIj+ht14DX5CQkCTG4EHzZ3d3sgckIf/Lm9ivZalEsFuEVnWv2B2WZvbrro2w==", - "dependencies": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" - } - }, "node_modules/memfs": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", @@ -22352,11 +21814,25 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mini-css-extract-plugin": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", - "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dev": true, + "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -22376,13 +21852,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "dev": true, + "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": { "brace-expansion": "^2.0.1" }, @@ -22416,6 +21894,7 @@ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -22428,6 +21907,7 @@ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", "dev": true, + "license": "MIT", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", @@ -22437,54 +21917,28 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" }, "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "encoding": "^0.1.13" + } }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, + "license": "ISC", "dependencies": { - "jsonparse": "^1.3.1", "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/minipass-json-stream/node_modules/minipass": { + "node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -22492,17 +21946,19 @@ "node": ">=8" } }, - "node_modules/minipass-json-stream/node_modules/yallist": { + "node_modules/minipass-flush/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -22515,6 +21971,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -22526,13 +21983,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -22545,6 +22004,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -22556,7 +22016,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minizlib": { "version": "2.1.2", @@ -22699,11 +22160,45 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/msgpackr": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.0.tgz", + "integrity": "sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, + "license": "MIT", "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" @@ -22717,6 +22212,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -22789,68 +22285,11 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "node_modules/ngx-i18nsupport": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/ngx-i18nsupport/-/ngx-i18nsupport-0.17.1.tgz", - "integrity": "sha512-d8OCQs/XYBEI9qvztQyEkd8gEPFEBmyRg8UcriGQV8Ew1ujvrIieHxmX8YpDpFZKQ4ePextQGUSvjpGd2NauEQ==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "commander": "^2.15.1", - "he": "^1.1.1", - "ngx-i18nsupport-lib": "^1.10.2", - "request": "^2.85.0", - "rxjs": "^6.0.0" - }, - "bin": { - "xliffmerge": "dist/xliffmerge/xliffmerge" - }, - "engines": { - "node": ">=6.9" - } - }, - "node_modules/ngx-i18nsupport-lib": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-1.10.2.tgz", - "integrity": "sha512-Z81I2/HUtZ/7X7C3sioJj/Zr/M0iQs0aR5EhYsrWTzdEy7fZWFVYabzzZs+8h6lhQ/4yIl+3sVOCBkI9BiUUEQ==", - "dev": true, - "dependencies": { - "@types/xmldom": "^0.1.29", - "tokenizr": "^1.3.4", - "xmldom": "^0.1.27" - }, - "engines": { - "node": ">=6.9" - } - }, - "node_modules/ngx-i18nsupport/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/ngx-i18nsupport/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/ngx-i18nsupport/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/ngx-markdown": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-17.2.1.tgz", - "integrity": "sha512-TKzxP2R2uTGpVFx0OZDFC/BpNJYUabG2z59/9/PXrTP3R7xTNFuvGQZSNitAQFD7nI3Uko87Ra3GJrbaHdjeBA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-18.0.0.tgz", + "integrity": "sha512-sFR9dIOKobdhNKZTlCrX3RmpoAhZ7k3T9h7oWJP676Oe9BsoxuAYZKJmFDT20vrY6xmFD3WtLJDZR7rNRLf6Uw==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -22862,9 +22301,9 @@ "prismjs": "^1.28.0" }, "peerDependencies": { - "@angular/common": "^17.0.0", - "@angular/core": "^17.0.0", - "@angular/platform-browser": "^17.0.0", + "@angular/common": "^18.0.0", + "@angular/core": "^18.0.0", + "@angular/platform-browser": "^18.0.0", "marked": ">= 9.0.0 < 13.0.0", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.14.0" @@ -22885,6 +22324,7 @@ "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "!win32" @@ -22894,6 +22334,14 @@ "node-gyp-build": "^4.2.2" } }, + "node_modules/nice-napi/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -22911,11 +22359,11 @@ "dev": true }, "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", "dev": true, - "optional": true + "license": "MIT" }, "node_modules/node-dir": { "version": "0.1.17", @@ -22982,6 +22430,7 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } @@ -22991,6 +22440,7 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", "dev": true, + "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", @@ -23015,6 +22465,7 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, + "license": "MIT", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -23022,29 +22473,37 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-gyp/node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=16" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -23061,12 +22520,6 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, - "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 - }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -23083,6 +22536,7 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, + "license": "ISC", "dependencies": { "abbrev": "^2.0.0" }, @@ -23098,6 +22552,7 @@ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", @@ -23120,6 +22575,7 @@ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -23129,6 +22585,7 @@ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", "dev": true, + "license": "ISC", "dependencies": { "npm-normalize-package-bin": "^3.0.0" }, @@ -23141,6 +22598,7 @@ "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" }, @@ -23153,18 +22611,20 @@ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-package-arg": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", - "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", + "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": { "hosted-git-info": "^7.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" }, @@ -23177,6 +22637,7 @@ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dev": true, + "license": "ISC", "dependencies": { "ignore-walk": "^6.0.4" }, @@ -23185,10 +22646,11 @@ } }, "node_modules/npm-pick-manifest": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz", - "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==", + "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": { "npm-install-checks": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", @@ -23200,16 +22662,17 @@ } }, "node_modules/npm-registry-fetch": { - "version": "16.2.1", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.2.1.tgz", - "integrity": "sha512-8l+7jxhim55S85fjiDGJ1rZXBWGtRLi1OSb4Z3BPLObPuIaeKRlPRiYMSHU4/81ck3t71Z+UwDDl47gcpmfQQA==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", "dev": true, + "license": "ISC", "dependencies": { - "@npmcli/redact": "^1.1.0", + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", "make-fetch-happen": "^13.0.0", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", "npm-package-arg": "^11.0.0", "proc-log": "^4.0.0" @@ -23218,15 +22681,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-registry-fetch/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -23251,173 +22705,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/nx": { - "version": "19.5.3", - "resolved": "https://registry.npmjs.org/nx/-/nx-19.5.3.tgz", - "integrity": "sha512-ZUrnRwPdRWXeo8IuLj16Oo9IfiDjd8C6xKWC4F6wcTNZ9ZS7ZErrfqaQr04zdO89ASF9brbkqm0UkMyDPc6kPQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@napi-rs/wasm-runtime": "0.2.4", - "@nrwl/tao": "19.5.3", - "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "3.0.0-rc.46", - "@zkochan/js-yaml": "0.0.7", - "axios": "^1.6.0", - "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.3", - "@nx/nx-darwin-x64": "19.5.3", - "@nx/nx-freebsd-x64": "19.5.3", - "@nx/nx-linux-arm-gnueabihf": "19.5.3", - "@nx/nx-linux-arm64-gnu": "19.5.3", - "@nx/nx-linux-arm64-musl": "19.5.3", - "@nx/nx-linux-x64-gnu": "19.5.3", - "@nx/nx-linux-x64-musl": "19.5.3", - "@nx/nx-win32-arm64-msvc": "19.5.3", - "@nx/nx-win32-x64-msvc": "19.5.3" - }, - "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, - "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, - "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/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, - "dependencies": { - "dotenv": "^16.4.4" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "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, - "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 - }, - "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, - "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/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/nypm": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.3.9.tgz", @@ -23572,15 +22859,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -23646,7 +22924,8 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ohash": { "version": "1.1.3", @@ -23817,6 +23096,13 @@ "node": ">=8" } }, + "node_modules/ordered-binary": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", + "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==", + "dev": true, + "license": "MIT" + }, "node_modules/os-name": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", @@ -23838,6 +23124,7 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -23894,16 +23181,21 @@ } }, "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/retry": "0.12.0", + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", "retry": "^0.13.1" }, "engines": { - "node": ">=8" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-retry/node_modules/retry": { @@ -23911,6 +23203,7 @@ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -23931,32 +23224,32 @@ "dev": true }, "node_modules/pacote": { - "version": "17.0.6", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.6.tgz", - "integrity": "sha512-cJKrW21VRE8vVTRskJo78c/RCvwJCn1f4qgfxL4w77SOWrTCRcmfkYHlHtS0gqpgjv3zhXflRtgsrUCX5xwNnQ==", + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^7.0.0", + "@npmcli/run-script": "^8.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^16.0.0", - "proc-log": "^3.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^7.0.0", - "read-package-json-fast": "^3.0.0", "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, "bin": { - "pacote": "lib/bin.js" + "pacote": "bin/index.js" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -24251,10 +23544,11 @@ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", - "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -24281,10 +23575,11 @@ } }, "node_modules/piscina": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.4.0.tgz", - "integrity": "sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", + "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", "dev": true, + "license": "MIT", "optionalDependencies": { "nice-napi": "^1.0.2" } @@ -24339,9 +23634,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "funding": [ { @@ -24357,10 +23652,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -24401,7 +23697,8 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/postcss-modules-extract-imports": { "version": "3.1.0", @@ -24518,6 +23815,29 @@ "pouchdb-adapter-leveldb-core": "9.0.0" } }, + "node_modules/pouchdb-adapter-memory/node_modules/abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "license": "MIT", + "dependencies": { + "xtend": "~4.0.0" + } + }, + "node_modules/pouchdb-adapter-memory/node_modules/memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha512-iVrGHZB8i4OQfM155xx8akvG9FIj+ht14DX5CQkCTG4EHzZ3d3sgckIf/Lm9ivZalEsFuEVnWv2B2WZvbrro2w==", + "license": "MIT", + "dependencies": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" + } + }, "node_modules/pouchdb-adapter-utils": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/pouchdb-adapter-utils/-/pouchdb-adapter-utils-9.0.0.tgz", @@ -24777,10 +24097,11 @@ } }, "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -24797,7 +24118,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", @@ -24812,13 +24133,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, + "license": "MIT", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -25281,35 +24604,6 @@ } } }, - "node_modules/read-package-json": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.1.tgz", - "integrity": "sha512-8PcDiZ8DXUjLf687Ol4BR8Bpm2umR7vhoZOzNRt+uxD9GpBh/K+CAAALVIiYFknmvlmyg7hM7BSNUXPaCCqd0Q==", - "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", - "dev": true, - "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", - "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", - "dev": true, - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -25440,16 +24734,33 @@ } }, "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==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, "node_modules/readdirp": { @@ -25750,38 +25061,6 @@ "entities": "^2.0.0" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", @@ -25791,91 +25070,6 @@ "throttleit": "^1.0.0" } }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/request/node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/request/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -25990,6 +25184,7 @@ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -26075,10 +25270,11 @@ "optional": true }, "node_modules/rollup": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz", - "integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.5" }, @@ -26090,32 +25286,36 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.19.1", - "@rollup/rollup-android-arm64": "4.19.1", - "@rollup/rollup-darwin-arm64": "4.19.1", - "@rollup/rollup-darwin-x64": "4.19.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.19.1", - "@rollup/rollup-linux-arm-musleabihf": "4.19.1", - "@rollup/rollup-linux-arm64-gnu": "4.19.1", - "@rollup/rollup-linux-arm64-musl": "4.19.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.19.1", - "@rollup/rollup-linux-riscv64-gnu": "4.19.1", - "@rollup/rollup-linux-s390x-gnu": "4.19.1", - "@rollup/rollup-linux-x64-gnu": "4.19.1", - "@rollup/rollup-linux-x64-musl": "4.19.1", - "@rollup/rollup-win32-arm64-msvc": "4.19.1", - "@rollup/rollup-win32-ia32-msvc": "4.19.1", - "@rollup/rollup-win32-x64-msvc": "4.19.1", + "@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" } }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/run-parallel": { @@ -26212,16 +25412,12 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "devOptional": true }, - "node_modules/safevalues": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/safevalues/-/safevalues-0.3.4.tgz", - "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==" - }, "node_modules/sass": { - "version": "1.71.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", - "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, + "license": "MIT", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -26235,10 +25431,11 @@ } }, "node_modules/sass-loader": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.1.tgz", - "integrity": "sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw==", + "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": { "neo-async": "^2.6.2" }, @@ -26335,13 +25532,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/selfsigned": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" @@ -26351,12 +25550,10 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -26364,22 +25561,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -26618,6 +25799,7 @@ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -26651,6 +25833,7 @@ "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.0.0", @@ -26712,6 +25895,7 @@ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -26784,6 +25968,7 @@ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, + "license": "MIT", "dependencies": { "faye-websocket": "^0.11.3", "uuid": "^8.3.2", @@ -26795,6 +25980,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -26804,6 +25990,7 @@ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, + "license": "MIT", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -26818,6 +26005,7 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.1", "debug": "^4.3.4", @@ -26946,6 +26134,7 @@ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -26962,6 +26151,7 @@ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -26971,6 +26161,21 @@ "wbuf": "^1.7.3" } }, + "node_modules/spdy-transport/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/split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", @@ -27025,6 +26230,7 @@ "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -27085,6 +26291,20 @@ "readable-stream": "^3.5.0" } }, + "node_modules/stream-browserify/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", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/stream-combiner": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", @@ -27293,56 +26513,125 @@ "node": ">=6" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", + "optional": true + }, + "node_modules/stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/stylus/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "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==", + "node_modules/stylus/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", + "optional": true, + "peer": true, "dependencies": { - "duplexer": "^0.1.1", - "minimist": "^1.2.0", - "through": "^2.3.4" - }, - "bin": { - "sl-log-transformer": "bin/sl-log-transformer.js" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=4" + "node": "*" } }, - "node_modules/style-loader": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", - "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "node_modules/stylus/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/stylis": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", - "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==", - "optional": true + "license": "ISC", + "optional": true, + "peer": true }, "node_modules/sublevel-pouchdb": { "version": "9.0.0", @@ -27357,12 +26646,14 @@ "node_modules/sublevel-pouchdb/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" }, "node_modules/sublevel-pouchdb/node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -27373,7 +26664,8 @@ "node_modules/sublevel-pouchdb/node_modules/string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" }, "node_modules/supports-color": { "version": "5.5.0", @@ -27513,6 +26805,21 @@ "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", @@ -27682,9 +26989,10 @@ } }, "node_modules/terser": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.1.tgz", - "integrity": "sha512-lZQ/fyaIGxsbGxApKmoPTODIzELy3++mXhS5hOqaAWZjQtpq/hFHAc+rm29NND1rYRxRWKcjuARNwULNXa5RtQ==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", + "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -27879,6 +27187,19 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, "node_modules/throttleit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", @@ -27904,41 +27225,27 @@ "xtend": "~4.0.1" } }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "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": { - "safe-buffer": "~5.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/tiny-emitter": { "version": "2.1.0", @@ -28007,12 +27314,6 @@ "node": ">=0.6" } }, - "node_modules/tokenizr": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/tokenizr/-/tokenizr-1.7.0.tgz", - "integrity": "sha512-XHJRmcTZRK4xr9Rjci/Z5JerDwV4SOczO7G/hhrIddNLDJY9hZY7pX6vC7GvDUtlfjFb0BvBblE5JAVKbB2OEQ==", - "dev": true - }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -28065,6 +27366,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -28259,6 +27577,7 @@ "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", "dev": true, + "license": "MIT", "dependencies": { "@tufjs/models": "2.0.1", "debug": "^4.3.4", @@ -28429,9 +27748,10 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -28497,19 +27817,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undici": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.11.1.tgz", - "integrity": "sha512-KyhzaLJnV1qa3BSHdj4AZ2ndqI0QWPxYzaIOio0WzcEJB9gvuysprJSLtpvc2D9mhR9jPDUk7xlJlZbH2KR5iw==", - "dev": true, - "engines": { - "node": ">=18.0" - } - }, "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==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -28577,11 +27889,25 @@ "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", "dev": true }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", "dev": true, + "license": "ISC", "dependencies": { "unique-slug": "^4.0.0" }, @@ -28594,6 +27920,7 @@ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", "dev": true, + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, @@ -28943,6 +28270,7 @@ "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" } @@ -28985,14 +28313,15 @@ } }, "node_modules/vite": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz", - "integrity": "sha512-sgnEEFTZYMui/sTlH1/XEnVNHMujOahPLGMxn1+5sIT45Xjng1Ec1K78jRP15dSmVgg5WBin9yO81j3o9OxofA==", + "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.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -29011,6 +28340,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -29028,6 +28358,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -29040,13 +28373,14 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "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" @@ -29056,13 +28390,14 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "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" @@ -29072,13 +28407,14 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "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" @@ -29088,13 +28424,14 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "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" @@ -29104,13 +28441,14 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "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" @@ -29120,13 +28458,14 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "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" @@ -29136,13 +28475,14 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "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" @@ -29152,13 +28492,14 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "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" @@ -29168,13 +28509,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "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" @@ -29184,13 +28526,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "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" @@ -29200,13 +28543,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "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" @@ -29216,13 +28560,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "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" @@ -29232,13 +28577,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "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" @@ -29248,13 +28594,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "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" @@ -29264,13 +28611,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "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" @@ -29280,13 +28628,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "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" @@ -29296,13 +28645,14 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "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", "optional": true, "os": [ "linux" @@ -29312,13 +28662,14 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "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", "optional": true, "os": [ "netbsd" @@ -29328,13 +28679,14 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "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": "MIT", "optional": true, "os": [ "openbsd" @@ -29344,13 +28696,14 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "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", "optional": true, "os": [ "sunos" @@ -29360,13 +28713,14 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "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": "MIT", "optional": true, "os": [ "win32" @@ -29376,13 +28730,14 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "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": "MIT", "optional": true, "os": [ "win32" @@ -29392,13 +28747,14 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "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" @@ -29408,11 +28764,12 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "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", "bin": { "esbuild": "bin/esbuild" }, @@ -29420,29 +28777,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@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/void-elements": { @@ -29469,9 +28826,10 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -29485,6 +28843,7 @@ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, + "license": "MIT", "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -29498,6 +28857,13 @@ "defaults": "^1.0.3" } }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT" + }, "node_modules/web-worker": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", @@ -29510,33 +28876,32 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.89.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", - "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -29584,54 +28949,55 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", + "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", "express": "^4.17.3", "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", + "html-entities": "^2.4.0", "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "rimraf": "^5.0.5", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.13.0" + "webpack-dev-middleware": "^7.1.0", + "ws": "^8.16.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" + "webpack": "^5.0.0" }, "peerDependenciesMeta": { "webpack": { @@ -29642,36 +29008,153 @@ } } }, + "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, "node_modules/webpack-dev-server/node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } }, + "node_modules/webpack-dev-server/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/memfs": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", + "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "node_modules/webpack-dev-server/node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.1.tgz", + "integrity": "sha512-/t6KpZw/bnmCR0VKILjJT05mWecbf1aIM2VxCJUvBbg0iXqaQJFxbJ4PCrsY4iBH7PGwnccm4BYyoP1G+lGfAA==", "dev": true, + "license": "MIT", "dependencies": { "colorette": "^2.0.10", - "memfs": "^3.4.3", + "memfs": "^4.6.0", "mime-types": "^2.1.31", + "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } } }, "node_modules/webpack-hot-middleware": { @@ -29686,17 +29169,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": { @@ -29907,7 +29391,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/windows-release": { "version": "4.0.0", @@ -30118,16 +29603,6 @@ "sax": "^1.2.4" } }, - "node_modules/xmldom": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", - "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==", - "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0", - "dev": true, - "engines": { - "node": ">=0.1" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -30217,6 +29692,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zepto": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", @@ -30224,9 +29712,10 @@ "dev": true }, "node_modules/zone.js": { - "version": "0.14.8", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.8.tgz", - "integrity": "sha512-48uh7MnVp4/OQDuCHeFdXw5d8xwPqFTvlHgPJ1LBFb5GaustLSZV+YUH0to5ygNyGpqTsjpbpt141U/j3pCfqQ==" + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", + "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==", + "license": "MIT" } } } diff --git a/package.json b/package.json index 42d5bf641a..bf50970f42 100644 --- a/package.json +++ b/package.json @@ -19,43 +19,46 @@ "extract-i18n": "ng extract-i18n --output-path ./src/assets/locale/ && xliffmerge --profile xliffmerge.json" }, "private": true, + "browser": { + "stream": "./node_modules/stream-browserify" + }, "dependencies": { - "@angular/animations": "^17.3.12", - "@angular/cdk": "^17.3.10", - "@angular/common": "^17.3.12", - "@angular/compiler": "^17.3.12", - "@angular/core": "^17.3.12", - "@angular/forms": "^17.3.12", - "@angular/localize": "^17.3.12", - "@angular/material": "^17.3.10", - "@angular/material-moment-adapter": "^17.3.10", - "@angular/platform-browser": "^17.3.12", - "@angular/platform-browser-dynamic": "^17.3.12", - "@angular/router": "^17.3.12", - "@angular/service-worker": "^17.3.12", + "@angular/animations": "^18.2.0", + "@angular/cdk": "^18.2.0", + "@angular/common": "^18.2.0", + "@angular/compiler": "^18.2.0", + "@angular/core": "^18.2.0", + "@angular/forms": "^18.2.0", + "@angular/localize": "^18.2.0", + "@angular/material": "^18.2.0", + "@angular/material-moment-adapter": "^18.2.0", + "@angular/platform-browser": "^18.2.0", + "@angular/platform-browser-dynamic": "^18.2.0", + "@angular/router": "^18.2.0", + "@angular/service-worker": "^18.2.0", "@aytek/material-color-picker": "^1.0.4", "@casl/ability": "^6.7.1", "@casl/angular": "^8.2.7", "@faker-js/faker": "^8.4.1", - "@fortawesome/angular-fontawesome": "^0.14.1", + "@fortawesome/angular-fontawesome": "^0.15.0", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@ngneat/until-destroy": "^10.0.0", - "@sentry/angular": "^8.20.0", + "@sentry/angular": "^8.26.0", "angulartics2": "^14.0.0", "assert": "^2.1.0", "crypto-es": "^2.1.0", "deep-object-diff": "^1.1.9", "hammerjs": "^2.0.8", "json-query": "^2.2.2", - "keycloak-angular": "^15.3.0", - "keycloak-js": "^25.0.2", + "keycloak-angular": "^16.0.1", + "keycloak-js": "^25.0.4", "leaflet": "^1.9.4", "lodash-es": "^4.17.21", "md5": "^2.3.0", "moment": "2.29.4", - "ngx-markdown": "^17.2.1", + "ngx-markdown": "^18.0.0", "ngx-papaparse": "^8.0.0", "pouchdb-adapter-memory": "^9.0.0", "pouchdb-browser": "^9.0.0", @@ -66,24 +69,24 @@ "tslib": "^2.6.3", "util": "^0.12.5", "uuid": "^10.0.0", - "webpack": "5.89.0", - "zone.js": "^0.14.8" + "webpack": "^5.94.0", + "zone.js": "^0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.3.8", - "@angular-eslint/builder": "^17.5.2", - "@angular-eslint/eslint-plugin": "^17.5.2", - "@angular-eslint/eslint-plugin-template": "^17.5.2", - "@angular-eslint/schematics": "^17.5.2", - "@angular-eslint/template-parser": "^17.5.2", - "@angular/cli": "^17.3.8", - "@angular/compiler-cli": "^17.3.12", + "@angular-devkit/build-angular": "^18.2.0", + "@angular-eslint/builder": "^18.3.0", + "@angular-eslint/eslint-plugin": "^18.3.0", + "@angular-eslint/eslint-plugin-template": "^18.3.0", + "@angular-eslint/schematics": "^18.3.0", + "@angular-eslint/template-parser": "^18.3.0", + "@angular/cli": "^18.2.0", + "@angular/compiler-cli": "^18.2.0", "@babel/core": "^7.24.9", "@compodoc/compodoc": "^1.1.25", "@cypress/schematic": "~2.5.2", "@percy/cli": "^1.29.0", "@percy/storybook": "^5.0.3", - "@schematics/angular": "^17.3.8", + "@schematics/angular": "^18.2.1", "@storybook/addon-actions": "^7.6.20", "@storybook/addon-essentials": "^7.6.20", "@storybook/angular": "^7.6.20", @@ -95,15 +98,15 @@ "@types/lodash-es": "^4.17.12", "@types/md5": "^2.3.5", "@types/pouchdb": "^6.4.2", - "@types/uuid": "^9.0.8", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "babel-loader": "^9.1.3", - "cypress": "13.6.0", + "cypress": "~13.14.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-storybook": "^0.7.0", + "eslint-plugin-storybook": "^0.8.0", "jasmine-core": "^5.2.0", "jasmine-spec-reporter": "^7.0.0", "karma": "^6.4.3", @@ -113,16 +116,12 @@ "karma-jasmine": "^5.1.0", "karma-jasmine-html-reporter": "^2.1.0", "mockdate": "^3.0.5", - "ngx-i18nsupport": "^0.17.1", "prettier": "^3.3.3", "react": "^18.3.1", "react-dom": "^18.3.1", "storybook": "^7.6.20", "ts-node": "^10.9.2", - "typescript": "~5.4.5", + "typescript": "~5.5.4", "xliff": "^6.2.1" - }, - "overrides": { - "webpack": "5.89.0" } } diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 11b9035cba..2e4c2a4a12 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -20,7 +20,11 @@ import { AppComponent } from "./app.component"; import { AppModule } from "./app.module"; import { environment } from "../environments/environment"; import { Database } from "./core/database/database"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { provideHttpClientTesting } from "@angular/common/http/testing"; +import { + provideHttpClient, + withInterceptorsFromDi, +} from "@angular/common/http"; describe("AppComponent", () => { let component: AppComponent; @@ -31,7 +35,11 @@ describe("AppComponent", () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; environment.demo_mode = true; TestBed.configureTestingModule({ - imports: [AppModule, HttpClientTestingModule], + imports: [AppModule], + providers: [ + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), + ], }).compileComponents(); })); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 755e371c59..51bf99fc41 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -18,7 +18,10 @@ import { BrowserModule } from "@angular/platform-browser"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { LOCALE_ID, NgModule } from "@angular/core"; -import { HttpClientModule } from "@angular/common/http"; +import { + provideHttpClient, + withInterceptorsFromDi, +} from "@angular/common/http"; import { AppComponent } from "./app.component"; import { allRoutes } from "./app.routing"; @@ -67,8 +70,6 @@ import { } from "./utils/di-tokens"; import { AttendanceModule } from "./child-dev-project/attendance/attendance.module"; import { NotesModule } from "./child-dev-project/notes/notes.module"; -import { SchoolsModule } from "./child-dev-project/schools/schools.module"; -import { HistoricalDataModule } from "./features/historical-data/historical-data.module"; import { MatchingEntitiesModule } from "./features/matching-entities/matching-entities.module"; import { ProgressDashboardWidgetModule } from "./features/dashboard-widgets/progress-dashboard-widget/progress-dashboard-widget.module"; import { ReportingModule } from "./features/reporting/reporting.module"; @@ -94,6 +95,7 @@ import { APP_INITIALIZER_DEMO_DATA } from "./core/demo-data/demo-data.app-initia */ @NgModule({ declarations: [AppComponent], + bootstrap: [AppComponent], imports: [ // Global Angular modules ServiceWorkerModule.register("ngsw-worker.js"), @@ -102,7 +104,6 @@ import { APP_INITIALIZER_DEMO_DATA } from "./core/demo-data/demo-data.app-initia }), BrowserModule, BrowserAnimationsModule, - HttpClientModule, RouterModule.forRoot(allRoutes), // Core modules CoreModule, @@ -116,12 +117,10 @@ import { APP_INITIALIZER_DEMO_DATA } from "./core/demo-data/demo-data.app-initia AttendanceModule, ChildrenModule, NotesModule, - SchoolsModule, // feature module ImportModule, FileModule, MarkdownPageModule, - HistoricalDataModule, LocationModule, MatchingEntitiesModule, ProgressDashboardWidgetModule, @@ -167,8 +166,8 @@ import { APP_INITIALIZER_DEMO_DATA } from "./core/demo-data/demo-data.app-initia }, APP_INITIALIZER_PROPAGATE_CONFIG_UPDATES, APP_INITIALIZER_DEMO_DATA, + provideHttpClient(withInterceptorsFromDi()), ], - bootstrap: [AppComponent], }) export class AppModule { constructor(icons: FaIconLibrary) { diff --git a/src/app/child-dev-project/attendance/activities-overview/activities-overview.component.ts b/src/app/child-dev-project/attendance/activities-overview/activities-overview.component.ts index 23d06654a3..81a9137c8d 100644 --- a/src/app/child-dev-project/attendance/activities-overview/activities-overview.component.ts +++ b/src/app/child-dev-project/attendance/activities-overview/activities-overview.component.ts @@ -20,7 +20,7 @@ export class ActivitiesOverviewComponent extends RelatedEntitiesComponent implements OnInit { - entityCtr = RecurringActivity; + override entityCtr = RecurringActivity; titleColumn: FormFieldConfig = { id: "title", @@ -39,7 +39,7 @@ export class ActivitiesOverviewComponent { id: "excludedParticipants" }, ]; - async ngOnInit() { + override async ngOnInit() { this.titleColumn.additional.relevantValue = this.entity.getId(); await super.ngOnInit(); } diff --git a/src/app/child-dev-project/attendance/activity-attendance-section/activity-attendance-section.stories.ts b/src/app/child-dev-project/attendance/activity-attendance-section/activity-attendance-section.stories.ts index dbdbfdc743..dd7d5bdf05 100644 --- a/src/app/child-dev-project/attendance/activity-attendance-section/activity-attendance-section.stories.ts +++ b/src/app/child-dev-project/attendance/activity-attendance-section/activity-attendance-section.stories.ts @@ -16,8 +16,8 @@ import moment from "moment"; import { AttendanceService } from "../attendance.service"; import { ChildrenService } from "../../children/children.service"; import { of } from "rxjs"; -import { Child } from "../../children/model/child"; import { importProvidersFrom } from "@angular/core"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; const demoActivity = RecurringActivity.create("Coaching Batch C"); const attendanceRecords = [ @@ -89,7 +89,9 @@ export default { }, { provide: ChildrenService, - useValue: { getChild: () => of(Child.create("John Doe")) }, + useValue: { + getChild: () => of(TestEntity.create({ name: "John Doe" })), + }, }, ], }), diff --git a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts index bd5f8760b9..e450a37d4e 100644 --- a/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts +++ b/src/app/child-dev-project/attendance/add-day-attendance/roll-call-setup/roll-call-setup.component.spec.ts @@ -14,7 +14,7 @@ import { AttendanceService } from "../../attendance.service"; import { EventNote } from "../../model/event-note"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; import { TEST_USER } from "../../../../core/user/demo-user-generator.service"; -import { User } from "../../../../core/user/user"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; describe("RollCallSetupComponent", () => { let component: RollCallSetupComponent; @@ -66,10 +66,10 @@ describe("RollCallSetupComponent", () => { expect(component.existingEvents.length).toBe(2); expect(component.existingEvents[0].authors).toEqual([ - `${User.ENTITY_TYPE}:${TEST_USER}`, + `${TestEntity.ENTITY_TYPE}:${TEST_USER}`, ]); expect(component.existingEvents[1].authors).toEqual([ - `${User.ENTITY_TYPE}:${TEST_USER}`, + `${TestEntity.ENTITY_TYPE}:${TEST_USER}`, ]); })); diff --git a/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts b/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts index 0f5155989f..49c3e37673 100644 --- a/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts +++ b/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.spec.ts @@ -9,7 +9,6 @@ import { import { RollCallComponent } from "./roll-call.component"; import { Note } from "../../../notes/model/note"; import { By } from "@angular/platform-browser"; -import { Child } from "../../../children/model/child"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; import { ConfirmationDialogService } from "../../../../core/common-components/confirmation-dialog/confirmation-dialog.service"; import { LoginState } from "../../../../core/session/session-states/login-state.enum"; @@ -17,6 +16,7 @@ import { SimpleChange } from "@angular/core"; import { AttendanceLogicalStatus } from "../../model/attendance-status"; import { ChildrenService } from "../../../children/children.service"; import { ConfigurableEnumService } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum.service"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; const PRESENT = { id: "PRESENT", @@ -37,16 +37,18 @@ describe("RollCallComponent", () => { let component: RollCallComponent; let fixture: ComponentFixture; - let participant1: Child, participant2: Child, participant3: Child; + let participant1: TestEntity, + participant2: TestEntity, + participant3: TestEntity; const dummyChanges = { eventEntity: new SimpleChange(undefined, {}, true), }; beforeEach(waitForAsync(() => { - participant1 = new Child("child1"); - participant2 = new Child("child2"); - participant3 = new Child("child3"); + participant1 = new TestEntity("child1"); + participant2 = new TestEntity("child2"); + participant3 = new TestEntity("child3"); TestBed.configureTestingModule({ imports: [ @@ -230,8 +232,8 @@ describe("RollCallComponent", () => { })); function testParticipantsAreSorted( - participantsInput: Child[], - expectedParticipantsOrder: Child[], + participantsInput: TestEntity[], + expectedParticipantsOrder: TestEntity[], sortParticipantsBy: string, ) { const event = new Note(); diff --git a/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.ts b/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.ts index e329d0b389..2af57483af 100644 --- a/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.ts +++ b/src/app/child-dev-project/attendance/add-day-attendance/roll-call/roll-call.component.ts @@ -15,7 +15,7 @@ import { import { Note } from "../../../notes/model/note"; import { EventAttendance } from "../../model/event-attendance"; import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; -import { Child } from "../../../children/model/child"; +import { Entity } from "../../../../core/entity/model/entity"; import { Logging } from "../../../../core/logging/logging.service"; import { sortByAttribute } from "../../../../utils/utils"; import { FormDialogService } from "../../../../core/form-dialog/form-dialog.service"; @@ -38,7 +38,7 @@ import { ConfirmationDialogService } from "../../../../core/common-components/co // Only allow horizontal swiping @Injectable() class HorizontalHammerConfig extends HammerGestureConfig { - overrides = { + override overrides = { swipe: { direction: Hammer.DIRECTION_HORIZONTAL }, pinch: { enable: false }, rotate: { enable: false }, @@ -105,7 +105,7 @@ export class RollCallComponent implements OnChanges { * The index, child and attendance that is currently being processed */ currentIndex = 0; - currentChild: Child; + currentChild: Entity; currentAttendance: EventAttendance; /** * whether any changes have been made to the model @@ -115,8 +115,8 @@ export class RollCallComponent implements OnChanges { /** options available for selecting an attendance status */ availableStatus: AttendanceStatusType[]; - children: Child[] = []; - inactiveParticipants: Child[]; + children: Entity[] = []; + inactiveParticipants: Entity[]; constructor( private enumService: ConfigurableEnumService, @@ -170,9 +170,12 @@ export class RollCallComponent implements OnChanges { this.children = []; this.inactiveParticipants = []; for (const childId of this.eventEntity.children) { - let child: Child; + let child: Entity; try { - child = await this.entityMapper.load(Child, childId); + child = await this.entityMapper.load( + Entity.extractTypeFromId(childId), + childId, + ); } catch (e) { Logging.debug( "Could not find child " + diff --git a/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts b/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts index d7bccf74e8..77e4102925 100644 --- a/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts +++ b/src/app/child-dev-project/attendance/attendance-calendar/attendance-calendar.component.spec.ts @@ -6,7 +6,6 @@ import { generateEventWithAttendance } from "../model/activity-attendance"; import { SimpleChange } from "@angular/core"; import moment from "moment"; import { Note } from "../../notes/model/note"; -import { Child } from "../../children/model/child"; import { defaultAttendanceStatusTypes } from "../../../core/config/default-config/default-attendance-status-types"; import { mockEntityMapper } from "../../../core/entity/entity-mapper/mock-entity-mapper-service"; import { EventNote } from "../model/event-note"; @@ -15,6 +14,7 @@ import { AnalyticsService } from "../../../core/analytics/analytics.service"; import { EntityAbility } from "../../../core/permissions/ability/entity-ability"; import { FormDialogService } from "../../../core/form-dialog/form-dialog.service"; import { MatNativeDateModule } from "@angular/material/core"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("AttendanceCalendarComponent", () => { let component: AttendanceCalendarComponent; @@ -78,9 +78,9 @@ describe("AttendanceCalendarComponent", () => { }); it("should correctly compute the average attendance", () => { - const attendedChild = new Child("attendedChild"); - const absentChild = new Child("absentChild"); - const childWithoutAttendance = new Child("childWithoutAttendance"); + const attendedChild = new TestEntity("attendedChild"); + const absentChild = new TestEntity("absentChild"); + const childWithoutAttendance = new TestEntity("childWithoutAttendance"); const note = new Note(); note.date = new Date(); note.addChild(attendedChild); @@ -105,7 +105,7 @@ describe("AttendanceCalendarComponent", () => { it("should add focused participant on the fly if not part of event already", () => { const testDate = new Date(); - const excludedChild = new Child("excluded_child"); + const excludedChild = new TestEntity("excluded_child"); const note = new Note(); note.date = testDate; component.records = [note]; diff --git a/src/app/child-dev-project/attendance/attendance.service.spec.ts b/src/app/child-dev-project/attendance/attendance.service.spec.ts index 75bd6f421e..aa4267f783 100644 --- a/src/app/child-dev-project/attendance/attendance.service.spec.ts +++ b/src/app/child-dev-project/attendance/attendance.service.spec.ts @@ -9,12 +9,12 @@ import { defaultInteractionTypes } from "../../core/config/default-config/defaul import { expectEntitiesToMatch } from "../../utils/expect-entity-data.spec"; import { EventNote } from "./model/event-note"; import { ChildrenService } from "../children/children.service"; -import { School } from "../schools/model/school"; import { ChildSchoolRelation } from "../children/model/childSchoolRelation"; -import { Child } from "../children/model/child"; import { Note } from "../notes/model/note"; import { DatabaseTestingModule } from "../../utils/database-testing.module"; import { Entity } from "../../core/entity/model/entity"; +import { createEntityOfType } from "../../core/demo-data/create-entity-of-type"; +import { TestEntity } from "../../utils/test-utils/TestEntity"; describe("AttendanceService", () => { let service: AttendanceService; @@ -282,7 +282,7 @@ describe("AttendanceService", () => { it("should include children from a linked school for event from activity", async () => { const activity = new RecurringActivity(); - const linkedSchool = new School(); + const linkedSchool = createEntityOfType("School"); activity.linkedGroups.push(linkedSchool.getId()); const childAttendingSchool = new ChildSchoolRelation(); @@ -292,7 +292,7 @@ describe("AttendanceService", () => { "queryActiveRelationsOf", ).and.resolveTo([childAttendingSchool]); - const directlyAddedChild = new Child(); + const directlyAddedChild = new TestEntity(); activity.participants.push(directlyAddedChild.getId()); const date = new Date(); @@ -306,22 +306,22 @@ describe("AttendanceService", () => { it("should not include duplicate children for event from activity", async () => { const activity = new RecurringActivity(); - const linkedSchool = new School(); + const linkedSchool = createEntityOfType("School"); activity.linkedGroups.push(linkedSchool.getId()); - const duplicateChild = new Child(); + const duplicateChild = new TestEntity(); const duplicateChildRelation = new ChildSchoolRelation(); duplicateChildRelation.childId = duplicateChild.getId(); duplicateChildRelation.schoolId = linkedSchool.getId(); const anotherRelation = new ChildSchoolRelation(); anotherRelation.childId = Entity.createPrefixedId( - Child.ENTITY_TYPE, + TestEntity.ENTITY_TYPE, "another_child_id", ); anotherRelation.schoolId = linkedSchool.getId(); await entityMapper.saveAll([duplicateChildRelation, anotherRelation]); - const directlyAddedChild = new Child(); + const directlyAddedChild = new TestEntity(); activity.participants.push( directlyAddedChild.getId(), duplicateChild.getId(), diff --git a/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.spec.ts b/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.spec.ts index d9b7e87bd1..fbdc025c03 100644 --- a/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.spec.ts +++ b/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.spec.ts @@ -2,7 +2,6 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { AttendanceWeekDashboardComponent } from "./attendance-week-dashboard.component"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; -import { Child } from "../../../children/model/child"; import { EventNote } from "../../model/event-note"; import { defaultAttendanceStatusTypes } from "../../../../core/config/default-config/default-attendance-status-types"; import { AttendanceLogicalStatus } from "../../model/attendance-status"; @@ -11,6 +10,7 @@ import { AttendanceService } from "../../attendance.service"; import { RecurringActivity } from "../../model/recurring-activity"; import moment from "moment"; import * as MockDate from "mockdate"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; describe("AttendanceWeekDashboardComponent", () => { let component: AttendanceWeekDashboardComponent; @@ -44,8 +44,8 @@ describe("AttendanceWeekDashboardComponent", () => { }); it("should display children with low attendance", async () => { - const absentChild = new Child(); - const presentChild = new Child(); + const absentChild = new TestEntity(); + const presentChild = new TestEntity(); const mondayLastWeek = moment().startOf("isoWeek").subtract(7, "days"); const e1 = EventNote.create(mondayLastWeek.toDate()); const e2 = EventNote.create(moment(e1.date).add(1, "day").toDate()); diff --git a/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.ts b/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.ts index 3800ac09e5..0e4a8dc371 100644 --- a/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.ts +++ b/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.component.ts @@ -1,6 +1,5 @@ import { Component, Input, OnInit } from "@angular/core"; import { Router } from "@angular/router"; -import { Child } from "../../../children/model/child"; import { AttendanceLogicalStatus } from "../../model/attendance-status"; import { AttendanceService } from "../../attendance.service"; import { EventAttendance } from "../../model/event-attendance"; @@ -16,6 +15,7 @@ import { AttendanceDayBlockComponent } from "./attendance-day-block/attendance-d import { DashboardWidget } from "../../../../core/dashboard/dashboard-widget/dashboard-widget"; import { EventNote } from "../../model/event-note"; import { DashboardListWidgetComponent } from "../../../../core/dashboard/dashboard-list-widget/dashboard-list-widget.component"; +import { EntityRegistry } from "../../../../core/entity/database-entity.decorator"; interface AttendanceWeekRow { childId: string; @@ -42,7 +42,7 @@ export class AttendanceWeekDashboardComponent extends DashboardWidget implements OnInit { - static getRequiredEntities() { + static override getRequiredEntities() { return EventNote.ENTITY_TYPE; } @@ -85,6 +85,7 @@ export class AttendanceWeekDashboardComponent constructor( private attendanceService: AttendanceService, private router: Router, + private entityRegistry: EntityRegistry, ) { super(); } @@ -180,6 +181,7 @@ export class AttendanceWeekDashboardComponent } goToChild(childId: string) { + const Child = this.entityRegistry.get("Child"); this.router.navigate([Child.route, childId]); } } diff --git a/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.stories.ts b/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.stories.ts index 58bd9ef872..7c5c5a212d 100644 --- a/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.stories.ts +++ b/src/app/child-dev-project/attendance/dashboard-widgets/attendance-week-dashboard/attendance-week-dashboard.stories.ts @@ -1,7 +1,6 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { AttendanceWeekDashboardComponent } from "./attendance-week-dashboard.component"; import { RecurringActivity } from "../../model/recurring-activity"; -import { Child } from "../../../children/model/child"; import { generateEventWithAttendance } from "../../model/activity-attendance"; import { AttendanceLogicalStatus } from "../../model/attendance-status"; import { Note } from "../../../notes/model/note"; @@ -9,9 +8,10 @@ import moment from "moment"; import { StorybookBaseModule } from "../../../../utils/storybook-base.module"; import { DatabaseIndexingService } from "../../../../core/entity/database-indexing/database-indexing.service"; import { importProvidersFrom } from "@angular/core"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; -const child1 = Child.create("Jack"); -const child2 = Child.create("Jane"); +const child1 = TestEntity.create("Jack"); +const child2 = TestEntity.create("Jane"); const act1 = RecurringActivity.create("Demo Activity"); act1.participants.push(child1.getId()); diff --git a/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.spec.ts b/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.spec.ts index c839abe397..9ed1a62e0b 100644 --- a/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.spec.ts +++ b/src/app/child-dev-project/attendance/demo-data/demo-activity-events-generator.service.spec.ts @@ -1,16 +1,16 @@ import { DemoActivityGeneratorService } from "./demo-activity-generator.service"; import { DemoDataGenerator } from "../../../core/demo-data/demo-data-generator"; import { RecurringActivity } from "../model/recurring-activity"; -import { Child } from "../../children/model/child"; import { EventNote } from "../model/event-note"; import { DemoActivityEventsGeneratorService } from "./demo-activity-events-generator.service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("DemoActivityEventsGenerator", () => { let service: DemoDataGenerator; beforeEach(() => { const testActivity = RecurringActivity.create("test-activity"); - testActivity.participants.push(Child.create("John Doe").getId()); + testActivity.participants.push(TestEntity.create("John Doe").getId()); const mockActivityGenerator = { entities: [testActivity], diff --git a/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.spec.ts b/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.spec.ts index 3bb088c44d..61f6d4c44d 100644 --- a/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.spec.ts +++ b/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.spec.ts @@ -3,19 +3,20 @@ import { DemoDataGenerator } from "../../../core/demo-data/demo-data-generator"; import { RecurringActivity } from "../model/recurring-activity"; import { DemoChildGenerator } from "../../children/demo-data-generators/demo-child-generator.service"; import { DemoUserGeneratorService } from "../../../core/user/demo-user-generator.service"; -import { Child } from "../../children/model/child"; -import { User } from "../../../core/user/user"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { Entity } from "../../../core/entity/model/entity"; describe("DemoActivityGenerator", () => { let service: DemoDataGenerator; beforeEach(() => { const mockChildGenerator = { - entities: [Child.create("John Doe")], + entities: [TestEntity.create("John Doe")] as Entity[], } as DemoChildGenerator; const mockUserGenerator = { - entities: [new User("test-user")], + entities: [createEntityOfType("User", "test-user")] as Entity[], } as DemoUserGeneratorService; service = new DemoActivityGeneratorService( diff --git a/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts b/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts index dc54605db1..eb725474e8 100644 --- a/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts +++ b/src/app/child-dev-project/attendance/demo-data/demo-activity-generator.service.ts @@ -1,12 +1,11 @@ import { DemoChildGenerator } from "../../children/demo-data-generators/demo-child-generator.service"; import { DemoDataGenerator } from "../../../core/demo-data/demo-data-generator"; import { Injectable } from "@angular/core"; -import { Child } from "../../children/model/child"; import { faker } from "../../../core/demo-data/faker"; import { RecurringActivity } from "../model/recurring-activity"; import { DemoUserGeneratorService } from "../../../core/user/demo-user-generator.service"; -import { User } from "../../../core/user/user"; import { defaultInteractionTypes } from "../../../core/config/default-config/default-interaction-types"; +import { Entity } from "../../../core/entity/model/entity"; /** * Generate RecurringActivity entities @@ -25,8 +24,8 @@ export class DemoActivityGeneratorService extends DemoDataGenerator { let component: EditAttendanceComponent; @@ -19,10 +20,10 @@ describe("EditAttendanceComponent", () => { let categoryForm: FormControl; let childrenForm: FormControl; - let childrenEntities: Child[]; + let childrenEntities: Entity[]; beforeEach(async () => { - childrenEntities = [new Child("child1"), new Child("child2")]; + childrenEntities = [new TestEntity("child1"), new TestEntity("child2")]; await TestBed.configureTestingModule({ imports: [ diff --git a/src/app/child-dev-project/attendance/edit-attendance/edit-attendance.component.ts b/src/app/child-dev-project/attendance/edit-attendance/edit-attendance.component.ts index 3dad3b07a1..5b8eee01ed 100644 --- a/src/app/child-dev-project/attendance/edit-attendance/edit-attendance.component.ts +++ b/src/app/child-dev-project/attendance/edit-attendance/edit-attendance.component.ts @@ -44,7 +44,8 @@ export class EditAttendanceComponent { showAttendance = false; mobile = false; - @Input() entity: Note; + + @Input() declare entity: Note; attendanceForm: FormControl>; constructor(screenWithObserver: ScreenWidthObserver) { @@ -55,7 +56,7 @@ export class EditAttendanceComponent .subscribe((isDesktop) => (this.mobile = !isDesktop)); } - ngOnInit() { + override ngOnInit() { super.ngOnInit(); const category = this.parent.get( "category", diff --git a/src/app/child-dev-project/attendance/model/activity-attendance.ts b/src/app/child-dev-project/attendance/model/activity-attendance.ts index 842c169cd3..8dd2744181 100644 --- a/src/app/child-dev-project/attendance/model/activity-attendance.ts +++ b/src/app/child-dev-project/attendance/model/activity-attendance.ts @@ -197,7 +197,7 @@ export class ActivityAttendance extends Entity { /** * Custom warning level for attendance thresholds - optionally for a specific child. */ - public getWarningLevel(forChildId?: string): WarningLevel { + public override getWarningLevel(forChildId?: string): WarningLevel { let attendancePercentage; if (forChildId) { attendancePercentage = this.getAttendancePercentage(forChildId); @@ -216,7 +216,7 @@ export class ActivityAttendance extends Entity { } } - public getColor(forChildId?: string): string { + public override getColor(forChildId?: string): string { return getWarningLevelColor(this.getWarningLevel(forChildId)); } } diff --git a/src/app/child-dev-project/attendance/model/event-attendance.datatype.ts b/src/app/child-dev-project/attendance/model/event-attendance.datatype.ts index 7b155a4de1..429f83cb28 100644 --- a/src/app/child-dev-project/attendance/model/event-attendance.datatype.ts +++ b/src/app/child-dev-project/attendance/model/event-attendance.datatype.ts @@ -23,7 +23,7 @@ export class EventAttendanceMapDatatype extends DefaultDatatype< this.embeddedType = new EventAttendanceDatatype(schemaService); } - transformToDatabaseFormat(value: Map) { + override transformToDatabaseFormat(value: Map) { if (!(value instanceof Map)) { console.warn( 'property to be saved with "map" EntitySchema is not of expected type', @@ -39,7 +39,7 @@ export class EventAttendanceMapDatatype extends DefaultDatatype< return result; } - transformToObjectFormat(value: any[]) { + override transformToObjectFormat(value: any[]) { if (value instanceof Map) { // usually this shouldn't already be a map but in MockDatabase somehow this can happen return value; diff --git a/src/app/child-dev-project/attendance/model/event-note.ts b/src/app/child-dev-project/attendance/model/event-note.ts index 7af3a431a3..8a7afba7b9 100644 --- a/src/app/child-dev-project/attendance/model/event-note.ts +++ b/src/app/child-dev-project/attendance/model/event-note.ts @@ -20,7 +20,7 @@ import { Note } from "../../notes/model/note"; @DatabaseEntity("EventNote") export class EventNote extends Note { - static create(date: Date, subject: string = ""): EventNote { + static override create(date: Date, subject: string = ""): EventNote { const instance = new EventNote(); instance.date = date; instance.subject = subject; diff --git a/src/app/child-dev-project/attendance/model/recurring-activity.ts b/src/app/child-dev-project/attendance/model/recurring-activity.ts index c55d9e7320..78e15036e9 100644 --- a/src/app/child-dev-project/attendance/model/recurring-activity.ts +++ b/src/app/child-dev-project/attendance/model/recurring-activity.ts @@ -6,18 +6,11 @@ import { INTERACTION_TYPE_CONFIG_ID, InteractionType, } from "../../notes/model/interaction-type.interface"; -import { User } from "../../../core/user/user"; -import { Child } from "../../children/model/child"; -import { School } from "../../schools/model/school"; import { asArray } from "../../../utils/utils"; @DatabaseEntity("RecurringActivity") export class RecurringActivity extends Entity { - static toStringAttributes = ["title"]; - static label = $localize`:label for entity:Recurring Activity`; - static labelPlural = $localize`:label (plural) for entity:Recurring Activities`; - static color = "#00838F"; - static route = "attendance/recurring-activity"; + static override route = "attendance/recurring-activity"; static create(title: string = ""): RecurringActivity { const instance = new RecurringActivity(); @@ -34,12 +27,7 @@ export class RecurringActivity extends Entity { } /** primary name to identify the activity */ - @DatabaseField({ - label: $localize`:Label for the title of a recurring activity:Title`, - validators: { - required: true, - }, - }) + @DatabaseField() title: string = ""; /** @@ -48,7 +36,6 @@ export class RecurringActivity extends Entity { * This is also assigned to individual events' category generated for this activity. */ @DatabaseField({ - label: $localize`:Label for the interaction type of a recurring activity:Type`, dataType: "configurable-enum", additional: INTERACTION_TYPE_CONFIG_ID, }) @@ -56,37 +43,29 @@ export class RecurringActivity extends Entity { /** IDs of children linked to this activity */ @DatabaseField({ - label: $localize`:Label for the participants of a recurring activity:Participants`, dataType: "entity", isArray: true, - additional: Child.ENTITY_TYPE, }) participants: string[] = []; /** IDs of groups (schools, teams) whose (active) members should be included in the activity*/ @DatabaseField({ - label: $localize`:Label for the linked schools of a recurring activity:Groups`, dataType: "entity", isArray: true, - additional: School.ENTITY_TYPE, }) linkedGroups: string[] = []; /** IDs of children that should be excluded from this activity despite being a group member */ @DatabaseField({ - label: $localize`:Label for excluded participants of a recurring activity:Excluded Participants`, dataType: "entity", isArray: true, - additional: Child.ENTITY_TYPE, }) excludedParticipants: string[] = []; /** IDs of the users who are responsible for conducting this activity */ @DatabaseField({ - label: $localize`:Label for the assigned user(s) of a recurring activity:Assigned user(s)`, dataType: "entity", isArray: true, - additional: User.ENTITY_TYPE, }) assignedTo: string[] = []; diff --git a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts b/src/app/child-dev-project/attendance/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts similarity index 82% rename from src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts rename to src/app/child-dev-project/attendance/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts index 1789999e2b..33b0b5feaa 100644 --- a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts +++ b/src/app/child-dev-project/attendance/recent-attendance-blocks/recent-attendance-blocks.component.spec.ts @@ -1,12 +1,12 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { RecentAttendanceBlocksComponent } from "./recent-attendance-blocks.component"; -import { Child } from "../../model/child"; -import { AttendanceService } from "../../../attendance/attendance.service"; -import { ActivityAttendance } from "../../../attendance/model/activity-attendance"; -import { RecurringActivity } from "../../../attendance/model/recurring-activity"; -import { defaultInteractionTypes } from "../../../../core/config/default-config/default-interaction-types"; -import { WINDOW_TOKEN } from "../../../../utils/di-tokens"; +import { AttendanceService } from "../attendance.service"; +import { ActivityAttendance } from "../model/activity-attendance"; +import { RecurringActivity } from "../model/recurring-activity"; +import { defaultInteractionTypes } from "../../../core/config/default-config/default-interaction-types"; +import { WINDOW_TOKEN } from "../../../utils/di-tokens"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("RecentAttendanceBlocksComponent", () => { let component: RecentAttendanceBlocksComponent; @@ -40,7 +40,7 @@ describe("RecentAttendanceBlocksComponent", () => { }); it("should display blocks for all activities of the filtered activity type", async () => { - const testChild = new Child("testID"); + const testChild = new TestEntity("testID"); const testActivity1 = RecurringActivity.create("test 1"); testActivity1.type = defaultInteractionTypes[1]; const testActivity2 = RecurringActivity.create("test 2"); diff --git a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.ts b/src/app/child-dev-project/attendance/recent-attendance-blocks/recent-attendance-blocks.component.ts similarity index 83% rename from src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.ts rename to src/app/child-dev-project/attendance/recent-attendance-blocks/recent-attendance-blocks.component.ts index 22b154d910..bc03b096af 100644 --- a/src/app/child-dev-project/children/children-list/recent-attendance-blocks/recent-attendance-blocks.component.ts +++ b/src/app/child-dev-project/attendance/recent-attendance-blocks/recent-attendance-blocks.component.ts @@ -1,16 +1,16 @@ import { Component, Input, OnInit } from "@angular/core"; -import { Child } from "../../model/child"; -import { ActivityAttendance } from "../../../attendance/model/activity-attendance"; -import { AttendanceService } from "../../../attendance/attendance.service"; +import { Entity } from "../../../core/entity/model/entity"; +import { ActivityAttendance } from "../model/activity-attendance"; +import { AttendanceService } from "../attendance.service"; import moment from "moment"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; -import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; +import { DynamicComponent } from "../../../core/config/dynamic-components/dynamic-component.decorator"; import { ScreenSize, ScreenWidthObserver, -} from "../../../../utils/media/screen-size-observer.service"; +} from "../../../utils/media/screen-size-observer.service"; import { NgForOf, SlicePipe } from "@angular/common"; -import { AttendanceBlockComponent } from "../../../attendance/attendance-block/attendance-block.component"; +import { AttendanceBlockComponent } from "../attendance-block/attendance-block.component"; /** * This component lists attendance blocks for a child for recent months filtered by institutions. @@ -35,7 +35,7 @@ export class RecentAttendanceBlocksComponent implements OnInit { attendanceList: ActivityAttendance[] = []; maxAttendanceBlocks: number = 3; - @Input() entity: Child; + @Input() entity: Entity; @Input() config: { filterByActivityType: string }; constructor( diff --git a/src/app/child-dev-project/children/aser/model/aser.spec.ts b/src/app/child-dev-project/children/aser/model/aser.spec.ts deleted file mode 100644 index bc648e8b9d..0000000000 --- a/src/app/child-dev-project/children/aser/model/aser.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { Aser } from "./aser"; -import { mathLevels, readingLevels } from "./skill-levels"; -import { WarningLevel } from "../../../warning-level"; -import { testEntitySubclass } from "../../../../core/entity/model/entity.spec"; - -describe("Aser", () => { - testEntitySubclass("Aser", Aser, { - _id: "Aser:some-id", - - child: "1", - date: new Date(), - hindi: readingLevels[2].id, - bengali: readingLevels[1].id, - english: readingLevels[2].id, - math: mathLevels[4].id, - remarks: "nothing to remark", - }); - - it("warning level OK if no results", function () { - const id = "test1"; - const entity = new Aser(id); - - expect(entity.getWarningLevel()).toBe(WarningLevel.OK); - }); - - it("warning level WARNING if some results are not passed", function () { - const id = "test1"; - const entity = new Aser(id); - entity.english = readingLevels[1]; - entity.math = readingLevels[2]; - - expect(entity.getWarningLevel()).toBe(WarningLevel.WARNING); - }); - - it("has a warning level of OK if english is passed", () => { - const entity = new Aser(); - entity.english = readingLevels.find((l) => l.passed); - - expect(entity.getWarningLevel()).toBe(WarningLevel.OK); - }); - - it("has a warning level of OK if all skills are passed", () => { - const entity = new Aser(); - entity.math = mathLevels.find((l) => l.passed); - entity.english = readingLevels.find((l) => l.passed); - entity.hindi = readingLevels.find((l) => l.passed); - entity.bengali = readingLevels.find((l) => l.passed); - - expect(entity.getWarningLevel()).toBe(WarningLevel.OK); - }); - - it("has warning level OK if some subjects are passed and others are empty", () => { - const entity = new Aser(); - entity.math = mathLevels.find((l) => l.passed); - entity.english = readingLevels.find((l) => l.passed); - entity.hindi = readingLevels.find((l) => l.id === ""); - entity.bengali = undefined; - - expect(entity.getWarningLevel()).toBe(WarningLevel.OK); - }); -}); diff --git a/src/app/child-dev-project/children/aser/model/aser.ts b/src/app/child-dev-project/children/aser/model/aser.ts deleted file mode 100644 index b345f50670..0000000000 --- a/src/app/child-dev-project/children/aser/model/aser.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { Entity } from "../../../../core/entity/model/entity"; -import { DatabaseField } from "../../../../core/entity/database-field.decorator"; -import { DatabaseEntity } from "../../../../core/entity/database-entity.decorator"; -import { SkillLevel } from "./skill-levels"; -import { WarningLevel } from "../../../warning-level"; -import { ConfigurableEnumDatatype } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum-datatype/configurable-enum.datatype"; -import { PLACEHOLDERS } from "../../../../core/entity/schema/entity-schema-field"; -import { Child } from "../../model/child"; - -@DatabaseEntity("Aser") -export class Aser extends Entity { - static override hasPII = true; - - @DatabaseField({ - dataType: "entity", - additional: Child.ENTITY_TYPE, - entityReferenceRole: "composite", - }) - child: string; - - @DatabaseField({ - label: $localize`:Label for date of the ASER results:Date`, - defaultValue: { - mode: "dynamic", - value: PLACEHOLDERS.NOW, - }, - anonymize: "retain-anonymized", - }) - date: Date; - - @DatabaseField({ - label: $localize`:Label of the Hindi ASER result:Hindi`, - dataType: "configurable-enum", - additional: "reading-levels", - }) - hindi: SkillLevel; - @DatabaseField({ - label: $localize`:Label of the Bengali ASER result:Bengali`, - dataType: "configurable-enum", - additional: "reading-levels", - }) - bengali: SkillLevel; - @DatabaseField({ - label: $localize`:Label of the English ASER result:English`, - dataType: "configurable-enum", - additional: "reading-levels", - }) - english: SkillLevel; - @DatabaseField({ - label: $localize`:Label of the Math ASER result:Math`, - dataType: "configurable-enum", - additional: "math-levels", - }) - math: SkillLevel; - - @DatabaseField({ - label: $localize`:Label for the remarks of a ASER result:Remarks`, - }) - remarks: string = ""; - - getWarningLevel(): WarningLevel { - if (this.hasPassedEverything()) { - return WarningLevel.OK; - } else { - return WarningLevel.WARNING; - } - } - - private hasPassedEverything(): boolean { - const schema = this.getSchema(); - return Object.keys(this) - .filter( - (key) => - schema.get(key)?.dataType === ConfigurableEnumDatatype.dataType, - ) - .every((key) => this.isPassed(this[key])); - } - - private isPassed(value: SkillLevel): boolean { - return !value || value.id === "" || value.passed; - } -} diff --git a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.html b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.html index 410de8035b..3d80ed68b5 100644 --- a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.html +++ b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.html @@ -7,23 +7,23 @@ />
-

{{ entity?.name }}

+

{{ entity?.["name"] }}

- {{ entity?.phone }} -

+

, - class {{ entity?.schoolClass }} + class {{ entity?.["schoolClass"] }}

diff --git a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.spec.ts b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.spec.ts index 643cbc08ec..a8c09a2ef6 100644 --- a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.spec.ts +++ b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ChildBlockTooltipComponent } from "./child-block-tooltip.component"; import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; import { FileService } from "../../../../features/file/file.service"; -import { Child } from "../../model/child"; +import { createEntityOfType } from "../../../../core/demo-data/create-entity-of-type"; describe("ChildBlockTooltipComponent", () => { let component: ChildBlockTooltipComponent; @@ -19,7 +19,7 @@ describe("ChildBlockTooltipComponent", () => { beforeEach(() => { fixture = TestBed.createComponent(ChildBlockTooltipComponent); component = fixture.componentInstance; - component.entity = new Child(); + component.entity = createEntityOfType("Child"); fixture.detectChanges(); }); diff --git a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.ts b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.ts index b1cd1e61f5..de11c6bb23 100644 --- a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.ts +++ b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from "@angular/core"; -import { Child } from "../../model/child"; +import { Entity } from "../../../../core/entity/model/entity"; import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; import { NgForOf, NgIf } from "@angular/common"; import { FaDynamicIconComponent } from "../../../../core/common-components/fa-dynamic-icon/fa-dynamic-icon.component"; @@ -25,14 +25,15 @@ import { EntityBlockComponent } from "../../../../core/basic-datatypes/entity/en }) export class ChildBlockTooltipComponent implements OnInit { /** The entity to show the tooltip for */ - @Input() entity: Child; - icon = Child.icon; + @Input() entity: Entity; + icon: string; imgPath: SafeUrl; constructor(private fileService: FileService) {} ngOnInit() { - if (this.entity.photo) { + this.icon = this.entity?.getConstructor().icon; + if (this.entity?.["photo"]) { this.fileService .loadFile(this.entity, "photo") .subscribe((res) => (this.imgPath = res)); diff --git a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.stories.ts b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.stories.ts index dbee432165..624f121ee8 100644 --- a/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.stories.ts +++ b/src/app/child-dev-project/children/child-block/child-block-tooltip/child-block-tooltip.stories.ts @@ -1,8 +1,8 @@ -import { Child } from "../../model/child"; import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { StorybookBaseModule } from "../../../../utils/storybook-base.module"; import { ChildBlockTooltipComponent } from "./child-block-tooltip.component"; import { importProvidersFrom } from "@angular/core"; +import { createEntityOfType } from "../../../../core/demo-data/create-entity-of-type"; export default { title: "Features/Participant/ChildBlockTooltip", @@ -14,7 +14,7 @@ export default { ], } as Meta; -const demoChild = new Child("1"); +const demoChild = createEntityOfType("Child", "1"); demoChild.name = "John Doe"; demoChild.projectNumber = "99"; demoChild.phone = "+49 199 1234567"; diff --git a/src/app/child-dev-project/children/child-block/child-block.component.html b/src/app/child-dev-project/children/child-block/child-block.component.html index a5e2166e74..18f2cbb9e4 100644 --- a/src/app/child-dev-project/children/child-block/child-block.component.html +++ b/src/app/child-dev-project/children/child-block/child-block.component.html @@ -12,8 +12,8 @@ class="child-pic" > {{ entity?.toString() }} - - ({{ entity?.projectNumber }}) + ({{ entity?.["projectNumber"] }}) diff --git a/src/app/child-dev-project/children/child-block/child-block.component.spec.ts b/src/app/child-dev-project/children/child-block/child-block.component.spec.ts index f847898ea8..011ab2b3fd 100644 --- a/src/app/child-dev-project/children/child-block/child-block.component.spec.ts +++ b/src/app/child-dev-project/children/child-block/child-block.component.spec.ts @@ -2,9 +2,9 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { ChildBlockComponent } from "./child-block.component"; import { ChildrenService } from "../children.service"; -import { Child } from "../model/child"; import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; import { FileService } from "../../../features/file/file.service"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; describe("ChildBlockComponent", () => { let component: ChildBlockComponent; @@ -15,7 +15,7 @@ describe("ChildBlockComponent", () => { mockChildrenService = jasmine.createSpyObj("mockChildrenService", [ "getChild", ]); - mockChildrenService.getChild.and.resolveTo(new Child("")); + mockChildrenService.getChild.and.resolveTo(createEntityOfType("Child")); TestBed.configureTestingModule({ imports: [ChildBlockComponent, FontAwesomeTestingModule], @@ -29,7 +29,7 @@ describe("ChildBlockComponent", () => { beforeEach(() => { fixture = TestBed.createComponent(ChildBlockComponent); component = fixture.componentInstance; - component.entity = new Child(""); + component.entity = createEntityOfType("Child"); fixture.detectChanges(); }); diff --git a/src/app/child-dev-project/children/child-block/child-block.component.ts b/src/app/child-dev-project/children/child-block/child-block.component.ts index 90eb5b97f1..afdb1b5fa8 100644 --- a/src/app/child-dev-project/children/child-block/child-block.component.ts +++ b/src/app/child-dev-project/children/child-block/child-block.component.ts @@ -6,12 +6,11 @@ import { SimpleChanges, } from "@angular/core"; import { ChildrenService } from "../children.service"; -import { Child } from "../model/child"; +import { Entity } from "../../../core/entity/model/entity"; import { DynamicComponent } from "../../../core/config/dynamic-components/dynamic-component.decorator"; import { NgIf } from "@angular/common"; import { TemplateTooltipDirective } from "../../../core/common-components/template-tooltip/template-tooltip.directive"; import { ChildBlockTooltipComponent } from "./child-block-tooltip/child-block-tooltip.component"; -import { FileService } from "../../../features/file/file.service"; import { DisplayImgComponent } from "../../../features/file/display-img/display-img.component"; @DynamicComponent("ChildBlock") @@ -28,7 +27,7 @@ import { DisplayImgComponent } from "../../../features/file/display-img/display- standalone: true, }) export class ChildBlockComponent implements OnChanges { - @Input() entity: Child; + @Input() entity: Entity; @Input() entityId: string; /** prevent clicks on the component to navigate to the details page */ @@ -37,16 +36,14 @@ export class ChildBlockComponent implements OnChanges { /** prevent additional details to be displayed in a tooltip on mouse over */ @Input() tooltipDisabled: boolean; - icon = Child.icon; + icon: string; - constructor( - private fileService: FileService, - @Optional() private childrenService: ChildrenService, - ) {} + constructor(@Optional() private childrenService: ChildrenService) {} async ngOnChanges(changes: SimpleChanges) { if (changes.hasOwnProperty("entityId")) { this.entity = await this.childrenService.getChild(this.entityId); + this.icon = this.entity?.getConstructor().icon; } } } diff --git a/src/app/child-dev-project/children/child-block/child-block.stories.ts b/src/app/child-dev-project/children/child-block/child-block.stories.ts index 3a3f49be50..0e4842dcd9 100644 --- a/src/app/child-dev-project/children/child-block/child-block.stories.ts +++ b/src/app/child-dev-project/children/child-block/child-block.stories.ts @@ -1,8 +1,8 @@ import { ChildBlockComponent } from "./child-block.component"; -import { Child } from "../model/child"; import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { StorybookBaseModule } from "../../../utils/storybook-base.module"; import { importProvidersFrom } from "@angular/core"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; export default { title: "Features/Participant/ChildBlock", @@ -14,7 +14,7 @@ export default { ], } as Meta; -const demoChild = new Child("1"); +const demoChild = createEntityOfType("Child", "1"); demoChild.name = "John Doe"; demoChild.projectNumber = "99"; demoChild.phone = "+49 199 1234567"; @@ -30,7 +30,8 @@ Primary.args = { entity: demoChild, }; -const demoInactiveChild = Child.create("John Doe"); +const demoInactiveChild = createEntityOfType("Child"); +demoInactiveChild.name = "John Doe"; demoInactiveChild.projectNumber = "42"; demoInactiveChild.status = "Dropout"; diff --git a/src/app/child-dev-project/children/child-details/grouped-child-attendance/grouped-child-attendance.component.ts b/src/app/child-dev-project/children/child-details/grouped-child-attendance/grouped-child-attendance.component.ts index e20a8f5001..9d8c116a76 100644 --- a/src/app/child-dev-project/children/child-details/grouped-child-attendance/grouped-child-attendance.component.ts +++ b/src/app/child-dev-project/children/child-details/grouped-child-attendance/grouped-child-attendance.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit, ViewEncapsulation } from "@angular/core"; -import { Child } from "../../model/child"; +import { Entity } from "../../../../core/entity/model/entity"; import { AttendanceService } from "../../../attendance/attendance.service"; import { RecurringActivity } from "../../../attendance/model/recurring-activity"; import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; @@ -26,7 +26,7 @@ import { ActivityAttendanceSectionComponent } from "../../../attendance/activity standalone: true, }) export class GroupedChildAttendanceComponent implements OnInit { - @Input() entity: Child = new Child(""); + @Input() entity: Entity; loading: boolean = true; activities: RecurringActivity[] = []; diff --git a/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.spec.ts b/src/app/child-dev-project/children/child-school-overview/child-school-overview.component.spec.ts similarity index 84% rename from src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.spec.ts rename to src/app/child-dev-project/children/child-school-overview/child-school-overview.component.spec.ts index 290095f548..a2d3488890 100644 --- a/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.spec.ts +++ b/src/app/child-dev-project/children/child-school-overview/child-school-overview.component.spec.ts @@ -9,10 +9,9 @@ import { import { ChildSchoolOverviewComponent } from "./child-school-overview.component"; import moment from "moment"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; -import { School } from "../model/school"; -import { ChildrenService } from "../../children/children.service"; -import { Child } from "../../children/model/child"; -import { ChildSchoolRelation } from "../../children/model/childSchoolRelation"; +import { ChildrenService } from "../children.service"; +import { ChildSchoolRelation } from "../model/childSchoolRelation"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; describe("ChildSchoolOverviewComponent", () => { let component: ChildSchoolOverviewComponent; @@ -39,7 +38,7 @@ describe("ChildSchoolOverviewComponent", () => { }); it("it calls children service with id from passed child", fakeAsync(() => { - component.entity = new Child(); + component.entity = createEntityOfType("Child"); fixture.detectChanges(); tick(); @@ -50,7 +49,7 @@ describe("ChildSchoolOverviewComponent", () => { })); it("it detects mode and uses correct index to load data ", fakeAsync(() => { - const testSchool = new School(); + const testSchool = createEntityOfType("School"); component.entity = testSchool; fixture.detectChanges(); @@ -63,7 +62,7 @@ describe("ChildSchoolOverviewComponent", () => { })); it("should create a relation with the child ID", fakeAsync(() => { - const child = new Child(); + const child = createEntityOfType("Child"); const existingRelation = new ChildSchoolRelation(); existingRelation.childId = child.getId(); existingRelation.start = moment().subtract(1, "year").toDate(); @@ -85,7 +84,8 @@ describe("ChildSchoolOverviewComponent", () => { })); it("should create a relation with the school ID", fakeAsync(() => { - component.entity = new School("testID"); + component.entity = createEntityOfType("School", "testID"); + //component.entity.getSchema().get; fixture.detectChanges(); tick(); @@ -96,7 +96,7 @@ describe("ChildSchoolOverviewComponent", () => { })); it("should show archived school in 'child' mode", fakeAsync(() => { - component.entity = new Child(); + component.entity = createEntityOfType("Child"); fixture.detectChanges(); tick(); @@ -105,7 +105,7 @@ describe("ChildSchoolOverviewComponent", () => { })); it("should not show archived children in 'school' mode", fakeAsync(() => { - component.entity = new School(); + component.entity = createEntityOfType("School"); fixture.detectChanges(); tick(); diff --git a/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.ts b/src/app/child-dev-project/children/child-school-overview/child-school-overview.component.ts similarity index 91% rename from src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.ts rename to src/app/child-dev-project/children/child-school-overview/child-school-overview.component.ts index 630aec3b10..7c90473929 100644 --- a/src/app/child-dev-project/schools/child-school-overview/child-school-overview.component.ts +++ b/src/app/child-dev-project/children/child-school-overview/child-school-overview.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from "@angular/core"; import { DynamicComponent } from "../../../core/config/dynamic-components/dynamic-component.decorator"; -import { ChildSchoolRelation } from "../../children/model/childSchoolRelation"; -import { ChildrenService } from "../../children/children.service"; +import { ChildSchoolRelation } from "../model/childSchoolRelation"; +import { ChildrenService } from "../children.service"; import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; import { MatSlideToggleModule } from "@angular/material/slide-toggle"; import { FormsModule } from "@angular/forms"; @@ -42,7 +42,7 @@ export class ChildSchoolOverviewComponent implements OnInit { mode: "child" | "school" = "child"; - entityCtr = ChildSchoolRelation; + override entityCtr = ChildSchoolRelation; constructor( private childrenService: ChildrenService, @@ -51,7 +51,13 @@ export class ChildSchoolOverviewComponent screenWidthObserver: ScreenWidthObserver, filterService: FilterService, ) { - super(entityMapper, entityRegistry, screenWidthObserver, filterService); + super( + entityMapper, + entityRegistry, + screenWidthObserver, + filterService, + null, + ); this.columns = [ { id: "childId" }, // schoolId/childId replaced dynamically during init diff --git a/src/app/child-dev-project/children/children-components.ts b/src/app/child-dev-project/children/children-components.ts index ae1382e88f..8a7cca13b8 100644 --- a/src/app/child-dev-project/children/children-components.ts +++ b/src/app/child-dev-project/children/children-components.ts @@ -1,14 +1,6 @@ import { ComponentTuple } from "../../dynamic-components"; export const childrenComponents: ComponentTuple[] = [ - [ - "ChildrenList", - () => - import("./children-list/children-list.component").then( - (c) => c.ChildrenListComponent, - ), - ], - [ "GroupedChildAttendance", () => @@ -20,7 +12,7 @@ export const childrenComponents: ComponentTuple[] = [ "RecentAttendanceBlocks", () => import( - "./children-list/recent-attendance-blocks/recent-attendance-blocks.component" + "../attendance/recent-attendance-blocks/recent-attendance-blocks.component" ).then((c) => c.RecentAttendanceBlocksComponent), ], [ @@ -30,4 +22,33 @@ export const childrenComponents: ComponentTuple[] = [ (c) => c.ChildBlockComponent, ), ], + + [ + "PreviousSchools", + () => + import("./child-school-overview/child-school-overview.component").then( + (c) => c.ChildSchoolOverviewComponent, + ), + ], + [ + "ChildrenOverview", + () => + import("./child-school-overview/child-school-overview.component").then( + (c) => c.ChildSchoolOverviewComponent, + ), + ], + [ + "ChildSchoolOverview", + () => + import("./child-school-overview/child-school-overview.component").then( + (c) => c.ChildSchoolOverviewComponent, + ), + ], + [ + "DisplayParticipantsCount", + () => + import( + "./display-participants-count/display-participants-count.component" + ).then((c) => c.DisplayParticipantsCountComponent), + ], ]; diff --git a/src/app/child-dev-project/children/children-list/children-list.component.spec.ts b/src/app/child-dev-project/children/children-list/children-list.component.spec.ts deleted file mode 100644 index ca6d940619..0000000000 --- a/src/app/child-dev-project/children/children-list/children-list.component.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; -import { ChildrenListComponent } from "./children-list.component"; -import { ChildrenService } from "../children.service"; -import { of } from "rxjs"; -import { ActivatedRoute, Router } from "@angular/router"; -import { Child } from "../model/child"; -import { - BooleanFilterConfig, - EntityListConfig, -} from "../../../core/entity-list/EntityListConfig"; -import { MockedTestingModule } from "../../../utils/mocked-testing.module"; -import { DownloadService } from "../../../core/export/download-service/download.service"; - -describe("ChildrenListComponent", () => { - let component: ChildrenListComponent; - let fixture: ComponentFixture; - const routeData: EntityListConfig = { - title: "Children List", - columns: [ - { viewComponent: "DisplayText", label: "PN", id: "projectNumber" }, - { viewComponent: "ChildBlock", label: "Name", id: "name" }, - { viewComponent: "DisplayDate", label: "DoB", id: "dateOfBirth" }, - { viewComponent: "DisplayText", label: "Gender", id: "gender" }, - { viewComponent: "DisplayText", label: "Class", id: "schoolClass" }, - { viewComponent: "DisplayText", label: "School", id: "schoolId" }, - { - viewComponent: "RecentAttendanceBlocks", - label: "Attendance (School)", - id: "school", - }, - ], - columnGroups: { - default: "Basic Info", - mobile: "School Info", - groups: [ - { - name: "Basic Info", - columns: ["projectNumber", "name", "dateOfBirth"], - }, - { - name: "School Info", - columns: ["name", "schoolClass", "schoolId", "school"], - }, - ], - }, - filters: [ - { - id: "isActive", - type: "boolean", - default: "true", - true: "Currently active children", - false: "Currently inactive children", - } as BooleanFilterConfig, - { - id: "center", - }, - ], - }; - const routeMock = { - data: of({ config: routeData }), - queryParams: of({}), - snapshot: { - queryParamMap: { - get: () => "", - }, - queryParams: {}, - }, - }; - const mockChildrenService: jasmine.SpyObj = - jasmine.createSpyObj(["getChildren"]); - - beforeEach(waitForAsync(() => { - mockChildrenService.getChildren.and.resolveTo([]); - TestBed.configureTestingModule({ - imports: [ChildrenListComponent, MockedTestingModule.withState()], - providers: [ - { - provide: ChildrenService, - useValue: mockChildrenService, - }, - { provide: ActivatedRoute, useValue: routeMock }, - { provide: DownloadService, useValue: {} }, - ], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ChildrenListComponent); - component = fixture.componentInstance; - const router = fixture.debugElement.injector.get(Router); - fixture.ngZone.run(() => router.initialNavigation()); - fixture.detectChanges(); - }); - - it("should create", () => { - expect(component).toBeTruthy(); - }); - - it("should load children on init", async () => { - const child1 = new Child("c1"); - const child2 = new Child("c2"); - mockChildrenService.getChildren.and.resolveTo([child1, child2]); - await component.ngOnChanges({}); - - expect(mockChildrenService.getChildren).toHaveBeenCalled(); - expect(component.allEntities).toEqual([child1, child2]); - }); -}); diff --git a/src/app/child-dev-project/children/children-list/children-list.component.ts b/src/app/child-dev-project/children/children-list/children-list.component.ts deleted file mode 100644 index e61c947d10..0000000000 --- a/src/app/child-dev-project/children/children-list/children-list.component.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Component } from "@angular/core"; -import { Child } from "../model/child"; -import { ActivatedRoute, Router, RouterLink } from "@angular/router"; -import { ChildrenService } from "../children.service"; -import { EntityListComponent } from "../../../core/entity-list/entity-list/entity-list.component"; -import { RouteTarget } from "../../../route-target"; -import { ScreenWidthObserver } from "../../../utils/media/screen-size-observer.service"; -import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; -import { EntityRegistry } from "../../../core/entity/database-entity.decorator"; -import { MatDialog } from "@angular/material/dialog"; -import { DuplicateRecordService } from "../../../core/entity-list/duplicate-records/duplicate-records.service"; -import { EntityActionsService } from "../../../core/entity/entity-actions/entity-actions.service"; -import { - AsyncPipe, - NgForOf, - NgIf, - NgStyle, - NgTemplateOutlet, -} from "@angular/common"; -import { MatButtonModule } from "@angular/material/button"; -import { Angulartics2OnModule } from "angulartics2"; -import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; -import { MatMenuModule } from "@angular/material/menu"; -import { MatTabsModule } from "@angular/material/tabs"; -import { MatFormFieldModule } from "@angular/material/form-field"; -import { MatInputModule } from "@angular/material/input"; -import { EntitiesTableComponent } from "../../../core/common-components/entities-table/entities-table.component"; -import { FormsModule } from "@angular/forms"; -import { FilterComponent } from "../../../core/filter/filter/filter.component"; -import { TabStateModule } from "../../../utils/tab-state/tab-state.module"; -import { ViewTitleComponent } from "../../../core/common-components/view-title/view-title.component"; -import { ExportDataDirective } from "../../../core/export/export-data-directive/export-data.directive"; -import { DisableEntityOperationDirective } from "../../../core/permissions/permission-directive/disable-entity-operation.directive"; -import { MatTooltipModule } from "@angular/material/tooltip"; -import { EntityCreateButtonComponent } from "../../../core/common-components/entity-create-button/entity-create-button.component"; -import { AbilityModule } from "@casl/angular"; -import { EntityActionsMenuComponent } from "../../../core/entity-details/entity-actions-menu/entity-actions-menu.component"; -import { ViewActionsComponent } from "../../../core/common-components/view-actions/view-actions.component"; - -@RouteTarget("ChildrenList") -@Component({ - selector: "app-children-list", - templateUrl: - "../../../core/entity-list/entity-list/entity-list.component.html", - styleUrls: [ - "../../../core/entity-list/entity-list/entity-list.component.scss", - ], - standalone: true, - - imports: [ - NgIf, - NgStyle, - MatButtonModule, - Angulartics2OnModule, - FontAwesomeModule, - MatMenuModule, - NgTemplateOutlet, - MatTabsModule, - NgForOf, - MatFormFieldModule, - MatInputModule, - EntitiesTableComponent, - FormsModule, - FilterComponent, - TabStateModule, - ViewTitleComponent, - ExportDataDirective, - DisableEntityOperationDirective, - RouterLink, - MatTooltipModule, - EntityCreateButtonComponent, - AbilityModule, - AsyncPipe, - EntityActionsMenuComponent, - ViewActionsComponent, - ], -}) -export class ChildrenListComponent extends EntityListComponent { - override entityConstructor = Child; - - constructor( - screenWidthObserver: ScreenWidthObserver, - router: Router, - activatedRoute: ActivatedRoute, - entityMapperService: EntityMapperService, - entities: EntityRegistry, - dialog: MatDialog, - duplicateRecord: DuplicateRecordService, - entityActionsService: EntityActionsService, - private childrenService: ChildrenService, - ) { - super( - screenWidthObserver, - router, - activatedRoute, - entityMapperService, - entities, - dialog, - duplicateRecord, - entityActionsService, - ); - } - - override async getEntities() { - return this.childrenService.getChildren(); - } -} diff --git a/src/app/child-dev-project/children/children.module.ts b/src/app/child-dev-project/children/children.module.ts index 1852887fa8..96f8712999 100644 --- a/src/app/child-dev-project/children/children.module.ts +++ b/src/app/child-dev-project/children/children.module.ts @@ -18,21 +18,11 @@ import { NgModule } from "@angular/core"; import { ComponentRegistry } from "../../dynamic-components"; import { childrenComponents } from "./children-components"; -import { Aser } from "./aser/model/aser"; -import { EducationalMaterial } from "./educational-material/model/educational-material"; -import { HealthCheck } from "./health-checkup/model/health-check"; -import { Child } from "./model/child"; import { ChildSchoolRelation } from "./model/childSchoolRelation"; @NgModule({}) export class ChildrenModule { - static databaseEntities = [ - Aser, - EducationalMaterial, - HealthCheck, - Child, - ChildSchoolRelation, - ]; + static databaseEntities = [ChildSchoolRelation]; constructor(components: ComponentRegistry) { components.addAll(childrenComponents); diff --git a/src/app/child-dev-project/children/children.service.spec.ts b/src/app/child-dev-project/children/children.service.spec.ts index 8ca7d26e21..0db7bd7f20 100644 --- a/src/app/child-dev-project/children/children.service.spec.ts +++ b/src/app/child-dev-project/children/children.service.spec.ts @@ -1,8 +1,6 @@ import { ChildrenService } from "./children.service"; import { EntityMapperService } from "../../core/entity/entity-mapper/entity-mapper.service"; import { ChildSchoolRelation } from "./model/childSchoolRelation"; -import { Child } from "./model/child"; -import { School } from "../schools/model/school"; import { TestBed, waitForAsync } from "@angular/core/testing"; import moment from "moment"; import { Database } from "../../core/database/database"; @@ -14,6 +12,8 @@ import { expectEntitiesToMatch } from "../../utils/expect-entity-data.spec"; import { DateWithAge } from "../../core/basic-datatypes/date-with-age/dateWithAge"; import { AttendanceModule } from "../attendance/attendance.module"; import { EntitySchemaService } from "../../core/entity/schema/entity-schema.service"; +import { createEntityOfType } from "../../core/demo-data/create-entity-of-type"; +import { Entity } from "../../core/entity/model/entity"; describe("ChildrenService", () => { let service: ChildrenService; @@ -43,18 +43,17 @@ describe("ChildrenService", () => { it("should list newly saved children", async () => { const childrenBefore = await service.getChildren(); - const child = new Child("10"); + const child = createEntityOfType("Child", "10"); await entityMapper.save(child); const childrenAfter = await service.getChildren(); - expect(childrenBefore).not.toContain(child); - - expect(childrenAfter).toContain(child); - expect(childrenBefore).toHaveSize(childrenAfter.length - 1); + expect(childrenBefore.some((x) => x.getId() === child.getId())).toBeFalse(); + expect(childrenAfter.some((x) => x.getId() === child.getId())).toBeTrue(); + expect(childrenAfter).toHaveSize(childrenBefore.length + 1); }); it("should find a newly saved child", async () => { - const child = new Child("10"); + const child = createEntityOfType("Child", "10"); try { await service.getChild(child.getId()); fail("Child should not be found"); @@ -64,13 +63,13 @@ describe("ChildrenService", () => { await entityMapper.save(child); const childAfter = await service.getChild(child.getId()); - expect(childAfter).toEqual(child); + expect(childAfter.getId()).toEqual(child.getId()); }); // TODO: test getAttendances it("calculates days since last note for children", async () => { - const allChildren = await entityMapper.loadType(Child); + const allChildren = await entityMapper.loadType("Child"); const c0 = allChildren[0].getId(); await entityMapper.save( @@ -91,7 +90,7 @@ describe("ChildrenService", () => { }); it("calculates days since last note as infinity if above cut-off period for better performance", async () => { - const allChildren = await entityMapper.loadType(Child); + const allChildren = await entityMapper.loadType("Child"); const c0 = allChildren[0].getId(); await entityMapper.save( @@ -99,7 +98,7 @@ describe("ChildrenService", () => { ); const recentNotesMap = await service.getDaysSinceLastNoteOfEachEntity( - Child.ENTITY_TYPE, + "Child", 49, ); @@ -107,7 +106,7 @@ describe("ChildrenService", () => { }); it("should calculate days since last note for other entity types", async () => { - const schools = await entityMapper.loadType(School); + const schools = await entityMapper.loadType("School"); const s1 = schools[0]; const s2 = schools[1]; const n1 = new Note(); @@ -119,9 +118,8 @@ describe("ChildrenService", () => { n2.schools.push(s1.getId()); await entityMapper.saveAll([n1, n2]); - const recentNotesMap = await service.getDaysSinceLastNoteOfEachEntity( - School.ENTITY_TYPE, - ); + const recentNotesMap = + await service.getDaysSinceLastNoteOfEachEntity("School"); expect(recentNotesMap.get(s1.getId())).toBe(2); expect(recentNotesMap.get(s2.getId())).toBe(10); @@ -130,47 +128,47 @@ describe("ChildrenService", () => { it("should load a single child and add school info", async () => { // no active relation const child2 = await service.getChild("Child:2"); - expect(child2.schoolClass).toBeUndefined(); - expect(child2.schoolId).toBeEmpty(); + expect(child2["schoolClass"]).toBeUndefined(); + expect(child2["schoolId"]).toBeEmpty(); // one active relation let child1 = await service.getChild("Child:1"); - expect(child1.schoolClass).toBe("2"); - expect(child1.schoolId).toEqual(["School:1"]); + expect(child1["schoolClass"]).toBe("2"); + expect(child1["schoolId"]).toEqual(["School:1"]); // multiple active relations const newRelation = new ChildSchoolRelation(); newRelation.childId = child1.getId(); newRelation.start = new Date(); - newRelation.schoolId = "School:2"; - newRelation.schoolClass = "3"; + newRelation["schoolId"] = "School:2"; + newRelation["schoolClass"] = "3"; await entityMapper.save(newRelation); child1 = await service.getChild(child1.getId()); - expect(child1.schoolClass).toBe("3"); - expect(child1.schoolId).toEqual(["School:2", "School:1"]); + expect(child1["schoolClass"]).toBe("3"); + expect(child1["schoolId"]).toEqual(["School:2", "School:1"]); // multiple active, no start date on one const noStartDate = new ChildSchoolRelation(); noStartDate.childId = child1.getId(); - noStartDate.schoolId = "School:2"; - noStartDate.schoolClass = "4"; + noStartDate["schoolId"] = "School:2"; + noStartDate["schoolClass"] = "4"; await entityMapper.save(noStartDate); child1 = await service.getChild(child1.getId()); - expect(child1.schoolClass).toBe("4"); - expect(child1.schoolId).toEqual(["School:2", "School:2", "School:1"]); + expect(child1["schoolClass"]).toBe("4"); + expect(child1["schoolId"]).toEqual(["School:2", "School:2", "School:1"]); }); it("should load all children with school info", async () => { const children = await service.getChildren(); const child1 = children.find((child) => child.getId() === "Child:1"); - expect(child1.schoolClass).toBe("2"); - expect(child1.schoolId).toEqual(["School:1"]); + expect(child1["schoolClass"]).toBe("2"); + expect(child1["schoolId"]).toEqual(["School:1"]); const child2 = children.find((child) => child.getId() === "Child:2"); - expect(child2.schoolClass).toBeUndefined(); - expect(child2.schoolId).toBeEmpty(); + expect(child2["schoolClass"]).toBeUndefined(); + expect(child2["schoolId"]).toBeEmpty(); const child3 = children.find((child) => child.getId() === "Child:3"); - expect(child3.schoolClass).toBe("2"); - expect(child3.schoolId).toEqual(["School:1"]); + expect(child3["schoolClass"]).toBe("2"); + expect(child3["schoolId"]).toEqual(["School:1"]); }); it("should get the relations for a child in sorted order", async () => { @@ -198,7 +196,7 @@ describe("ChildrenService", () => { it("should get a active relation which starts today", async () => { const todayRelation = new ChildSchoolRelation("today"); - todayRelation.schoolId = "School:3"; + todayRelation["schoolId"] = "School:3"; todayRelation.start = new Date(); await entityMapper.save(todayRelation); const relations = await service.queryActiveRelationsOf("School:3"); @@ -230,10 +228,10 @@ describe("ChildrenService", () => { }); it("should return related notes", async () => { - const c1 = new Child("c1"); - const c2 = new Child("c2"); - const s1 = new School("s1"); - const s2 = new School("s2"); + const c1 = createEntityOfType("Child", "c1"); + const c2 = createEntityOfType("Child", "c2"); + const s1 = createEntityOfType("School", "s1"); + const s2 = createEntityOfType("School", "s2"); const n1 = new Note("n1"); n1.addChild(c1); n1.addChild(c2); @@ -255,8 +253,8 @@ describe("ChildrenService", () => { }); it("should include related notes through children and schools links (legacy)", async () => { - const c1 = new Child("c1"); - const s1 = new School("s1"); + const c1 = createEntityOfType("Child", "c1"); + const s1 = createEntityOfType("School", "s1"); const n1 = new Note("n1"); n1.children.push(c1.getId()); n1.relatedEntities.push(c1.getId()); @@ -287,10 +285,10 @@ describe("ChildrenService", () => { }); }); -function generateChildEntities(): Child[] { +function generateChildEntities(): Entity[] { const data = []; - const a1 = new Child("1"); + const a1 = createEntityOfType("Child", "1"); a1.name = "Arjun A."; a1.projectNumber = "1"; a1["religion"] = "Hindu"; @@ -300,7 +298,7 @@ function generateChildEntities(): Child[] { a1.center = { id: "delhi", label: "Delhi" }; data.push(a1); - const a2 = new Child("2"); + const a2 = createEntityOfType("Child", "2"); a2.name = "Bandana B."; a2.projectNumber = "2"; a2["religion"] = "Hindu"; @@ -310,7 +308,7 @@ function generateChildEntities(): Child[] { a2.center = { id: "kolkata", label: "Kolkata" }; data.push(a2); - const a3 = new Child("3"); + const a3 = createEntityOfType("Child", "3"); a3.name = "Chandan C."; a3.projectNumber = "3"; a3["religion"] = "Hindu"; @@ -323,14 +321,14 @@ function generateChildEntities(): Child[] { return data; } -function generateSchoolEntities(): School[] { +function generateSchoolEntities(): Entity[] { const data = []; - const s1 = new School("1"); + const s1 = createEntityOfType("School", "1"); s1.name = "People's Primary"; data.push(s1); - const s2 = new School("2"); + const s2 = createEntityOfType("School", "2"); s2.name = "Hope High School"; data.push(s2); @@ -341,32 +339,32 @@ function generateChildSchoolRelationEntities(): ChildSchoolRelation[] { const data: ChildSchoolRelation[] = []; const rel1: ChildSchoolRelation = new ChildSchoolRelation("1"); rel1.childId = "Child:1"; - rel1.schoolId = "School:1"; + rel1["schoolId"] = "School:1"; rel1.start = new Date("2016-10-01"); - rel1.schoolClass = "2"; + rel1["schoolClass"] = "2"; data.push(rel1); const rel4: ChildSchoolRelation = new ChildSchoolRelation("2"); rel4.childId = "Child:3"; - rel4.schoolId = "School:2"; + rel4["schoolId"] = "School:2"; rel4.start = new Date("2001-01-01"); rel4.end = new Date("2002-01-01"); - rel4.schoolClass = "1"; + rel4["schoolClass"] = "1"; data.push(rel4); const rel2: ChildSchoolRelation = new ChildSchoolRelation("3"); rel2.childId = "Child:2"; - rel2.schoolId = "School:2"; + rel2["schoolId"] = "School:2"; rel2.start = new Date("2018-05-07"); rel2.end = new Date("2018-05-09"); - rel2.schoolClass = "3"; + rel2["schoolClass"] = "3"; data.push(rel2); const rel3: ChildSchoolRelation = new ChildSchoolRelation("4"); rel3.childId = "Child:3"; - rel3.schoolId = "School:1"; + rel3["schoolId"] = "School:1"; rel3.start = new Date("2010-01-01"); - rel3.schoolClass = "2"; + rel3["schoolClass"] = "2"; data.push(rel3); return data; diff --git a/src/app/child-dev-project/children/children.service.ts b/src/app/child-dev-project/children/children.service.ts index 08b9c9a445..b97e355ba7 100644 --- a/src/app/child-dev-project/children/children.service.ts +++ b/src/app/child-dev-project/children/children.service.ts @@ -1,13 +1,10 @@ import { Injectable } from "@angular/core"; -import { Child } from "./model/child"; import { EntityMapperService } from "../../core/entity/entity-mapper/entity-mapper.service"; import { Note } from "../notes/model/note"; import { ChildSchoolRelation } from "./model/childSchoolRelation"; import moment, { Moment } from "moment"; import { DatabaseIndexingService } from "../../core/entity/database-indexing/database-indexing.service"; import { Entity } from "../../core/entity/model/entity"; -import { School } from "../schools/model/school"; -import { User } from "../../core/user/user"; import { groupBy } from "../../utils/utils"; @Injectable({ providedIn: "root" }) @@ -27,8 +24,8 @@ export class ChildrenService { /** * returns a list of children with additional school info */ - async getChildren(): Promise { - const children = await this.entityMapper.loadType(Child); + async getChildren(): Promise { + const children = await this.entityMapper.loadType("Child"); const relations = await this.entityMapper.loadType(ChildSchoolRelation); groupBy(relations, "childId").forEach(([id, rels]) => { const child = children.find((c) => c.getId() === id); @@ -43,21 +40,21 @@ export class ChildrenService { * returns a child with additional school info * @param id id of child */ - async getChild(id: string): Promise { - const child = await this.entityMapper.load(Child, id); + async getChild(id: string): Promise { + const child = await this.entityMapper.load("Child", id); const relations = await this.queryRelations(id); this.extendChildWithSchoolInfo(child, relations); return child; } private extendChildWithSchoolInfo( - child: Child, + child: Entity, relations: ChildSchoolRelation[], ) { const active = relations.filter((r) => r.isActive); - child.schoolId = active.map((r) => r.schoolId); + child["schoolId"] = active.map((r) => r.schoolId); if (active.length > 0) { - child.schoolClass = active[0].schoolClass; + child["schoolClass"] = active[0]["schoolClass"]; } } @@ -132,13 +129,14 @@ export class ChildrenService { } private inferNoteLinkPropertyFromEntityType(entityId: string): string { + // TODO: rework this to check the entity schema and find the relevant field? const entityType = Entity.extractTypeFromId(entityId); switch (entityType) { - case Child.ENTITY_TYPE: + case "Child": return "children"; - case School.ENTITY_TYPE: + case "School": return "schools"; - case User.ENTITY_TYPE: + case "User": return "authors"; } } @@ -154,7 +152,7 @@ export class ChildrenService { * For performance reasons the days since last note are set to infinity when larger then the forLastNDays parameter */ public async getDaysSinceLastNoteOfEachEntity( - entityType = Child.ENTITY_TYPE, + entityType = "Child", forLastNDays: number = 30, ): Promise> { const startDay = moment().subtract(forLastNDays, "days"); diff --git a/src/app/child-dev-project/children/aser/demo-aser-generator.service.ts b/src/app/child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service.ts similarity index 70% rename from src/app/child-dev-project/children/aser/demo-aser-generator.service.ts rename to src/app/child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service.ts index d8d73844ee..c627814040 100644 --- a/src/app/child-dev-project/children/aser/demo-aser-generator.service.ts +++ b/src/app/child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service.ts @@ -1,18 +1,19 @@ -import { DemoChildGenerator } from "../demo-data-generators/demo-child-generator.service"; -import { DemoDataGenerator } from "../../../core/demo-data/demo-data-generator"; +import { DemoChildGenerator } from "../demo-child-generator.service"; +import { DemoDataGenerator } from "../../../../core/demo-data/demo-data-generator"; import { Injectable } from "@angular/core"; -import { Child } from "../model/child"; -import { faker } from "../../../core/demo-data/faker"; -import { Aser } from "./model/aser"; -import { mathLevels, readingLevels } from "./model/skill-levels"; -import { WarningLevel } from "../../warning-level"; +import { faker } from "../../../../core/demo-data/faker"; +import { mathLevels, readingLevels } from "./skill-levels"; +import { WarningLevel } from "../../../warning-level"; +import { Entity } from "../../../../core/entity/model/entity"; +import { createEntityOfType } from "../../../../core/demo-data/create-entity-of-type"; +import { ConfigurableEnumValue } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; /** * Generate ASER results every 12 months for each Child until passing. * Builds upon the generated demo Child entities. */ @Injectable() -export class DemoAserGeneratorService extends DemoDataGenerator { +export class DemoAserGeneratorService extends DemoDataGenerator { /** * This function returns a provider object to be used in an Angular Module configuration: * `providers: [DemoAserGeneratorService.provider()]` @@ -27,7 +28,7 @@ export class DemoAserGeneratorService extends DemoDataGenerator { super(); } - public generateEntities(): Aser[] { + public generateEntities(): Entity[] { const data = []; for (const child of this.demoChildren.entities) { @@ -37,35 +38,35 @@ export class DemoAserGeneratorService extends DemoDataGenerator { return data; } - private generateAserResultsForChild(child: Child): Aser[] { + private generateAserResultsForChild(child: Entity): Entity[] { const data = []; - let date = new Date(child.admissionDate.getTime()); - let previousResult = new Aser(""); + let date = new Date(child["admissionDate"].getTime()); + let previousResult = createEntityOfType("Aser"); const firstLanguage = child["motherTongue"].toLowerCase(); do { - const aserResult = new Aser(); + const aserResult = createEntityOfType("Aser"); aserResult.child = child.getId(); aserResult.date = date; aserResult.math = this.selectNextSkillLevel( mathLevels.slice(1), previousResult.math, - ); + ).id; aserResult.english = this.selectNextSkillLevel( readingLevels.slice(1), previousResult.english, - ); + ).id; aserResult[firstLanguage] = this.selectNextSkillLevel( readingLevels.slice(1), previousResult[firstLanguage], - ); + ).id; data.push(aserResult); date = new Date(date.getFullYear() + 1, 2, 1); previousResult = aserResult; } while ( - date < faker.getEarlierDateOrToday(child.dropoutDate) && + date < faker.getEarlierDateOrToday(child["dropoutDate"]) && previousResult.getWarningLevel() !== WarningLevel.OK ); @@ -77,7 +78,10 @@ export class DemoAserGeneratorService extends DemoDataGenerator { * @param skillRange The array of skill levels for the desired subject (mathLevels or readingLevels) * @param previousSkillLevel The string indicating the level from the previous test for this subject */ - private selectNextSkillLevel(skillRange: T[], previousSkillLevel: T): T { + private selectNextSkillLevel( + skillRange: T[], + previousSkillLevel: T, + ): T { const previousSkillLevelIndex = skillRange.indexOf(previousSkillLevel); let nextSkillLevelIndex; diff --git a/src/app/child-dev-project/children/aser/model/skill-levels.ts b/src/app/child-dev-project/children/demo-data-generators/aser/skill-levels.ts similarity index 86% rename from src/app/child-dev-project/children/aser/model/skill-levels.ts rename to src/app/child-dev-project/children/demo-data-generators/aser/skill-levels.ts index f57580720e..a412bc6c51 100644 --- a/src/app/child-dev-project/children/aser/model/skill-levels.ts +++ b/src/app/child-dev-project/children/demo-data-generators/aser/skill-levels.ts @@ -12,23 +12,28 @@ export const readingLevels: ConfigurableEnumConfig = { id: "Nothing", label: $localize`:Label reading level:Nothing`, + color: "#fd7272", }, { id: "Read Letters", label: $localize`:Label reading level:Read Letters`, + color: "#ff8153", }, { id: "Read Words", label: $localize`:Label reading level:Read Words`, + color: "#ff982a", }, { id: "Read Sentence", label: $localize`:Label reading level:Read Sentence`, + color: "#d2c92e", }, { id: "Read Paragraph", label: $localize`:Label reading level:Read Paragraph`, passed: true, + color: "#90ee90", }, ]); @@ -38,22 +43,27 @@ export const mathLevels: ConfigurableEnumConfig = { id: "Nothing", label: $localize`:Label math level:Nothing`, + color: "#fd7272", }, { id: "Numbers 1-9", label: $localize`:Label math level:Numbers 1-9`, + color: "#ff8153", }, { id: "Numbers 10-99", label: $localize`:Label math level:Numbers 10-99`, + color: "#ff982a", }, { id: "Subtraction", label: $localize`:Label math level:Subtraction`, + color: "#d2c92e", }, { id: "Division", label: $localize`:Label math level:Division`, passed: true, + color: "#c8e6c9", }, ]); diff --git a/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts b/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts index f10e8d1a66..f219e586d6 100644 --- a/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts +++ b/src/app/child-dev-project/children/demo-data-generators/demo-child-generator.service.ts @@ -1,4 +1,4 @@ -import { Child } from "../model/child"; +import { Entity } from "../../../core/entity/model/entity"; import { religions } from "./fixtures/religions"; import { languages } from "./fixtures/languages"; import { dropoutTypes } from "./fixtures/dropout-types"; @@ -9,13 +9,14 @@ import { centersWithProbability } from "./fixtures/centers"; import { genders } from "../model/genders"; import { calculateAge } from "../../../utils/utils"; import { DateWithAge } from "../../../core/basic-datatypes/date-with-age/dateWithAge"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; export class DemoChildConfig { count: number; } @Injectable() -export class DemoChildGenerator extends DemoDataGenerator { +export class DemoChildGenerator extends DemoDataGenerator { static count: number; /** @@ -31,14 +32,14 @@ export class DemoChildGenerator extends DemoDataGenerator { } static generateEntity(id: string) { - const child = new Child(id); + const child = createEntityOfType("Child", id); child.name = faker.person.firstName() + " " + faker.person.lastName(); child.projectNumber = id; - child["religion"] = faker.helpers.arrayElement(religions); - child.gender = faker.helpers.arrayElement(genders.slice(1)); + child.religion = faker.helpers.arrayElement(religions); + child.gender = faker.helpers.arrayElement(genders.slice(1)).id; child.dateOfBirth = new DateWithAge(faker.dateOfBirth(5, 20)); - child["motherTongue"] = faker.helpers.arrayElement(languages); - child.center = faker.helpers.arrayElement(centersWithProbability); + child.motherTongue = faker.helpers.arrayElement(languages); + child.center = faker.helpers.arrayElement(centersWithProbability).id; child.phone = "+" + faker.number.int({ min: 10, max: 99 }) + @@ -57,7 +58,7 @@ export class DemoChildGenerator extends DemoDataGenerator { return child; } - private static makeChildDropout(child: Child) { + private static makeChildDropout(child: Entity & { [key: string]: any }) { child.dropoutDate = faker.date.between({ from: child.admissionDate, to: new Date(), @@ -65,13 +66,14 @@ export class DemoChildGenerator extends DemoDataGenerator { child.dropoutRemarks = faker.lorem.sentence(); child.dropoutType = faker.helpers.arrayElement(dropoutTypes); child.status = $localize`:Child status:Dropout`; + child.inactive = true; } constructor(public config: DemoChildConfig) { super(); } - generateEntities(): Child[] { + generateEntities(): Entity[] { const data = []; for (let i = 1; i <= this.config.count; i++) { data.push(DemoChildGenerator.generateEntity(String(i))); diff --git a/src/app/child-dev-project/children/demo-data-generators/demo-child-school-relation-generator.service.ts b/src/app/child-dev-project/children/demo-data-generators/demo-child-school-relation-generator.service.ts index d982efe11e..264067066b 100644 --- a/src/app/child-dev-project/children/demo-data-generators/demo-child-school-relation-generator.service.ts +++ b/src/app/child-dev-project/children/demo-data-generators/demo-child-school-relation-generator.service.ts @@ -1,11 +1,11 @@ import { DemoChildGenerator } from "./demo-child-generator.service"; -import { DemoSchoolGenerator } from "../../schools/demo-school-generator.service"; +import { DemoSchoolGenerator } from "./demo-school-generator.service"; import { DemoDataGenerator } from "../../../core/demo-data/demo-data-generator"; import { Injectable } from "@angular/core"; -import { Child } from "../model/child"; import { ChildSchoolRelation } from "../model/childSchoolRelation"; import { faker } from "../../../core/demo-data/faker"; -import { School } from "../../schools/model/school"; +import { Entity } from "../../../core/entity/model/entity"; +import { EntityRegistry } from "../../../core/entity/database-entity.decorator"; /** * Generate ChildSchoolRelation entities linking a child to a school for a specific year. @@ -30,6 +30,7 @@ export class DemoChildSchoolRelationGenerator extends DemoDataGenerator { +export class DemoSchoolGenerator extends DemoDataGenerator { /** * This function returns a provider object to be used in an Angular Module configuration: * `providers: [DemoSchoolGenerator.provider({count: 10})]` @@ -28,11 +29,11 @@ export class DemoSchoolGenerator extends DemoDataGenerator { super(); } - generateEntities(): School[] { + generateEntities(): Entity[] { const data = []; for (let i = 1; i <= this.config.count; i++) { - const school = new School(String(i)); + const school = createEntityOfType("School", String(i)); school["language"] = faker.helpers.arrayElement([ $localize`:Language of a school:Hindi`, $localize`:Language of a school:English`, @@ -43,7 +44,7 @@ export class DemoSchoolGenerator extends DemoDataGenerator { )}`; const schoolNameWithLanguage = $localize`${faker.person.firstName()} ${ school["language"] - } Language`; + } Medium`; school.name = faker.helpers.arrayElement([ schoolNameWithType, schoolNameWithLanguage, diff --git a/src/app/child-dev-project/children/educational-material/demo-educational-material-generator.service.ts b/src/app/child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service.ts similarity index 67% rename from src/app/child-dev-project/children/educational-material/demo-educational-material-generator.service.ts rename to src/app/child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service.ts index 1f1498e9cb..1c7edb6fd9 100644 --- a/src/app/child-dev-project/children/educational-material/demo-educational-material-generator.service.ts +++ b/src/app/child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service.ts @@ -1,10 +1,10 @@ -import { DemoChildGenerator } from "../demo-data-generators/demo-child-generator.service"; -import { DemoDataGenerator } from "../../../core/demo-data/demo-data-generator"; +import { DemoChildGenerator } from "../demo-child-generator.service"; +import { DemoDataGenerator } from "../../../../core/demo-data/demo-data-generator"; import { Injectable } from "@angular/core"; -import { Child } from "../model/child"; -import { faker } from "../../../core/demo-data/faker"; -import { EducationalMaterial } from "./model/educational-material"; -import { materials } from "./model/materials"; +import { faker } from "../../../../core/demo-data/faker"; +import { materials } from "./materials"; +import { Entity } from "../../../../core/entity/model/entity"; +import { createEntityOfType } from "../../../../core/demo-data/create-entity-of-type"; export class DemoEducationMaterialConfig { minCount: number; @@ -16,7 +16,7 @@ export class DemoEducationMaterialConfig { * Builds upon the generated demo Child entities. */ @Injectable() -export class DemoEducationalMaterialGeneratorService extends DemoDataGenerator { +export class DemoEducationalMaterialGeneratorService extends DemoDataGenerator { /** * This function returns a provider object to be used in an Angular Module configuration: * `providers: [DemoEducationalMaterialGeneratorService.provider()]` @@ -38,7 +38,7 @@ export class DemoEducationalMaterialGeneratorService extends DemoDataGenerator material.hasOwnProperty("color")), - ); + ).id; specialMaterial.materialAmount = 1; data.push(specialMaterial); } @@ -61,16 +61,18 @@ export class DemoEducationalMaterialGeneratorService extends DemoDataGenerator { +export class DemoHealthCheckGeneratorService extends DemoDataGenerator { /** * This function returns a provider object to be used in an Angular Module configuration: * `providers: [DemoHealthCheckGeneratorService.provider()]` @@ -29,7 +29,7 @@ export class DemoHealthCheckGeneratorService extends DemoDataGenerator { +export class DemoHistoricalDataGenerator extends DemoDataGenerator { static provider(config: DemoHistoricalDataConfig) { return [ { @@ -33,29 +34,29 @@ export class DemoHistoricalDataGenerator extends DemoDataGenerator { - const historicalData = new HistoricalEntityData(); - historicalData.date = faker.date.past(); - historicalData.relatedEntity = child.getId(); + const historicalData = createEntityOfType("HistoricalEntityData"); for (const attribute of attributes) { - historicalData[attribute] = faker.helpers.arrayElement(ratingAnswers); + historicalData[attribute] = + faker.helpers.arrayElement(ratingAnswers).id; } + historicalData.date = faker.date.past(); + historicalData.relatedEntity = child.getId(); return historicalData; }); entities.push(...historicalDataOfChild); diff --git a/src/app/features/historical-data/model/rating-answers.ts b/src/app/child-dev-project/children/demo-data-generators/observations/rating-answers.ts similarity index 85% rename from src/app/features/historical-data/model/rating-answers.ts rename to src/app/child-dev-project/children/demo-data-generators/observations/rating-answers.ts index 61f8472b38..fcec0cb1f9 100644 --- a/src/app/features/historical-data/model/rating-answers.ts +++ b/src/app/child-dev-project/children/demo-data-generators/observations/rating-answers.ts @@ -1,4 +1,4 @@ -import { Ordering } from "../../../core/basic-datatypes/configurable-enum/configurable-enum-ordering"; +import { Ordering } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum-ordering"; export const ratingAnswers = Ordering.imposeTotalOrdering([ { diff --git a/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.html b/src/app/child-dev-project/children/display-participants-count/display-participants-count.component.html similarity index 100% rename from src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.html rename to src/app/child-dev-project/children/display-participants-count/display-participants-count.component.html diff --git a/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.spec.ts b/src/app/child-dev-project/children/display-participants-count/display-participants-count.component.spec.ts similarity index 89% rename from src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.spec.ts rename to src/app/child-dev-project/children/display-participants-count/display-participants-count.component.spec.ts index 64d2a15b2f..572ffdb0f2 100644 --- a/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.spec.ts +++ b/src/app/child-dev-project/children/display-participants-count/display-participants-count.component.spec.ts @@ -1,9 +1,9 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { DisplayParticipantsCountComponent } from "./display-participants-count.component"; -import { ChildrenService } from "../../children/children.service"; -import { School } from "../model/school"; -import { ChildSchoolRelation } from "../../children/model/childSchoolRelation"; +import { ChildrenService } from "../children.service"; +import { ChildSchoolRelation } from "../model/childSchoolRelation"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; describe("DisplayParticipantsCountComponent", () => { let component: DisplayParticipantsCountComponent; @@ -30,7 +30,7 @@ describe("DisplayParticipantsCountComponent", () => { fixture = TestBed.createComponent(DisplayParticipantsCountComponent); component = fixture.componentInstance; - component.entity = new School("s-1"); + component.entity = createEntityOfType("School", "s-1"); fixture.detectChanges(); }); diff --git a/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.ts b/src/app/child-dev-project/children/display-participants-count/display-participants-count.component.ts similarity index 89% rename from src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.ts rename to src/app/child-dev-project/children/display-participants-count/display-participants-count.component.ts index 5c7ebb36eb..edf696a8b9 100644 --- a/src/app/child-dev-project/schools/display-participants-count/display-participants-count.component.ts +++ b/src/app/child-dev-project/children/display-participants-count/display-participants-count.component.ts @@ -1,9 +1,9 @@ import { Component, OnChanges, signal, WritableSignal } from "@angular/core"; import { CommonModule } from "@angular/common"; -import { ChildrenService } from "../../children/children.service"; +import { ChildrenService } from "../children.service"; import { ViewDirective } from "../../../core/entity/default-datatype/view.directive"; import { DynamicComponent } from "../../../core/config/dynamic-components/dynamic-component.decorator"; -import { ChildSchoolRelation } from "../../children/model/childSchoolRelation"; +import { ChildSchoolRelation } from "../model/childSchoolRelation"; import { Logging } from "../../../core/logging/logging.service"; @DynamicComponent("DisplayParticipantsCount") diff --git a/src/app/child-dev-project/children/educational-material/model/educational-material.spec.ts b/src/app/child-dev-project/children/educational-material/model/educational-material.spec.ts deleted file mode 100644 index 86b3489bbc..0000000000 --- a/src/app/child-dev-project/children/educational-material/model/educational-material.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { EducationalMaterial } from "./educational-material"; -import { materials } from "./materials"; -import { testEntitySubclass } from "../../../../core/entity/model/entity.spec"; - -describe("EducationalMaterial Entity", () => { - testEntitySubclass("EducationalMaterial", EducationalMaterial, { - _id: "EducationalMaterial:some-id", - _rev: "XYZ", - - child: "1", - date: new Date(), - materialType: materials[1].id, - materialAmount: 2, - description: "bar", - }); -}); diff --git a/src/app/child-dev-project/children/educational-material/model/educational-material.ts b/src/app/child-dev-project/children/educational-material/model/educational-material.ts deleted file mode 100644 index ea03ca55b4..0000000000 --- a/src/app/child-dev-project/children/educational-material/model/educational-material.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { Entity } from "../../../../core/entity/model/entity"; -import { DatabaseEntity } from "../../../../core/entity/database-entity.decorator"; -import { DatabaseField } from "../../../../core/entity/database-field.decorator"; -import { ConfigurableEnumValue } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; -import { EntityDatatype } from "../../../../core/basic-datatypes/entity/entity.datatype"; -import { Child } from "../../model/child"; -import { PLACEHOLDERS } from "../../../../core/entity/schema/entity-schema-field"; - -@DatabaseEntity("EducationalMaterial") -export class EducationalMaterial extends Entity { - static create(params: Partial): EducationalMaterial { - return Object.assign(new EducationalMaterial(), params); - } - - @DatabaseField({ - dataType: EntityDatatype.dataType, - additional: Child.ENTITY_TYPE, - entityReferenceRole: "composite", - }) - child: string; - - @DatabaseField({ - label: $localize`:Date on which the material has been borrowed:Date`, - defaultValue: { - mode: "dynamic", - value: PLACEHOLDERS.NOW, - }, - }) - date: Date; - - @DatabaseField({ - label: $localize`:The material which has been borrowed:Material`, - dataType: "configurable-enum", - additional: "materials", - validators: { - required: true, - }, - }) - materialType: ConfigurableEnumValue; - - @DatabaseField({ - label: $localize`:The amount of the material which has been borrowed:Amount`, - defaultValue: { - mode: "static", - value: 1, - }, - validators: { - required: true, - }, - }) - materialAmount: number; - - @DatabaseField({ - label: $localize`:An additional description for the borrowed material:Description`, - }) - description = ""; - - public getColor() { - return this.materialType?.color || "white"; - } -} diff --git a/src/app/child-dev-project/children/health-checkup/model/health-check.spec.ts b/src/app/child-dev-project/children/health-checkup/model/health-check.spec.ts deleted file mode 100644 index 50552bd66d..0000000000 --- a/src/app/child-dev-project/children/health-checkup/model/health-check.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { HealthCheck } from "./health-check"; -import { testEntitySubclass } from "../../../../core/entity/model/entity.spec"; - -describe("HealthCheck Entity", () => { - testEntitySubclass("HealthCheck", HealthCheck, { - _id: "HealthCheck:some-id", - - child: "1", - date: new Date(), - height: 101, - weight: 32, - }); -}); diff --git a/src/app/child-dev-project/children/health-checkup/model/health-check.ts b/src/app/child-dev-project/children/health-checkup/model/health-check.ts deleted file mode 100644 index 3bcd34ad93..0000000000 --- a/src/app/child-dev-project/children/health-checkup/model/health-check.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { Entity } from "../../../../core/entity/model/entity"; -import { DatabaseEntity } from "../../../../core/entity/database-entity.decorator"; -import { DatabaseField } from "../../../../core/entity/database-field.decorator"; -import { WarningLevel } from "../../../warning-level"; -import { Child } from "../../model/child"; -import { PLACEHOLDERS } from "../../../../core/entity/schema/entity-schema-field"; - -/** - * Model Class for the Health Checks that are taken for a Child. - * It stores the Child's ID in a String and both, the height and weight in cm as a number, and the Date - */ -@DatabaseEntity("HealthCheck") -export class HealthCheck extends Entity { - static override hasPII = true; - - static create(contents: Partial) { - return Object.assign(new HealthCheck(), contents); - } - - @DatabaseField({ - dataType: "entity", - additional: Child.ENTITY_TYPE, - entityReferenceRole: "composite", - anonymize: "retain", - }) - child: string; - - @DatabaseField({ - label: $localize`:Label for date of a health check:Date`, - anonymize: "retain-anonymized", - defaultValue: { - mode: "dynamic", - value: PLACEHOLDERS.NOW, - }, - }) - date: Date; - - /** height measurement in cm **/ - @DatabaseField({ - label: $localize`:Label for height in cm of a health check:Height [cm]`, - viewComponent: "DisplayUnit", - additional: "cm", - }) - height: number; - - /** weight measurement in kg **/ - @DatabaseField({ - label: $localize`:Label for weight in kg of a health check:Weight [kg]`, - viewComponent: "DisplayUnit", - additional: "kg", - }) - weight: number; - - /** - * dynamically calculated BMI value based on the height and weight, rounded to 2 decimal digits - */ - get bmi(): number { - const bmi = this.weight / ((this.height / 100) * (this.height / 100)); - return Math.round(bmi * 100) / 100; - } - - getWarningLevel(): WarningLevel { - if (this.bmi <= 16 || this.bmi >= 30) { - return WarningLevel.URGENT; - } else if (this.bmi >= 18 && this.bmi <= 25) { - return WarningLevel.OK; - } else { - return WarningLevel.WARNING; - } - } - - getColor(): string { - if (!this.height) { - return "#DEDEDE"; - } else { - return super.getColor(); - } - } -} diff --git a/src/app/child-dev-project/children/model/child.spec.ts b/src/app/child-dev-project/children/model/child.spec.ts deleted file mode 100644 index 6f35d39f6f..0000000000 --- a/src/app/child-dev-project/children/model/child.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { Child } from "./child"; -import { genders } from "./genders"; -import { testEntitySubclass } from "../../../core/entity/model/entity.spec"; -import { centersUnique } from "../demo-data-generators/fixtures/centers"; - -describe("Child", () => { - testEntitySubclass("Child", Child, { - _id: "Child:some-id", - - name: "Max", - projectNumber: "projectNumber01", - gender: genders[1].id, - dateOfBirth: "2010-01-01", - - photo: "..", - center: centersUnique[0].id, - admissionDate: new Date("2021-03-1"), - status: "Active", - - dropoutDate: new Date("2022-03-31"), - dropoutType: "unknown", - dropoutRemarks: "no idea what happened", - }); - - it("should determine isActive based on inferred state", () => { - const testEntity1 = new Child(); - expect(testEntity1.isActive).withContext("default").toBeTrue(); - - testEntity1["exit_date"] = new Date(); - expect(testEntity1.isActive).withContext("exit_date").toBeFalse(); - - const testEntity2a = new Child(); - testEntity2a["inactive"] = true; - expect(testEntity2a.isActive).withContext("inactive").toBeFalse(); - - const testEntity2b = new Child(); - testEntity2b["active"] = false; - expect(testEntity2b.isActive).withContext("active").toBeFalse(); - - const testEntity3 = new Child(); - testEntity3["status"] = "Dropout"; - expect(testEntity3.isActive).withContext("Dropout").toBeFalse(); - - // always give "inactive" precedence over other logic (as it is trigger in UI) - const testEntityPrec = new Child(); - testEntityPrec["inactive"] = false; - testEntityPrec["status"] = "Dropout"; - expect(testEntityPrec.isActive) - .withContext("inactive taking precedence") - .toBeTrue(); - }); -}); diff --git a/src/app/child-dev-project/children/model/child.ts b/src/app/child-dev-project/children/model/child.ts deleted file mode 100644 index 36bede16c1..0000000000 --- a/src/app/child-dev-project/children/model/child.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { Entity } from "../../../core/entity/model/entity"; -import { DatabaseEntity } from "../../../core/entity/database-entity.decorator"; -import { DatabaseField } from "../../../core/entity/database-field.decorator"; -import { ConfigurableEnumValue } from "../../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; -import { IconName } from "@fortawesome/fontawesome-svg-core"; -import { DateWithAge } from "../../../core/basic-datatypes/date-with-age/dateWithAge"; - -export type Center = ConfigurableEnumValue; - -@DatabaseEntity("Child") -export class Child extends Entity { - static toStringAttributes = ["name"]; - static icon: IconName = "child"; - static label = $localize`:label for entity:Participant`; - static labelPlural = $localize`:label (plural) for entity:Participants`; - static color = "#1565C0"; - static override hasPII = true; - - static create(name: string): Child { - const instance = new Child(); - instance.name = name; - return instance; - } - - static getBlockComponent(): string { - return "ChildBlock"; - } - - @DatabaseField({ - label: $localize`:Label for the name of a child:Name`, - validators: { - required: true, - }, - }) - name: string; - - @DatabaseField({ - label: $localize`:Label for the project number of a child:Project Number`, - labelShort: $localize`:Short label for the project number:PN`, - searchable: true, - anonymize: "retain", - }) - projectNumber: string; - - @DatabaseField({ - label: $localize`:Label for the date of birth of a child:Date of birth`, - labelShort: $localize`:Short label for the date of birth:DoB`, - anonymize: "retain-anonymized", - }) - dateOfBirth: DateWithAge; - - @DatabaseField({ - dataType: "configurable-enum", - label: $localize`:Label for the gender of a child:Gender`, - additional: "genders", - anonymize: "retain", - }) - gender: ConfigurableEnumValue; - - @DatabaseField({ - dataType: "configurable-enum", - additional: "center", - label: $localize`:Label for the center of a child:Center`, - anonymize: "retain", - }) - center: Center; - - @DatabaseField({ - label: $localize`:Label for the admission date of a child:Admission`, - anonymize: "retain-anonymized", - }) - admissionDate: Date; - - @DatabaseField({ - label: $localize`:Label for the status of a child:Status`, - }) - status: string; - - @DatabaseField({ - label: $localize`:Label for the dropout date of a child:Dropout Date`, - anonymize: "retain-anonymized", - }) - dropoutDate: Date; - - @DatabaseField({ - label: $localize`:Label for the type of dropout of a child:Dropout Type`, - anonymize: "retain", - }) - dropoutType: string; - - @DatabaseField({ - label: $localize`:Label for the remarks about a dropout of a child:Dropout remarks`, - }) - dropoutRemarks: string; - - /** current school (as determined through the ChildSchoolRelation docs) set during loading through ChildrenService */ - schoolId: string[] = []; - /** current class (as determined through the ChildSchoolRelation docs) set during loading through ChildrenService */ - schoolClass: string; - - @DatabaseField({ - dataType: "file", - label: $localize`:Label for the file field of a photo of a child:Photo`, - editComponent: "EditPhoto", - }) - photo: string; - - @DatabaseField({ - label: $localize`:Label for the phone number of a child:Phone Number`, - }) - phone: string; - - get isActive(): boolean { - if (this.inactive !== undefined) { - // explicit property set through UI has to take precedence - return super.isActive; - } - - return ( - this.status !== "Dropout" && - !this["dropoutDate"] && - !this["exit_date"] && - super.isActive - ); - } -} diff --git a/src/app/child-dev-project/children/model/childSchoolRelation.ts b/src/app/child-dev-project/children/model/childSchoolRelation.ts index 4362b98af5..134c783f05 100644 --- a/src/app/child-dev-project/children/model/childSchoolRelation.ts +++ b/src/app/child-dev-project/children/model/childSchoolRelation.ts @@ -1,20 +1,20 @@ import { DatabaseEntity } from "../../../core/entity/database-entity.decorator"; import { DatabaseField } from "../../../core/entity/database-field.decorator"; -import { School } from "../../schools/model/school"; -import { Child } from "./child"; import { TimePeriod } from "../../../core/entity-details/related-time-period-entities/time-period"; /** * Record of a school year that a Child attended a certain class in a School. + * + * This class remains as a stub and in the future will be further refactored + * TODO: refactor into generic time-period based relationship entity --> #2512 */ @DatabaseEntity("ChildSchoolRelation") export class ChildSchoolRelation extends TimePeriod { static override hasPII = true; @DatabaseField({ - label: $localize`:Label for the child of a relation:Child`, dataType: "entity", - additional: Child.ENTITY_TYPE, + additional: "Child", entityReferenceRole: "composite", validators: { required: true, @@ -24,9 +24,8 @@ export class ChildSchoolRelation extends TimePeriod { childId: string; @DatabaseField({ - label: $localize`:Label for the school of a relation:School`, dataType: "entity", - additional: School.ENTITY_TYPE, + additional: "School", entityReferenceRole: "aggregate", validators: { required: true, @@ -35,16 +34,13 @@ export class ChildSchoolRelation extends TimePeriod { }) schoolId: string; - @DatabaseField({ label: $localize`:Label for the class of a relation:Class` }) - schoolClass: string = ""; - @DatabaseField({ dataType: "date-only", label: $localize`:Label for the start date of a relation:Start date`, description: $localize`:Description of the start date of a relation:The date a child joins a school`, anonymize: "retain", }) - start: Date; + declare start: Date; @DatabaseField({ dataType: "date-only", @@ -52,17 +48,5 @@ export class ChildSchoolRelation extends TimePeriod { description: $localize`:Description of the end date of a relation:The date of a child leaving the school`, anonymize: "retain", }) - end: Date; - - /** percentage achieved in the final school exams of that year */ - @DatabaseField({ - label: $localize`:Label for the percentage result of a relation:Result`, - viewComponent: "DisplayPercentage", - editComponent: "EditNumber", - validators: { - min: 0, - max: 100, - }, - }) - result: number; + declare end: Date; } diff --git a/src/app/child-dev-project/notes/dashboard-widgets/important-notes-dashboard/important-notes-dashboard.component.ts b/src/app/child-dev-project/notes/dashboard-widgets/important-notes-dashboard/important-notes-dashboard.component.ts index 54ad4370ea..e66cc4d5e9 100644 --- a/src/app/child-dev-project/notes/dashboard-widgets/important-notes-dashboard/important-notes-dashboard.component.ts +++ b/src/app/child-dev-project/notes/dashboard-widgets/important-notes-dashboard/important-notes-dashboard.component.ts @@ -17,7 +17,7 @@ import { DashboardWidget } from "../../../../core/dashboard/dashboard-widget/das standalone: true, }) export class ImportantNotesDashboardComponent extends DashboardWidget { - static getRequiredEntities() { + static override getRequiredEntities() { return Note.ENTITY_TYPE; } diff --git a/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.spec.ts b/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.spec.ts index 1547930e35..d3214b618b 100644 --- a/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.spec.ts +++ b/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.spec.ts @@ -9,7 +9,13 @@ import { import { ChildrenService } from "../../../children/children.service"; import { NotesDashboardComponent } from "./notes-dashboard.component"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; -import { RecurringActivity } from "../../../attendance/model/recurring-activity"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; +import { EntityRegistry } from "../../../../core/entity/database-entity.decorator"; +import { Entity } from "../../../../core/entity/model/entity"; + +class Child extends Entity { + static override ENTITY_TYPE = "Child"; +} describe("NotesDashboardComponent", () => { let component: NotesDashboardComponent; @@ -29,6 +35,8 @@ describe("NotesDashboardComponent", () => { imports: [NotesDashboardComponent, MockedTestingModule.withState()], providers: [{ provide: ChildrenService, useValue: mockChildrenService }], }).compileComponents(); + + TestBed.inject(EntityRegistry).set("Child", Child); })); describe("with recent notes", () => { @@ -121,7 +129,7 @@ describe("NotesDashboardComponent", () => { mockChildrenService.getDaysSinceLastNoteOfEachEntity.and.resolveTo( new Map(), ); - const entity = RecurringActivity.ENTITY_TYPE; + const entity = TestEntity.ENTITY_TYPE; component.entity = entity; component.mode = "with-recent-notes"; diff --git a/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.ts b/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.ts index 6331422628..d40e19e1e9 100644 --- a/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.ts +++ b/src/app/child-dev-project/notes/dashboard-widgets/notes-dashboard/notes-dashboard.component.ts @@ -3,7 +3,6 @@ import { ChildrenService } from "../../../children/children.service"; import moment from "moment"; import { MatTableModule } from "@angular/material/table"; import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; -import { Child } from "../../../children/model/child"; import { EntityRegistry } from "../../../../core/entity/database-entity.decorator"; import { EntityConstructor } from "../../../../core/entity/model/entity"; import { DecimalPipe, NgIf } from "@angular/common"; @@ -43,7 +42,7 @@ export class NotesDashboardComponent extends DashboardWidget implements OnInit, NotesDashboardConfig { - static getRequiredEntities(config: NotesDashboardConfig) { + static override getRequiredEntities(config: NotesDashboardConfig) { return config?.entity || Note.ENTITY_TYPE; } @@ -54,7 +53,7 @@ export class NotesDashboardComponent this._entity = this.entities.get(value); } - _entity: EntityConstructor = Child; + _entity: EntityConstructor; /** * number of days since last note that entities should be considered having a "recent" note. */ @@ -80,6 +79,10 @@ export class NotesDashboardComponent } ngOnInit() { + if (!this._entity) { + this.entity = "Child"; + } + let dayRangeBoundary = this.sinceDays; if (this.fromBeginningOfWeek) { dayRangeBoundary += moment().diff(moment().startOf("week"), "days"); diff --git a/src/app/child-dev-project/notes/demo-data/demo-note-generator.service.ts b/src/app/child-dev-project/notes/demo-data/demo-note-generator.service.ts index cf88addf54..ff2dcaf67d 100644 --- a/src/app/child-dev-project/notes/demo-data/demo-note-generator.service.ts +++ b/src/app/child-dev-project/notes/demo-data/demo-note-generator.service.ts @@ -1,7 +1,6 @@ import { DemoChildGenerator } from "../../children/demo-data-generators/demo-child-generator.service"; import { DemoDataGenerator } from "../../../core/demo-data/demo-data-generator"; import { Injectable } from "@angular/core"; -import { Child } from "../../children/model/child"; import { Note } from "../model/note"; import { faker } from "../../../core/demo-data/faker"; import { noteIndividualStories } from "./notes_individual-stories"; @@ -13,6 +12,7 @@ import { AttendanceLogicalStatus } from "../../attendance/model/attendance-statu import { DemoUserGeneratorService } from "../../../core/user/demo-user-generator.service"; import { defaultAttendanceStatusTypes } from "../../../core/config/default-config/default-attendance-status-types"; import { warningLevels } from "../../warning-level"; +import { Entity } from "../../../core/entity/model/entity"; export class DemoNoteConfig { minNotesPerChild: number; @@ -71,7 +71,7 @@ export class DemoNoteGeneratorService extends DemoDataGenerator { child, faker.date.between({ from: moment().subtract(6, "days").toDate(), - to: faker.getEarlierDateOrToday(child.dropoutDate), + to: moment().toDate(), }), ), ); @@ -84,8 +84,8 @@ export class DemoNoteGeneratorService extends DemoDataGenerator { } for (const center of centersUnique) { - const children: Child[] = this.demoChildren.entities.filter( - (c) => c.center === center, + const children: Entity[] = this.demoChildren.entities.filter( + (c) => c["center"] === center, ); for (let i = 0; i < this.config.groupNotes; i++) { data.push(this.generateGroupNote(children)); @@ -95,7 +95,7 @@ export class DemoNoteGeneratorService extends DemoDataGenerator { return data; } - private generateNoteForChild(child: Child, date?: Date): Note { + private generateNoteForChild(child: Entity, date?: Date): Note { const note = new Note(); const selectedStory = faker.helpers.arrayElement(noteIndividualStories); @@ -108,8 +108,8 @@ export class DemoNoteGeneratorService extends DemoDataGenerator { if (!date) { date = faker.date.between({ - from: child.admissionDate, - to: faker.getEarlierDateOrToday(child.dropoutDate), + from: child["admissionDate"], + to: faker.getEarlierDateOrToday(child["dropoutDate"]), }); } note.date = date; @@ -130,7 +130,7 @@ export class DemoNoteGeneratorService extends DemoDataGenerator { } } - private generateGroupNote(children: Child[]) { + private generateGroupNote(children: Entity[]) { const note = new Note(); const selectedStory = faker.helpers.arrayElement(noteGroupStories); diff --git a/src/app/child-dev-project/notes/model/note.ts b/src/app/child-dev-project/notes/model/note.ts index 4fcb49e812..bb37755e59 100644 --- a/src/app/child-dev-project/notes/model/note.ts +++ b/src/app/child-dev-project/notes/model/note.ts @@ -30,18 +30,13 @@ import { AttendanceLogicalStatus, NullAttendanceStatusType, } from "../../attendance/model/attendance-status"; -import { User } from "../../../core/user/user"; -import { Child } from "../../children/model/child"; import { getWarningLevelColor, WarningLevel } from "../../warning-level"; -import { School } from "../../schools/model/school"; import { Ordering } from "../../../core/basic-datatypes/configurable-enum/configurable-enum-ordering"; import { PLACEHOLDERS } from "../../../core/entity/schema/entity-schema-field"; @DatabaseEntity("Note") export class Note extends Entity { - static toStringAttributes = ["subject"]; - static label = $localize`:label for entity:Note`; - static labelPlural = $localize`:label (plural) for entity:Notes`; + static override toStringAttributes = ["subject"]; static override hasPII = true; static create( @@ -75,12 +70,12 @@ export class Note extends Entity { } } + // TODO: remove these special properties (children, schools) and use relatedEntities instead once the attendance system is generalized (#1364) /** IDs of Child entities linked with this note */ @DatabaseField({ - label: $localize`:Label for the children of a note:Children`, dataType: "entity", isArray: true, - additional: Child.ENTITY_TYPE, + additional: "Child", entityReferenceRole: "composite", editComponent: "EditAttendance", anonymize: "retain", @@ -96,7 +91,6 @@ export class Note extends Entity { private childrenAttendance: EventAttendanceMap = new EventAttendanceMap(); @DatabaseField({ - label: $localize`:Label for the date of a note:Date`, dataType: "date-only", defaultValue: { mode: "dynamic", @@ -106,21 +100,17 @@ export class Note extends Entity { }) date: Date; - @DatabaseField({ label: $localize`:Label for the subject of a note:Subject` }) + @DatabaseField() subject: string; - @DatabaseField({ - label: $localize`:Label for the actual notes of a note:Notes`, - editComponent: "EditLongText", - }) + @DatabaseField({ dataType: "long-text" }) text: string; /** IDs of users that authored this note */ @DatabaseField({ - label: $localize`:Label for the social worker(s) who created the note:SW`, dataType: "entity", isArray: true, - additional: User.ENTITY_TYPE, + additional: "User", defaultValue: { mode: "dynamic", value: PLACEHOLDERS.CURRENT_USER, @@ -130,7 +120,6 @@ export class Note extends Entity { authors: string[] = []; @DatabaseField({ - label: $localize`:Label for the category of a note:Category`, dataType: "configurable-enum", additional: INTERACTION_TYPE_CONFIG_ID, anonymize: "retain", @@ -138,7 +127,6 @@ export class Note extends Entity { category: InteractionType; @DatabaseField({ - label: $localize`Attachment`, dataType: "file", }) attachment: string; @@ -158,7 +146,6 @@ export class Note extends Entity { * This property saves ids including their entity type prefix. */ @DatabaseField({ - label: $localize`:label for the related Entities:Related Records`, dataType: "entity", isArray: true, // by default no additional relatedEntities can be linked apart from children and schools, overwrite this in config to display (e.g. additional: "ChildSchoolRelation") @@ -171,24 +158,22 @@ export class Note extends Entity { * related school ids (e.g. to infer participants for event roll calls) */ @DatabaseField({ - label: $localize`:label for the linked schools:Groups`, dataType: "entity", isArray: true, - additional: School.ENTITY_TYPE, + additional: "School", entityReferenceRole: "composite", anonymize: "retain", }) schools: string[] = []; @DatabaseField({ - label: $localize`:Status of a note:Status`, dataType: "configurable-enum", additional: "warning-levels", anonymize: "retain", }) warningLevel: Ordering.EnumValue; - getWarningLevel(): WarningLevel { + override getWarningLevel(): WarningLevel { if (this.warningLevel) { return WarningLevel[this.warningLevel.id]; } else { @@ -196,7 +181,7 @@ export class Note extends Entity { } } - public getColor() { + public override getColor() { const actualLevel = this.getWarningLevel(); if (actualLevel === WarningLevel.OK || actualLevel === WarningLevel.NONE) { return this.category?.color; @@ -230,7 +215,7 @@ export class Note extends Entity { * adds a new child to this note * @param child The child or the id of the child to add to the notes */ - addChild(child: Child | string) { + addChild(child: Entity | string) { const childId = typeof child === "string" ? child : child?.getId(); if (!childId || this.children.includes(childId)) { return; @@ -243,7 +228,7 @@ export class Note extends Entity { * adds a new school to this note * @param school The school or its id to be added to the note */ - addSchool(school: School | string) { + addSchool(school: Entity | string) { const schoolId = typeof school === "string" ? school : school.getId(); if (this.schools.includes(schoolId)) { return; @@ -260,7 +245,7 @@ export class Note extends Entity { * * @param child: The child or the id of the child to look for */ - getAttendance(child: string | Child): EventAttendance { + getAttendance(child: string | Entity): EventAttendance { const childId = typeof child === "string" ? child : child.getId(); if (!this.children.includes(childId)) { return undefined; @@ -320,7 +305,7 @@ export class Note extends Entity { * (such as the date, author, e.t.c.) as well as copying the * child-array */ - copy(): this { + override copy(): this { const note = super.copy(); note.children = [...this.children]; note.schools = [...this.schools]; diff --git a/src/app/child-dev-project/notes/note-details/note-details.component.html b/src/app/child-dev-project/notes/note-details/note-details.component.html index 9256917b1a..7f531f906e 100644 --- a/src/app/child-dev-project/notes/note-details/note-details.component.html +++ b/src/app/child-dev-project/notes/note-details/note-details.component.html @@ -47,7 +47,7 @@ - + + +} + + +
+
+ Inherited value from parent record selected in field + "" +
+ +
+ + @if (defaultValueHint.isEmpty) { +
+ Select exactly one record in the parent field to automatically inherit + its value here. +
+ } @else if (defaultValueHint.isInSync) { +
(up-to-date, inherited from parent record)
+ } @else { +
+ (manually overwritten): click to reset value to inherited from parent + record + @if (!form.formGroup.enabled) { + (requires the form to be in "Edit" mode) + } +
+
+ } +
+
diff --git a/src/app/core/default-values/inherited-value-button/inherited-value-button.component.scss b/src/app/core/default-values/inherited-value-button/inherited-value-button.component.scss new file mode 100644 index 0000000000..f55d17edce --- /dev/null +++ b/src/app/core/default-values/inherited-value-button/inherited-value-button.component.scss @@ -0,0 +1,12 @@ +.inline-box { + display: inline-block; +} + +.field-hint { + color: white; + font-size: 12px; + + border-radius: 4px; + background-color: #3e3e3e; + padding: 1em; +} diff --git a/src/app/core/default-values/inherited-value-button/inherited-value-button.component.spec.ts b/src/app/core/default-values/inherited-value-button/inherited-value-button.component.spec.ts new file mode 100644 index 0000000000..a3ae19457e --- /dev/null +++ b/src/app/core/default-values/inherited-value-button/inherited-value-button.component.spec.ts @@ -0,0 +1,29 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { InheritedValueButtonComponent } from "./inherited-value-button.component"; +import { DefaultValueService } from "../default-value.service"; + +describe("InheritedValueButtonComponent", () => { + let component: InheritedValueButtonComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [InheritedValueButtonComponent], + providers: [ + { + provide: DefaultValueService, + useValue: jasmine.createSpyObj(["getDefaultValueUiHint"]), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(InheritedValueButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/core/default-values/inherited-value-button/inherited-value-button.component.ts b/src/app/core/default-values/inherited-value-button/inherited-value-button.component.ts new file mode 100644 index 0000000000..f489b8c636 --- /dev/null +++ b/src/app/core/default-values/inherited-value-button/inherited-value-button.component.ts @@ -0,0 +1,59 @@ +import { Component, Input, OnChanges, SimpleChanges } from "@angular/core"; +import { EntityForm } from "../../common-components/entity-form/entity-form.service"; +import { EntityFieldLabelComponent } from "../../common-components/entity-field-label/entity-field-label.component"; +import { FaIconComponent } from "@fortawesome/angular-fontawesome"; +import { TemplateTooltipDirective } from "../../common-components/template-tooltip/template-tooltip.directive"; +import { Entity } from "../../entity/model/entity"; +import { + DefaultValueHint, + DefaultValueService, +} from "../default-value.service"; +import { FormFieldConfig } from "../../common-components/entity-form/FormConfig"; +import { MatIconButton } from "@angular/material/button"; + +/** + * Display an indicator for form fields explaining the status of the inherited-value config of that field + * and allowing users to re-sync the inherited value manually. + */ +@Component({ + selector: "app-inherited-value-button", + standalone: true, + imports: [ + EntityFieldLabelComponent, + FaIconComponent, + TemplateTooltipDirective, + MatIconButton, + ], + templateUrl: "./inherited-value-button.component.html", + styleUrl: "./inherited-value-button.component.scss", +}) +export class InheritedValueButtonComponent implements OnChanges { + @Input() form: EntityForm; + @Input() field: FormFieldConfig; + @Input() entity: Entity; + + defaultValueHint: DefaultValueHint | undefined; + + constructor(private defaultValueService: DefaultValueService) {} + + ngOnChanges(changes: SimpleChanges): void { + this.defaultValueHint = this.defaultValueService.getDefaultValueUiHint( + this.form, + this.field?.id, + ); + + if (changes.form && changes.form.firstChange) { + this.form?.formGroup.valueChanges.subscribe((value) => + // ensure this is only called after the other changes handler + setTimeout( + () => + (this.defaultValueHint = + this.defaultValueService.getDefaultValueUiHint( + this.form, + this.field?.id, + )), + ), + ); + } + } +} diff --git a/src/app/core/default-values/inherited-value.service.spec.ts b/src/app/core/default-values/inherited-value.service.spec.ts new file mode 100644 index 0000000000..4304f0b5bf --- /dev/null +++ b/src/app/core/default-values/inherited-value.service.spec.ts @@ -0,0 +1,373 @@ +import { fakeAsync, TestBed, tick } from "@angular/core/testing"; + +import { InheritedValueService } from "./inherited-value.service"; +import { Entity } from "../entity/model/entity"; +import { EntityMapperService } from "../entity/entity-mapper/entity-mapper.service"; +import { + cleanUpTemporarySchemaFields, + getDefaultInheritedForm, +} from "./default-value.service.spec"; +import { DynamicPlaceholderValueService } from "./dynamic-placeholder-value.service"; +import { FormControl, FormGroup } from "@angular/forms"; +import { EntityForm } from "../common-components/entity-form/entity-form.service"; +import { DefaultValueService } from "./default-value.service"; + +describe("InheritedValueService", () => { + let service: InheritedValueService; + let defaultValueService: DefaultValueService; + let mockEntityMapperService: jasmine.SpyObj; + + beforeEach(() => { + mockEntityMapperService = jasmine.createSpyObj(["load"]); + + TestBed.configureTestingModule({ + providers: [ + { provide: EntityMapperService, useValue: mockEntityMapperService }, + { provide: DynamicPlaceholderValueService, useValue: null }, + ], + }); + service = TestBed.inject(InheritedValueService); + defaultValueService = TestBed.inject(DefaultValueService); + }); + + afterEach(() => { + cleanUpTemporarySchemaFields(); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + it("should do nothing, if field in parent entity is missing", () => { + // given + let entity = new Entity(); + + let form: EntityForm = { + formGroup: new FormGroup({ + field1: new FormControl(), + }), + entity: entity, + defaultValueConfigs: new Map(), + watcher: new Map(), + inheritedParentValues: new Map(), + }; + + let targetFormControl = form.formGroup.get("field1"); + + // when + service.setDefaultValue( + targetFormControl, + { + defaultValue: { + mode: "inherited", + field: "invalid-field", + localAttribute: "reference-1", + }, + }, + form, + ); + + // then + expect(targetFormControl.value).toBe(undefined); + expect( + form.watcher.has("sourceFormControlValueChanges_field1"), + ).toBeFalse(); + }); + + it("should set default value on FormControl, if target field empty", fakeAsync(() => { + // given + let entity = new Entity(); + entity["foo"] = "bar"; + mockEntityMapperService.load.and.returnValue(Promise.resolve(entity)); + + let form: EntityForm = { + formGroup: new FormGroup({ + field1: new FormControl(), + field2: new FormControl(), + }), + entity: entity, + defaultValueConfigs: new Map(), + watcher: new Map(), + inheritedParentValues: new Map(), + }; + + let targetFormControl = form.formGroup.get("field1"); + + // when + service.setDefaultValue( + targetFormControl, + { + isArray: false, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "field2", + }, + }, + form, + ); + + // when + tick(); + form.formGroup.get("field2").setValue("Entity:0"); + tick(10); // fetching reference is always async + + // then + expect(form.watcher.has("sourceFormControlValueChanges_field2")).toBeTrue(); + expect(targetFormControl.value).toBe("bar"); + })); + + it("should set default array value on FormControl, if target field empty", fakeAsync(() => { + // given + let entity = new Entity(); + entity["foo"] = ["bar", "doo"]; + mockEntityMapperService.load.and.returnValue(Promise.resolve(entity)); + + let form: EntityForm = { + formGroup: new FormGroup({ + field1: new FormControl(), + field2: new FormControl(), + }), + entity: entity, + defaultValueConfigs: new Map(), + watcher: new Map(), + inheritedParentValues: new Map(), + }; + + let targetFormControl = form.formGroup.get("field1"); + + // when + service.setDefaultValue( + targetFormControl, + { + isArray: true, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "field2", + }, + }, + form, + ); + + // when + tick(); + form.formGroup.get("field2").setValue("Entity:0"); + tick(10); // fetching reference is always async + + // then + expect(form.watcher.has("sourceFormControlValueChanges_field2")).toBeTrue(); + expect(targetFormControl.value).toEqual(["bar", "doo"]); + })); + + it("should set value on FormControl, if source is single value array", fakeAsync(() => { + // given + let form = getDefaultInheritedForm({ + field: { + isArray: true, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "reference-1", + }, + }, + }); + + let entity0 = new Entity(); + entity0["foo"] = ["bar"]; + mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); + + // when + defaultValueService.handleEntityForm(form, form.entity); + tick(); + form.formGroup.get("reference-1").setValue(["Entity:0"]); + tick(10); // fetching reference is always async + + // then + expect(form.formGroup.get("field").value).toEqual(["bar"]); + })); + + it("should not set value on FormControl, if source is multi value array", fakeAsync(() => { + // given + let form = getDefaultInheritedForm({ + field: { + isArray: true, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "reference-1", + }, + }, + }); + + let entity0 = new Entity(); + entity0["foo"] = ["bar", "doo"]; + mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); + + // when + defaultValueService.handleEntityForm(form, form.entity); + tick(); + form.formGroup.get("reference-1").setValue(["Entity:0", "Entity:1"]); + tick(10); // fetching reference is always async + + // then + expect(form.formGroup.get("field").value).toEqual(undefined); + })); + + it("should reset FormControl, if parent (array) field got cleared", fakeAsync(() => { + // given + let form = getDefaultInheritedForm({ + field: { + isArray: true, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "reference-1", + }, + }, + }); + + let entity0 = new Entity(); + entity0["foo"] = ["bar", "doo"]; + mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); + + // when + defaultValueService.handleEntityForm(form, form.entity); + tick(); + form.formGroup.get("reference-1").setValue("foo"); + tick(); + + expect(form.formGroup.get("field").value).toEqual(["bar", "doo"]); + + // when/then + form.formGroup.get("reference-1").setValue(null); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(undefined); + + form.formGroup.get("reference-1").setValue(undefined); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(undefined); + + form.formGroup.get("reference-1").setValue(""); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(undefined); + })); + + it("should reset FormControl, if parent (single value) field got cleared", fakeAsync(() => { + // given + let form = getDefaultInheritedForm({ + field: { + isArray: false, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "reference-1", + }, + }, + }); + + let entity0 = new Entity(); + entity0["foo"] = "bar"; + mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); + + // when + defaultValueService.handleEntityForm(form, form.entity); + tick(); + form.formGroup.get("reference-1").setValue("foo"); + tick(); + + expect(form.formGroup.get("field").value).toBe("bar"); + + // when/then + form.formGroup.get("reference-1").setValue(null); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(undefined); + + form.formGroup.get("reference-1").setValue(undefined); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(undefined); + + form.formGroup.get("reference-1").setValue(""); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(undefined); + })); + + it("should do nothing, if parent entity does not exist", fakeAsync(() => { + // given + let form = getDefaultInheritedForm({ + field: { + isArray: true, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "reference-1", + }, + }, + }); + + mockEntityMapperService.load.and.returnValue(Promise.resolve(undefined)); + + // when + defaultValueService.handleEntityForm(form, form.entity); + tick(); + + // when/then + form.formGroup.get("reference-1").setValue("non-existing-entity-id"); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(undefined); + })); + + it("should do nothing, if formGroup is disabled", fakeAsync(() => { + // given + let form = getDefaultInheritedForm({ + field: { + isArray: true, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "reference-1", + }, + }, + }); + + form.formGroup.disable(); + + mockEntityMapperService.load.and.returnValue(Promise.resolve(undefined)); + + // when + defaultValueService.handleEntityForm(form, form.entity); + tick(); + + // when/then + form.formGroup.get("reference-1").setValue("non-existing-entity-id"); + tick(); // fetching reference is always async + expect(form.formGroup.get("field").value).toBe(null); + })); + + it("should set value on FormControl, if source is not in formGroup but set on entity", fakeAsync(() => { + // given + let form = getDefaultInheritedForm({ + field: { + isArray: true, + defaultValue: { + mode: "inherited", + field: "foo", + localAttribute: "reference-1", + }, + }, + }); + form.formGroup.removeControl("reference-1"); + + let entity0 = new Entity(); + entity0["foo"] = ["bar"]; + mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); + form.entity["reference-1"] = entity0.getId(); + + // when + defaultValueService.handleEntityForm(form, form.entity); + tick(); + + // then + expect(form.formGroup.get("field").value).toEqual(["bar"]); + })); +}); diff --git a/src/app/core/default-values/inherited-value.service.ts b/src/app/core/default-values/inherited-value.service.ts new file mode 100644 index 0000000000..529299986e --- /dev/null +++ b/src/app/core/default-values/inherited-value.service.ts @@ -0,0 +1,276 @@ +import { Injectable } from "@angular/core"; +import { AbstractControl } from "@angular/forms"; +import { EntitySchemaField } from "../entity/schema/entity-schema-field"; +import { EntityForm } from "../common-components/entity-form/entity-form.service"; +import { Entity } from "../entity/model/entity"; +import { + DefaultValueStrategy, + getConfigsByMode, +} from "./default-value-strategy.interface"; +import { EntityMapperService } from "../entity/entity-mapper/entity-mapper.service"; +import { DefaultValueConfig } from "../entity/schema/default-value-config"; +import { DefaultValueHint } from "./default-value.service"; +import { asArray } from "../../utils/utils"; + +/** + * An advanced default-value strategy that sets values based on the value in a referenced related entity. + * + * This allows to configure hierarchies and inherited fields, + * e.g. setting the category field based on the category field in a linked "parent entity". + */ +@Injectable({ + providedIn: "root", +}) +export class InheritedValueService extends DefaultValueStrategy { + constructor(private entityMapper: EntityMapperService) { + super(); + } + + override async initEntityForm(form: EntityForm) { + await this.updateLinkedEntities(form); + } + + /** + * Set up the inheritance value for a field, triggering an initial inheritance + * and watching future changes of the source (parent) field for automatic updates. + * @param targetFormControl + * @param fieldConfig + * @param form + */ + async setDefaultValue( + targetFormControl: AbstractControl, + fieldConfig: EntitySchemaField, + form: EntityForm, + ) { + // load inherited from initial entity + await this.onSourceValueChange( + form, + targetFormControl, + fieldConfig, + this.getParentRefId(form, fieldConfig.defaultValue), + ); + + // subscribe to update inherited whenever source field changes + let sourceFormControl: AbstractControl | null = + form.formGroup.get(fieldConfig.defaultValue.localAttribute); + if (sourceFormControl && targetFormControl) { + form.watcher.set( + "sourceFormControlValueChanges_" + + fieldConfig.defaultValue.localAttribute, + sourceFormControl.valueChanges.subscribe( + async (change) => + await this.onSourceValueChange( + form, + targetFormControl, + fieldConfig, + change, + ), + ), + ); + } + } + + /** + * Update the inherited (target field) value based on the change of the source field (parent reference) change. + * @param form + * @param targetFormControl + * @param fieldConfig + * @param change The new entity ID value of the source (parent ref) field. + * @private + */ + private async onSourceValueChange( + form: EntityForm, + targetFormControl: AbstractControl, + fieldConfig: EntitySchemaField, + change, + ) { + if (form.formGroup.disabled) { + return; + } + + if ( + targetFormControl.dirty && + !!targetFormControl.value && + form.entity.isNew + ) { + return; + } + + if (!form.entity.isNew) { + return; + } + + if (!change || "") { + targetFormControl.setValue(undefined); + return; + } + + // source field is array, use first element if only one element + if (Array.isArray(change)) { + if (change.length === 1) { + change = change[0]; + } else { + targetFormControl.setValue(undefined); + return; + } + } + + let parentEntity: Entity = await this.entityMapper.load( + Entity.extractTypeFromId(change), + change, + ); + + if ( + !parentEntity || + parentEntity[fieldConfig.defaultValue.field] === undefined + ) { + return; + } + + if (fieldConfig.isArray) { + targetFormControl.setValue([ + ...parentEntity[fieldConfig.defaultValue.field], + ]); + } else { + targetFormControl.setValue(parentEntity[fieldConfig.defaultValue.field]); + } + + targetFormControl.markAsUntouched(); + targetFormControl.markAsPristine(); + } + + override async onFormValueChanges(form: EntityForm) { + await this.updateLinkedEntities(form); + } + + /** + * Get details about the status and context of an inherited value field + * to display to the user. + * @param form + * @param fieldId + */ + getDefaultValueUiHint( + form: EntityForm, + fieldId: string, + ): DefaultValueHint | undefined { + const defaultConfig = form?.defaultValueConfigs?.get(fieldId); + if (!defaultConfig) { + return; + } + + const parentRefValue = this.getParentRefId(form, defaultConfig); + if (!parentRefValue) { + return { + inheritedFromField: defaultConfig.localAttribute, + isEmpty: true, + }; + } + + return { + inheritedFromField: defaultConfig.localAttribute, + inheritedFromType: parentRefValue + ? Entity.extractTypeFromId(parentRefValue) + : undefined, + isInSync: + JSON.stringify(form.inheritedParentValues.get(fieldId)) === + JSON.stringify(form.formGroup.get(fieldId)?.value), + syncFromParentField: () => { + form.formGroup + .get(fieldId) + .setValue(form.inheritedParentValues.get(fieldId)); + }, + }; + } + + private async updateLinkedEntities(form: EntityForm) { + let inheritedConfigs: Map = getConfigsByMode( + form.defaultValueConfigs, + ["inherited"], + ); + + const linkedEntityRefs: Map = this.getLinkedEntityRefs( + inheritedConfigs, + form, + ); + + for (let [fieldId, parentEntityIds] of linkedEntityRefs) { + if (parentEntityIds.length > 1) { + // multi-inheritance not supported (yet) -> keep values in form and stop inheritance + form.inheritedParentValues.delete(fieldId); + continue; + } + + let parentEntity: Entity; + if (parentEntityIds.length === 1) { + parentEntity = await this.entityMapper.load( + Entity.extractTypeFromId(parentEntityIds[0]), + parentEntityIds[0], + ); + } + + // if value empty -> set inherited values to undefined + form.inheritedParentValues.set( + fieldId, + parentEntity?.[inheritedConfigs.get(fieldId).field], + ); + } + } + + /** + * Get the linked entity references from the form. + * @param inheritedConfigs + * @param form + * @return Entity ids of the linked parent entities (always wrapped as an array) + * @private + */ + private getLinkedEntityRefs( + inheritedConfigs: Map, + form: EntityForm, + ): Map { + const linkedEntityRefs: Map = new Map(); + + for (const [key, defaultValueConfig] of inheritedConfigs) { + let linkedEntities: null | string | string[] = this.getParentRefId( + form, + defaultValueConfig, + false, + ); + + if (linkedEntities == null || linkedEntities.length == 0) { + continue; + } + + linkedEntityRefs.set(key, asArray(linkedEntities)); + } + + return linkedEntityRefs; + } + + /** + * Get the parent reference id from the form or entity. + * @param form + * @param defaultConfig + * @param castToSingle Whether for arrays of IDs, this should be cast to a single ID only. + * @private + */ + private getParentRefId( + form: EntityForm, + defaultConfig: DefaultValueConfig, + castToSingle = true, + ): string | undefined { + const linkedFieldValue = + form.formGroup?.get(defaultConfig.localAttribute)?.value ?? + form.entity?.[defaultConfig.localAttribute]; + + if (!castToSingle) { + return linkedFieldValue; + } + + if (Array.isArray(linkedFieldValue)) { + // inheritance is only supported for a single parent reference + return linkedFieldValue?.length === 1 ? linkedFieldValue[0] : undefined; + } else { + return linkedFieldValue; + } + } +} diff --git a/src/app/core/demo-data/create-entity-of-type.ts b/src/app/core/demo-data/create-entity-of-type.ts new file mode 100644 index 0000000000..057cd12ec9 --- /dev/null +++ b/src/app/core/demo-data/create-entity-of-type.ts @@ -0,0 +1,29 @@ +import { v4 as uuid } from "uuid"; +import { EntitySchemaField } from "../entity/schema/entity-schema-field"; +import { Entity } from "../entity/model/entity"; + +/** + * "Simulate" a custom entity type that saves all fields without transformations through a mocked EntitySchema. + * + * This is a utility function to help demo data creation + * because the dynamically migrated config is not ready at the time of demo data generation yet. + * + * @param type + * @param id + */ +export function createEntityOfType( + type: string, + id: string = uuid(), +): Entity & { [key: string]: any } { + const entity = new Entity(id); + // @ts-ignore + entity._id = entity._id.replace(Entity.ENTITY_TYPE, type); + + entity.getSchema = () => + new Map( + Object.keys(entity).map((key) => [key, {}]), + ); + entity.getType = () => type; + + return entity; +} diff --git a/src/app/core/demo-data/demo-data-initializer.service.spec.ts b/src/app/core/demo-data/demo-data-initializer.service.spec.ts index bc21375a1b..466e79b4da 100644 --- a/src/app/core/demo-data/demo-data-initializer.service.spec.ts +++ b/src/app/core/demo-data/demo-data-initializer.service.spec.ts @@ -16,12 +16,14 @@ import { LoginState } from "../session/session-states/login-state.enum"; describe("DemoDataInitializerService", () => { const normalUser: SessionInfo = { - name: "demo", + name: DemoUserGeneratorService.DEFAULT_USERNAME, + id: DemoUserGeneratorService.DEFAULT_USERNAME, entityId: "User:demo", roles: ["user_app"], }; const adminUser: SessionInfo = { - name: "demo-admin", + name: DemoUserGeneratorService.ADMIN_USERNAME, + id: DemoUserGeneratorService.ADMIN_USERNAME, entityId: "User:demo-admin", roles: ["user_app", "admin_app"], }; @@ -114,7 +116,8 @@ describe("DemoDataInitializerService", () => { tick(); TestBed.inject(SessionSubject).next({ - name: DemoUserGeneratorService.ADMIN_USERNAME, + name: adminUser.name, + id: adminUser.id, roles: [], }); database.initInMemoryDB(adminDBName); @@ -145,7 +148,8 @@ describe("DemoDataInitializerService", () => { const database = TestBed.inject(Database) as PouchDatabase; TestBed.inject(SessionSubject).next({ - name: DemoUserGeneratorService.ADMIN_USERNAME, + name: adminUser.name, + id: adminUser.id, roles: [], }); database.initInMemoryDB(adminDBName); diff --git a/src/app/core/demo-data/demo-data-initializer.service.ts b/src/app/core/demo-data/demo-data-initializer.service.ts index 701bfce728..0ff2c51bdb 100644 --- a/src/app/core/demo-data/demo-data-initializer.service.ts +++ b/src/app/core/demo-data/demo-data-initializer.service.ts @@ -14,7 +14,6 @@ import { LoginState } from "../session/session-states/login-state.enum"; import { LoginStateSubject, SessionType } from "../session/session-type"; import memory from "pouchdb-adapter-memory"; import PouchDB from "pouchdb-browser"; -import { User } from "../user/user"; /** * This service handles everything related to the demo-mode @@ -29,13 +28,15 @@ export class DemoDataInitializerService { private pouchDatabase: PouchDatabase; private readonly normalUser: SessionInfo = { name: DemoUserGeneratorService.DEFAULT_USERNAME, + id: DemoUserGeneratorService.DEFAULT_USERNAME, roles: ["user_app"], - entityId: `${User.ENTITY_TYPE}:${DemoUserGeneratorService.DEFAULT_USERNAME}`, + entityId: `User:${DemoUserGeneratorService.DEFAULT_USERNAME}`, }; private readonly adminUser: SessionInfo = { name: DemoUserGeneratorService.ADMIN_USERNAME, + id: DemoUserGeneratorService.ADMIN_USERNAME, roles: ["user_app", "admin_app"], - entityId: `${User.ENTITY_TYPE}:${DemoUserGeneratorService.ADMIN_USERNAME}`, + entityId: `User:${DemoUserGeneratorService.ADMIN_USERNAME}`, }; constructor( private demoDataService: DemoDataService, diff --git a/src/app/core/demo-data/demo-data.module.ts b/src/app/core/demo-data/demo-data.module.ts index e04fec7a98..721b8282f1 100644 --- a/src/app/core/demo-data/demo-data.module.ts +++ b/src/app/core/demo-data/demo-data.module.ts @@ -23,17 +23,17 @@ import { DemoDataGeneratingProgressDialogComponent } from "./demo-data-generatin import { DemoDataInitializerService } from "./demo-data-initializer.service"; import { DemoConfigGeneratorService } from "../config/demo-config-generator.service"; import { DemoChildGenerator } from "../../child-dev-project/children/demo-data-generators/demo-child-generator.service"; -import { DemoSchoolGenerator } from "../../child-dev-project/schools/demo-school-generator.service"; +import { DemoSchoolGenerator } from "../../child-dev-project/children/demo-data-generators/demo-school-generator.service"; import { DemoChildSchoolRelationGenerator } from "../../child-dev-project/children/demo-data-generators/demo-child-school-relation-generator.service"; import { DemoActivityGeneratorService } from "../../child-dev-project/attendance/demo-data/demo-activity-generator.service"; import { DemoActivityEventsGeneratorService } from "../../child-dev-project/attendance/demo-data/demo-activity-events-generator.service"; import { DemoNoteGeneratorService } from "../../child-dev-project/notes/demo-data/demo-note-generator.service"; -import { DemoAserGeneratorService } from "../../child-dev-project/children/aser/demo-aser-generator.service"; -import { DemoEducationalMaterialGeneratorService } from "../../child-dev-project/children/educational-material/demo-educational-material-generator.service"; -import { DemoHealthCheckGeneratorService } from "../../child-dev-project/children/health-checkup/demo-data/demo-health-check-generator.service"; +import { DemoAserGeneratorService } from "../../child-dev-project/children/demo-data-generators/aser/demo-aser-generator.service"; +import { DemoEducationalMaterialGeneratorService } from "../../child-dev-project/children/demo-data-generators/educational-material/demo-educational-material-generator.service"; +import { DemoHealthCheckGeneratorService } from "../../child-dev-project/children/demo-data-generators/health-check/demo-health-check-generator.service"; import { DemoProgressDashboardWidgetGeneratorService } from "../../features/dashboard-widgets/progress-dashboard-widget/demo-progress-dashboard-widget-generator.service"; import { DemoUserGeneratorService } from "../user/demo-user-generator.service"; -import { DemoHistoricalDataGenerator } from "../../features/historical-data/demo-historical-data-generator"; +import { DemoHistoricalDataGenerator } from "../../child-dev-project/children/demo-data-generators/observations/demo-historical-data-generator"; import { DemoPermissionGeneratorService } from "../permissions/demo-permission-generator.service"; import { DemoTodoGeneratorService } from "../../features/todos/model/demo-todo-generator.service"; import { DemoConfigurableEnumGeneratorService } from "../basic-datatypes/configurable-enum/demo-configurable-enum-generator.service"; diff --git a/src/app/core/entity-details/EntityDetailsConfig.ts b/src/app/core/entity-details/EntityDetailsConfig.ts index b78e1f6c56..4637749a5f 100644 --- a/src/app/core/entity-details/EntityDetailsConfig.ts +++ b/src/app/core/entity-details/EntityDetailsConfig.ts @@ -1,7 +1,7 @@ import { Entity } from "../entity/model/entity"; /** - * The configuration for a entity details page + * The configuration for an entity details page */ export interface EntityDetailsConfig { /** @@ -47,7 +47,7 @@ export interface PanelComponent { component: string; /** - * A addition config which will be passed to the component. + * An addition config which will be passed to the component. */ config?: any; } diff --git a/src/app/core/entity-details/abstract-entity-details/abstract-entity-details.component.spec.ts b/src/app/core/entity-details/abstract-entity-details/abstract-entity-details.component.spec.ts index ed402ad710..8e6123e81e 100644 --- a/src/app/core/entity-details/abstract-entity-details/abstract-entity-details.component.spec.ts +++ b/src/app/core/entity-details/abstract-entity-details/abstract-entity-details.component.spec.ts @@ -8,13 +8,13 @@ import { import { AbstractEntityDetailsComponent } from "./abstract-entity-details.component"; import { Router } from "@angular/router"; import { EntityDetailsConfig } from "../EntityDetailsConfig"; -import { Child } from "../../../child-dev-project/children/model/child"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { EntityActionsService } from "../../entity/entity-actions/entity-actions.service"; import { EntityAbility } from "../../permissions/ability/entity-ability"; import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service"; import { Component, SimpleChange } from "@angular/core"; import { mockEntityMapper } from "../../entity/entity-mapper/mock-entity-mapper-service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; @Component({ template: ``, @@ -27,7 +27,7 @@ describe("AbstractEntityDetailsComponent", () => { let fixture: ComponentFixture; const routeConfig: EntityDetailsConfig = { - entityType: "Child", + entityType: TestEntity.ENTITY_TYPE, panels: [], }; @@ -68,7 +68,7 @@ describe("AbstractEntityDetailsComponent", () => { it("should load the correct entity on init", fakeAsync(() => { component.isLoading = true; - const testChild = new Child("Test-Child"); + const testChild = new TestEntity("Test-Child"); const entityMapper = TestBed.inject(EntityMapperService); entityMapper.save(testChild); tick(); @@ -80,7 +80,7 @@ describe("AbstractEntityDetailsComponent", () => { tick(); expect(entityMapper.load).toHaveBeenCalledWith( - Child, + TestEntity, testChild.getId(true), ); expect(component.entity).toBe(testChild); @@ -88,7 +88,7 @@ describe("AbstractEntityDetailsComponent", () => { })); it("should also support the long ID format", fakeAsync(() => { - const child = new Child(); + const child = new TestEntity(); const entityMapper = TestBed.inject(EntityMapperService); entityMapper.save(child); tick(); @@ -98,7 +98,7 @@ describe("AbstractEntityDetailsComponent", () => { component.ngOnChanges(simpleChangesFor(component, "id")); tick(); - expect(entityMapper.load).toHaveBeenCalledWith(Child, child.getId()); + expect(entityMapper.load).toHaveBeenCalledWith(TestEntity, child.getId()); expect(component.entity).toEqual(child); // entity is updated diff --git a/src/app/core/entity-details/entity-details/entity-details.component.spec.ts b/src/app/core/entity-details/entity-details/entity-details.component.spec.ts index 64f729915e..2c7538ff53 100644 --- a/src/app/core/entity-details/entity-details/entity-details.component.spec.ts +++ b/src/app/core/entity-details/entity-details/entity-details.component.spec.ts @@ -7,20 +7,20 @@ import { } from "@angular/core/testing"; import { EntityDetailsComponent } from "./entity-details.component"; import { EntityDetailsConfig, PanelConfig } from "../EntityDetailsConfig"; -import { Child } from "../../../child-dev-project/children/model/child"; import { ChildrenService } from "../../../child-dev-project/children/children.service"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { EntityActionsService } from "../../entity/entity-actions/entity-actions.service"; import { EntityAbility } from "../../permissions/ability/entity-ability"; import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service"; import { SimpleChange } from "@angular/core"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("EntityDetailsComponent", () => { let component: EntityDetailsComponent; let fixture: ComponentFixture; const routeConfig: EntityDetailsConfig = { - entityType: "Child", + entityType: TestEntity.ENTITY_TYPE, panels: [ { title: "One Form", @@ -81,7 +81,7 @@ describe("EntityDetailsComponent", () => { }); it("sets the panels config with child and creating status", fakeAsync(() => { - const testChild = new Child("Test-Child"); + const testChild = new TestEntity("Test-Child"); testChild["_rev"] = "1"; // mark as "not new" TestBed.inject(EntityMapperService).save(testChild); tick(); diff --git a/src/app/core/entity-details/entity-details/entity-details.component.ts b/src/app/core/entity-details/entity-details/entity-details.component.ts index 00124a88ea..330e2874cf 100644 --- a/src/app/core/entity-details/entity-details/entity-details.component.ts +++ b/src/app/core/entity-details/entity-details/entity-details.component.ts @@ -65,7 +65,7 @@ export class EntityDetailsComponent */ @Input() panels: Panel[] = []; - async ngOnChanges(changes: SimpleChanges) { + override async ngOnChanges(changes: SimpleChanges) { await super.ngOnChanges(changes); if (changes.id || changes.entity || changes.panels) { diff --git a/src/app/core/entity-details/entity-details/entity-details.stories.ts b/src/app/core/entity-details/entity-details/entity-details.stories.ts index 7647e36dfd..3078760a4a 100644 --- a/src/app/core/entity-details/entity-details/entity-details.stories.ts +++ b/src/app/core/entity-details/entity-details/entity-details.stories.ts @@ -2,14 +2,14 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { StorybookBaseModule } from "../../../utils/storybook-base.module"; import { importProvidersFrom } from "@angular/core"; import { EntityDetailsComponent } from "./entity-details.component"; -import { Child } from "../../../child-dev-project/children/model/child"; import { EntityDetailsConfig } from "../EntityDetailsConfig"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; -const demoEntity = Child.create("John Doe"); +const demoEntity = TestEntity.create("John Doe"); demoEntity._rev = "1"; // make not "isNew" const config: EntityDetailsConfig = { - entityType: "Child", + entityType: TestEntity.ENTITY_TYPE, panels: [ { title: $localize`:Panel title:Basic Information`, @@ -19,12 +19,11 @@ const config: EntityDetailsConfig = { component: "Form", config: { fieldGroups: [ - { fields: ["photo"] }, { - fields: ["name", "projectNumber", "admissionDate"], + fields: ["name", "dateOfBirth"], header: "Personal Information", }, - { fields: ["center", "phone"], header: "Contact Details" }, + { fields: ["category", "other"], header: "Contact Details" }, ], }, }, diff --git a/src/app/core/entity-details/form/form.component.html b/src/app/core/entity-details/form/form.component.html index aef83d5002..97f6f212cc 100644 --- a/src/app/core/entity-details/form/form.component.html +++ b/src/app/core/entity-details/form/form.component.html @@ -2,7 +2,7 @@ class="form-buttons-wrapper padding-bottom-small flex-row gap-small align-self-end" > - + + +
- +
+ +
@@ -239,7 +249,9 @@

{{ title }}

-
Actions on selected records:
+
+ Actions on {{ selectedRows.length }} selected records: +
{ let component: EntityListComponent; @@ -46,11 +46,11 @@ describe("EntityListComponent", () => { groups: [ { name: "Basic Info", - columns: ["projectNumber", "name", "age", "gender"], + columns: ["name", "age", "category"], }, { name: "School Info", - columns: ["name", "age", "school"], + columns: ["name", "age", "other"], }, ], }, @@ -63,7 +63,7 @@ describe("EntityListComponent", () => { false: "Currently inactive children", } as BooleanFilterConfig, { - id: "center", + id: "category", }, ], }; @@ -196,7 +196,7 @@ describe("EntityListComponent", () => { initComponentInputs(); tick(); - const entity = new Child(); + const entity = new TestEntity(); entityUpdates.next({ entity: entity, type: "new" }); tick(); @@ -207,7 +207,7 @@ describe("EntityListComponent", () => { const entityUpdates = new Subject>(); const entityMapper = TestBed.inject(EntityMapperService); spyOn(entityMapper, "receiveUpdates").and.returnValue(entityUpdates); - const entity = new Child(); + const entity = new TestEntity(); createComponent(); initComponentInputs(); tick(); @@ -224,7 +224,7 @@ describe("EntityListComponent", () => { loader = TestbedHarnessEnvironment.loader(fixture); component = fixture.componentInstance; - component.entityConstructor = Child; + component.entityConstructor = TestEntity; fixture.detectChanges(); } diff --git a/src/app/core/entity-list/entity-list/entity-list.component.ts b/src/app/core/entity-list/entity-list/entity-list.component.ts index 8d9231f7f7..3b6bd2678f 100644 --- a/src/app/core/entity-list/entity-list/entity-list.component.ts +++ b/src/app/core/entity-list/entity-list/entity-list.component.ts @@ -3,6 +3,7 @@ import { EventEmitter, Input, OnChanges, + Optional, Output, SimpleChanges, } from "@angular/core"; @@ -55,6 +56,10 @@ import { EntityCreateButtonComponent } from "../../common-components/entity-crea import { AbilityModule } from "@casl/angular"; import { EntityActionsMenuComponent } from "../../entity-details/entity-actions-menu/entity-actions-menu.component"; import { ViewActionsComponent } from "../../common-components/view-actions/view-actions.component"; +import { + EntitySpecialLoaderService, + LoaderMethod, +} from "../../entity/entity-special-loader/entity-special-loader.service"; /** * This component allows to create a full-blown table with pagination, filtering, searching and grouping. @@ -112,6 +117,11 @@ export class EntityListComponent @Input() defaultSort: Sort; @Input() exportConfig: ExportColumnConfig[]; + /** + * The special service or method to load data via an index or other special method. + */ + @Input() loaderMethod: LoaderMethod; + @Input() clickMode: "navigate" | "popup" | "none" = "navigate"; /** initial / default state whether to include archived records in the list */ @@ -171,6 +181,7 @@ export class EntityListComponent private dialog: MatDialog, private duplicateRecord: DuplicateRecordService, private entityActionsService: EntityActionsService, + @Optional() private entitySpecialLoader: EntitySpecialLoaderService, ) { this.screenWidthObserver .platform() @@ -225,7 +236,11 @@ export class EntityListComponent * Template method that can be overwritten to change the loading logic. * @protected */ - protected async getEntities(): Promise { + protected getEntities(): Promise { + if (this.loaderMethod && this.entitySpecialLoader) { + return this.entitySpecialLoader.loadData(this.loaderMethod); + } + return this.entityMapperService.loadType(this.entityConstructor); } diff --git a/src/app/core/entity-list/entity-list/entity-list.stories.ts b/src/app/core/entity-list/entity-list/entity-list.stories.ts index 9f4b087e9d..60c2b4a30b 100644 --- a/src/app/core/entity-list/entity-list/entity-list.stories.ts +++ b/src/app/core/entity-list/entity-list/entity-list.stories.ts @@ -1,12 +1,9 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { EntityListComponent } from "./entity-list.component"; -import { Child } from "../../../child-dev-project/children/model/child"; import { DemoChildGenerator } from "../../../child-dev-project/children/demo-data-generators/demo-child-generator.service"; -import { User } from "../../user/user"; import { StorybookBaseModule } from "../../../utils/storybook-base.module"; import { importProvidersFrom } from "@angular/core"; - -const user = new User(); +import { TestEntity } from "../../../utils/test-utils/TestEntity"; export default { title: "Core/Entities/Entity List", @@ -18,8 +15,8 @@ export default { ], } as Meta; -const Template: StoryFn> = ( - args: EntityListComponent, +const Template: StoryFn> = ( + args: EntityListComponent, ) => ({ component: EntityListComponent, props: args, diff --git a/src/app/core/entity/database-indexing/database-indexing.service.spec.ts b/src/app/core/entity/database-indexing/database-indexing.service.spec.ts index 2b48d9b14c..fdbf74c71f 100644 --- a/src/app/core/entity/database-indexing/database-indexing.service.spec.ts +++ b/src/app/core/entity/database-indexing/database-indexing.service.spec.ts @@ -20,13 +20,29 @@ import { Database } from "../../database/database"; import { expectObservable } from "../../../utils/test-utils/observable-utils"; import { fakeAsync, tick } from "@angular/core/testing"; import { firstValueFrom } from "rxjs"; -import { Todo } from "../../../features/todos/model/todo"; import { Note } from "../../../child-dev-project/notes/model/note"; +import { Entity } from "../model/entity"; +import { DatabaseField } from "../database-field.decorator"; +import { DatabaseEntity } from "../database-entity.decorator"; describe("DatabaseIndexingService", () => { let service: DatabaseIndexingService; let mockDb: jasmine.SpyObj; + @DatabaseEntity("TestEntityWithRelation") + class TestEntityWithRelation extends Entity { + @DatabaseField({ + dataType: "entity", + isArray: true, + }) + relatedEntities: string[]; + + @DatabaseField({ + dataType: "date", + }) + deadline: Date; + } + beforeEach(() => { mockDb = jasmine.createSpyObj("mockDb", ["saveDatabaseIndex", "query"]); service = new DatabaseIndexingService(mockDb, null); @@ -154,7 +170,11 @@ describe("DatabaseIndexingService", () => { it("should generate index for entity property", async () => { const call = spyOn(service, "createIndex"); - await service.generateIndexOnProperty("testIndex", Todo, "relatedEntities"); + await service.generateIndexOnProperty( + "testIndex", + TestEntityWithRelation, + "relatedEntities", + ); const actualCreatedDesignDoc = call.calls.argsFor(0)[0]; expect(cleanedUpStringify(actualCreatedDesignDoc)).toEqual( @@ -163,7 +183,7 @@ describe("DatabaseIndexingService", () => { views: { by_relatedEntities: { map: `(doc) => { - if (!doc._id.startsWith("Todo")) return; + if (!doc._id.startsWith("${TestEntityWithRelation.ENTITY_TYPE}")) return; if (!Array.isArray(doc.relatedEntities)) return; doc.relatedEntities.forEach((relatedEntity) => { emit(relatedEntity); @@ -195,7 +215,7 @@ describe("DatabaseIndexingService", () => { const call = spyOn(service, "createIndex"); await service.generateIndexOnProperty( "testIndex", - Todo, + TestEntityWithRelation, "relatedEntities", "deadline", ); @@ -205,7 +225,7 @@ describe("DatabaseIndexingService", () => { cleanedUpStringify(actualCreatedDesignDoc.views.by_relatedEntities.map), ).toEqual( cleanedUpStringify(`(doc) => { - if (!doc._id.startsWith("Todo")) return; + if (!doc._id.startsWith("${TestEntityWithRelation.ENTITY_TYPE}")) return; if (!Array.isArray(doc.relatedEntities)) return; doc.relatedEntities.forEach((relatedEntity) => { diff --git a/src/app/core/entity/default-datatype/edit-component.ts b/src/app/core/entity/default-datatype/edit-component.ts index 56dfec3698..1c3c00d0dc 100644 --- a/src/app/core/entity/default-datatype/edit-component.ts +++ b/src/app/core/entity/default-datatype/edit-component.ts @@ -2,6 +2,7 @@ import { FormControl, FormGroup } from "@angular/forms"; import { FormFieldConfig } from "../../common-components/entity-form/FormConfig"; import { Entity } from "../model/entity"; import { Directive, Input, OnChanges, OnInit } from "@angular/core"; +import { DefaultValueConfig } from "../schema/default-value-config"; /** * A simple helper class which sets up all the required information for edit-components. @@ -29,6 +30,11 @@ export abstract class EditComponent implements OnInit, OnChanges { */ @Input() formControlName: string; + /** + * Describes the defaultValue behaviour for this field + */ + @Input() defaultValueConfig: DefaultValueConfig | undefined; + /** * A label for this component. */ diff --git a/src/app/core/entity/default-field-value/handle-default-values.usecase.spec.ts b/src/app/core/entity/default-field-value/handle-default-values.usecase.spec.ts deleted file mode 100644 index 5c50ba8e4f..0000000000 --- a/src/app/core/entity/default-field-value/handle-default-values.usecase.spec.ts +++ /dev/null @@ -1,672 +0,0 @@ -import { fakeAsync, TestBed, tick } from "@angular/core/testing"; - -import { HandleDefaultValuesUseCase } from "./handle-default-values.usecase"; -import { EntityMapperService } from "../entity-mapper/entity-mapper.service"; -import { CurrentUserSubject } from "../../session/current-user-subject"; -import { FormBuilder, FormControl } from "@angular/forms"; -import { EntitySchemaField } from "../schema/entity-schema-field"; -import { Entity } from "../model/entity"; - -function getDefaultInheritedFormGroup() { - return new FormBuilder().group({ - "field-1": new FormControl(), - "field-2": new FormControl(), - "reference-1": new FormControl(), - }); -} - -describe("HandleDefaultValuesUseCase", () => { - let service: HandleDefaultValuesUseCase; - let mockEntityMapperService: jasmine.SpyObj; - - beforeEach(() => { - mockEntityMapperService = jasmine.createSpyObj(["load"]); - - TestBed.configureTestingModule({ - providers: [ - { provide: EntityMapperService, useValue: mockEntityMapperService }, - CurrentUserSubject, - ], - }); - service = TestBed.inject(HandleDefaultValuesUseCase); - }); - - it("should be created", () => { - expect(service).toBeTruthy(); - }); - - describe("on dynamic mode", () => { - it("should do nothing, if targetFormControl is missing", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-invalid", - { - defaultValue: { - mode: "dynamic", - value: "bar", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe(null); - })); - it("should not set default value on FormControl, if target field is dirty and not empty", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "dynamic", - value: "foo", - }, - }, - ], - ]; - - formGroup.get("field-2").setValue("pre-filled"); - formGroup.get("field-2").markAsDirty(); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe("pre-filled"); - })); - - it("should do nothing, if value is not a valid PLACEHOLDER", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "dynamic", - value: "invalid-placeholder", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe(null); - })); - - it("should set current USER, if PLACEHOLDER.CURRENT_USER is selected", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "dynamic", - value: "$current_user", - }, - }, - ], - ]; - - let user = new Entity(); - - TestBed.inject(CurrentUserSubject).next(user); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe(user.getId()); - })); - - it("should set current Date, if PLACEHOLDER.NOW is selected", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "dynamic", - value: "$now", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBeDate(new Date()); - })); - - it("should do nothing, if entity is not new", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "dynamic", - value: "$now", - }, - }, - ], - ]; - - mockEntityMapperService.load.and.returnValue(Promise.resolve(undefined)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, false); - - // when/then - formGroup.get("reference-1").setValue("non-existing-entity-id"); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(null); - })); - }); - - describe("on static mode", () => { - it("should do nothing, if targetFormControl is missing", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-invalid", - { - defaultValue: { - mode: "static", - value: "bar", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe(null); - })); - - it("should set default value on FormControl, if target field empty", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "static", - value: "bar", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe("bar"); - })); - - it("should not set default value on FormControl, if target field is dirty and not empty", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "static", - value: "foo", - }, - }, - ], - ]; - - formGroup.get("field-2").setValue("pre-filled"); - formGroup.get("field-2").markAsDirty(); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe("pre-filled"); - })); - - it("should not set default value on FormControl, if target field is not empty", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "static", - value: "bar", - }, - }, - ], - ]; - formGroup.get("field-2").setValue("foo"); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // then - expect(formGroup.get("field-2").value).toBe("foo"); - })); - - it("should do nothing, if entity is not new", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "static", - value: "foo", - }, - }, - ], - ]; - - mockEntityMapperService.load.and.returnValue(Promise.resolve(undefined)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, false); - - // when/then - formGroup.get("reference-1").setValue("non-existing-entity-id"); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(null); - })); - }); - - describe("on inherited mode", () => { - it("should do nothing, if parentFormControl is missing", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-invalid", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - tick(); // fetching reference is always async - - // then - expect(formGroup.get("field-2").value).toBe(null); - })); - - it("should do nothing, if field in parent entity is missing", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "invalid-field", - localAttribute: "reference-1", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - tick(); // fetching reference is always async - - // then - expect(formGroup.get("field-2").value).toBe(null); - })); - - it("should do nothing, if targetFormControl is missing", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-invalid", - { - defaultValue: { - mode: "inherited", - field: "invalid-field", - localAttribute: "reference-1", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - tick(); // fetching reference is always async - - // then - expect(formGroup.get("field-2").value).toBe(null); - })); - - it("should set default value on FormControl, if target field empty", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - let entity0 = new Entity(); - entity0["foo"] = "bar"; - mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - formGroup.get("reference-1").setValue("Entity:0"); - tick(10); // fetching reference is always async - - // then - expect(formGroup.get("field-1").value).toBe(null); - expect(formGroup.get("field-2").value).toBe("bar"); - })); - - it("should set default array value on FormControl, if target field empty", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - isArray: true, - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - let entity0 = new Entity(); - entity0["foo"] = ["bar", "doo"]; - mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - formGroup.get("reference-1").setValue("Entity:0"); - tick(10); // fetching reference is always async - - // then - expect(formGroup.get("field-1").value).toBe(null); - expect(formGroup.get("field-2").value).toEqual(["bar", "doo"]); - })); - - it("should set value on FormControl, if source is single value array", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - isArray: true, - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - let entity0 = new Entity(); - entity0["foo"] = ["bar"]; - mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - formGroup.get("reference-1").setValue(["Entity:0"]); - tick(10); // fetching reference is always async - - // then - expect(formGroup.get("field-1").value).toBe(null); - expect(formGroup.get("field-2").value).toEqual(["bar"]); - })); - - it("should not set value on FormControl, if source is multi value array", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - isArray: true, - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - let entity0 = new Entity(); - entity0["foo"] = ["bar", "doo"]; - mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - formGroup.get("reference-1").setValue(["Entity:0", "Entity:1"]); - tick(10); // fetching reference is always async - - // then - expect(formGroup.get("field-1").value).toBe(null); - expect(formGroup.get("field-2").value).toEqual(null); - })); - - it("should not set default value on FormControl, if target field is dirty and not empty", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - let entity0 = new Entity(); - entity0["foo"] = "bar"; - mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); - formGroup.get("field-2").setValue("pre-filled"); - formGroup.get("field-2").markAsDirty(); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - formGroup.get("reference-1").setValue("Entity:0"); - tick(); // fetching reference is always async - - // then - expect(formGroup.get("field-2").value).toBe("pre-filled"); - })); - - it("should reset FormControl, if parent field got cleared", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - formGroup.get("reference-1").setValue("foo bar doo"); - tick(); - - // when/then - formGroup.get("reference-1").setValue(null); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(undefined); - - formGroup.get("reference-1").setValue(undefined); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(undefined); - - formGroup.get("reference-1").setValue(""); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(undefined); - })); - - it("should do nothing, if parent entity does not exist", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - mockEntityMapperService.load.and.returnValue(Promise.resolve(undefined)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // when/then - formGroup.get("reference-1").setValue("non-existing-entity-id"); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(null); - })); - - it("should do nothing, if formGroup is disabled", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - formGroup.disable(); - - mockEntityMapperService.load.and.returnValue(Promise.resolve(undefined)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, true); - - // when/then - formGroup.get("reference-1").setValue("non-existing-entity-id"); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(null); - })); - - it("should do nothing, if entity is not new", fakeAsync(() => { - // given - let formGroup = getDefaultInheritedFormGroup(); - - let fieldConfigs: [string, EntitySchemaField][] = [ - [ - "field-2", - { - defaultValue: { - mode: "inherited", - field: "foo", - localAttribute: "reference-1", - }, - }, - ], - ]; - - let entity0 = new Entity(); - entity0["foo"] = "bar"; - mockEntityMapperService.load.and.returnValue(Promise.resolve(entity0)); - - // when - service.handleFormGroup(formGroup, fieldConfigs, false); - - // when/then - formGroup.get("reference-1").setValue("Entity:0"); - tick(); // fetching reference is always async - expect(formGroup.get("field-2").value).toBe(null); - })); - }); -}); diff --git a/src/app/core/entity/default-field-value/handle-default-values.usecase.ts b/src/app/core/entity/default-field-value/handle-default-values.usecase.ts deleted file mode 100644 index bc24383e28..0000000000 --- a/src/app/core/entity/default-field-value/handle-default-values.usecase.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Injectable } from "@angular/core"; -import { AbstractControl, FormGroup } from "@angular/forms"; -import { EntitySchemaField, PLACEHOLDERS } from "../schema/entity-schema-field"; -import { DefaultValueConfig } from "../schema/default-value-config"; -import { Entity } from "../model/entity"; -import { EntityMapperService } from "../entity-mapper/entity-mapper.service"; -import { Logging } from "../../logging/logging.service"; -import { CurrentUserSubject } from "../../session/current-user-subject"; - -/** - * When edit an Entity, apply this business logic for DefaultValueConfig - */ -@Injectable({ - providedIn: "root", -}) -export class HandleDefaultValuesUseCase { - constructor( - private entityMapper: EntityMapperService, - private currentUser: CurrentUserSubject, - ) {} - - handleFormGroup( - formGroup: FormGroup, - fieldConfigs: [string, EntitySchemaField][], - isNew: boolean, - ) { - fieldConfigs.forEach(([fieldName, fieldSchema]) => { - let defaultValueConfig = fieldSchema.defaultValue; - - switch (defaultValueConfig.mode) { - case "inherited": - return this.handleInheritedMode( - formGroup, - fieldName, - defaultValueConfig, - isNew, - fieldSchema.isArray, - ); - case "static": - return this.handleStaticMode( - formGroup, - fieldName, - defaultValueConfig, - isNew, - fieldSchema.isArray, - ); - case "dynamic": - return this.handleDynamicMode( - formGroup, - fieldName, - defaultValueConfig, - isNew, - fieldSchema.isArray, - ); - } - }); - } - - private handleInheritedMode( - formGroup: FormGroup, - fieldName: string, - defaultValueConfig: DefaultValueConfig, - isNew: boolean, - isArray: boolean, - ) { - let sourceFormControl: AbstractControl | null = formGroup.get( - defaultValueConfig.localAttribute, - ); - - let targetFormControl: AbstractControl | null = - formGroup.get(fieldName); - - if (!sourceFormControl || !targetFormControl) { - return; - } - - sourceFormControl.valueChanges.subscribe(async (change) => { - if (formGroup.disabled) { - return; - } - - if (targetFormControl.dirty && !!targetFormControl.value && isNew) { - return; - } - - if (!isNew) { - return; - } - - if (!change || "") { - targetFormControl.setValue(undefined); - return; - } - - // source field is array, use first element if only one element - if (Array.isArray(change)) { - if (change.length === 1) { - change = change[0]; - } else { - return; - } - } - - let parentEntity: Entity = await this.entityMapper.load( - Entity.extractTypeFromId(change), - change, - ); - - if (!parentEntity || !parentEntity[defaultValueConfig.field]) { - return; - } - - if (isArray) { - targetFormControl.setValue([...parentEntity[defaultValueConfig.field]]); - } else { - targetFormControl.setValue(parentEntity[defaultValueConfig.field]); - } - - targetFormControl.markAsUntouched(); - targetFormControl.markAsPristine(); - }); - } - - private handleStaticMode( - formGroup: FormGroup, - fieldName: string, - defaultValueConfig: DefaultValueConfig, - isNew: boolean, - isArray: boolean, - ) { - let targetFormControl = formGroup.get(fieldName); - - if (!this.preConditionsFulfilled(isNew, targetFormControl, isArray)) { - return; - } - - if (isArray) { - targetFormControl.setValue([defaultValueConfig.value]); - } else { - targetFormControl.setValue(defaultValueConfig.value); - } - } - - private handleDynamicMode( - formGroup: FormGroup, - fieldName: string, - defaultValueConfig: DefaultValueConfig, - isNew: boolean, - isArray: boolean, - ) { - let targetFormControl = formGroup.get(fieldName); - - if (!this.preConditionsFulfilled(isNew, targetFormControl, isArray)) { - return; - } - - switch (defaultValueConfig.value) { - case PLACEHOLDERS.NOW: - let now = new Date(); - if (isArray) { - targetFormControl.setValue([now]); - } else { - targetFormControl.setValue(now); - } - break; - case PLACEHOLDERS.CURRENT_USER: - let userId = this.currentUser.value?.getId(); - if (!userId) { - break; - } - - if (isArray) { - targetFormControl.setValue([userId]); - } else { - targetFormControl.setValue(userId); - } - break; - default: - Logging.warn( - "Unknown PLACEHOLDERS value used in fieldValueConfig: " + - defaultValueConfig, - ); - break; - } - } - - private preConditionsFulfilled( - isNew: boolean, - formControl: AbstractControl, - isArray: boolean, - ): boolean { - if (!isNew) { - return false; - } - - if (!formControl) { - return false; - } - - if (!isArray && !!formControl.value) { - return false; - } - - if (isArray && formControl.value && formControl.value.length > 0) { - return false; - } - - return true; - } -} diff --git a/src/app/core/entity/default-value.service.spec.ts b/src/app/core/entity/default-value.service.spec.ts deleted file mode 100644 index 09f9091db8..0000000000 --- a/src/app/core/entity/default-value.service.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { TestBed } from "@angular/core/testing"; - -import { DefaultValueService } from "./default-value.service"; -import { HandleDefaultValuesUseCase } from "./default-field-value/handle-default-values.usecase"; -import { FormBuilder, FormControl } from "@angular/forms"; -import { Entity } from "./model/entity"; -import { EntitySchemaField } from "./schema/entity-schema-field"; -import anything = jasmine.anything; - -describe("DefaultValueService", () => { - let service: DefaultValueService; - let mockHandleDefaultValuesUseCase: jasmine.SpyObj; - - beforeEach(() => { - mockHandleDefaultValuesUseCase = jasmine.createSpyObj({ - handleFormGroup: jasmine.createSpy(), - }); - mockHandleDefaultValuesUseCase.handleFormGroup.calls.saveArgumentsByValue(); - - TestBed.configureTestingModule({ - providers: [ - { - provide: HandleDefaultValuesUseCase, - useValue: mockHandleDefaultValuesUseCase, - }, - ], - }); - service = TestBed.inject(DefaultValueService); - }); - - it("should be created", () => { - expect(service).toBeTruthy(); - }); - - it("should call HandleDefaultValuesUseCase if defaultValue is set", () => { - // given - let formGroup = new FormBuilder().group({ test: {} }); - const schema: EntitySchemaField = { - defaultValue: { - mode: "static", - value: "bar", - }, - }; - Entity.schema.set("test", schema); - - // when - service.handle(formGroup, new Entity()); - - // then - expect(mockHandleDefaultValuesUseCase.handleFormGroup).toHaveBeenCalled(); - - Entity.schema.delete("test"); - }); - - // The inherited mode listen to changes and loads the entities async. - // When a static value is processed, before the inheritance hook is registered, the loading does not trigger. - it("should apply inherited modes before static and dynamic modes", () => { - // given - let formGroup = new FormBuilder().group({ - test1: new FormControl(), - test2: new FormControl(), - test3: new FormControl(), - test4: new FormControl(), - test5: new FormControl(), - }); - - Entity.schema.set("test1", { - defaultValue: { - mode: "static", - value: "bar", - }, - }); - - Entity.schema.set("test2", { - defaultValue: { - mode: "dynamic", - value: "bar", - }, - }); - - Entity.schema.set("test3", { - defaultValue: { - mode: "inherited", - value: "bar", - }, - }); - - Entity.schema.set("test4", { - defaultValue: { - mode: "dynamic", - value: "bar", - }, - }); - - Entity.schema.set("test5", { - defaultValue: { - mode: "inherited", - value: "bar", - }, - }); - - // when - service.handle(formGroup, new Entity()); - - // then - expect( - mockHandleDefaultValuesUseCase.handleFormGroup, - ).toHaveBeenCalledTimes(2); - - expect( - mockHandleDefaultValuesUseCase.handleFormGroup.calls.argsFor(0), - ).toEqual([ - anything(), - [ - ["test3", anything()], - ["test5", anything()], - ], - anything(), - ]); - - expect( - mockHandleDefaultValuesUseCase.handleFormGroup.calls.argsFor(1), - ).toEqual([ - anything(), - [ - ["test1", anything()], - ["test2", anything()], - ["test4", anything()], - ], - anything(), - ]); - - Entity.schema.delete("test1"); - Entity.schema.delete("test2"); - Entity.schema.delete("test3"); - Entity.schema.delete("test4"); - Entity.schema.delete("test5"); - }); -}); diff --git a/src/app/core/entity/default-value.service.ts b/src/app/core/entity/default-value.service.ts deleted file mode 100644 index 33b866b49c..0000000000 --- a/src/app/core/entity/default-value.service.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Injectable } from "@angular/core"; -import { Entity } from "./model/entity"; -import { FormGroup } from "@angular/forms"; -import { HandleDefaultValuesUseCase } from "./default-field-value/handle-default-values.usecase"; - -@Injectable({ - providedIn: "root", -}) -export class DefaultValueService { - constructor(private handleDefaultValuesUseCase: HandleDefaultValuesUseCase) {} - - handle(formGroup: FormGroup, entity: Entity): void { - let schema = entity.getSchema(); - - let defaultValueConfigs = Array.from(schema.entries()).filter( - ([key, fieldSchema]) => { - return fieldSchema.defaultValue; - }, - ); - - let inheritedConfigs = defaultValueConfigs.filter( - ([key, fieldSchema]) => fieldSchema.defaultValue.mode == "inherited", - ); - - let nonInheritedConfigs = defaultValueConfigs.filter( - ([key, fieldSchema]) => fieldSchema.defaultValue.mode != "inherited", - ); - - if (inheritedConfigs.length > 0) { - // apply inherited rules first, to be sure, that default values are reflected correctly - this.handleDefaultValuesUseCase.handleFormGroup( - formGroup, - inheritedConfigs, - entity.isNew, - ); - } - - if (nonInheritedConfigs.length > 0) { - this.handleDefaultValuesUseCase.handleFormGroup( - formGroup, - nonInheritedConfigs, - entity.isNew, - ); - } - } -} diff --git a/src/app/core/entity/entity-actions/entity-actions.service.spec.ts b/src/app/core/entity/entity-actions/entity-actions.service.spec.ts index fd54f91acf..fe5418ca7d 100644 --- a/src/app/core/entity/entity-actions/entity-actions.service.spec.ts +++ b/src/app/core/entity/entity-actions/entity-actions.service.spec.ts @@ -103,6 +103,7 @@ describe("EntityActionsService", () => { expect(snackBarSpy.open).toHaveBeenCalled(); expect(mockedEntityDeleteService.deleteEntity).toHaveBeenCalledWith( singleTestEntity, + true, ); expect(mockRouter.navigate).toHaveBeenCalled(); }); @@ -119,12 +120,15 @@ describe("EntityActionsService", () => { expect(mockedEntityDeleteService.deleteEntity).toHaveBeenCalledTimes(3); expect(mockedEntityDeleteService.deleteEntity).toHaveBeenCalledWith( severalTestEntities[0], + true, ); expect(mockedEntityDeleteService.deleteEntity).toHaveBeenCalledWith( severalTestEntities[1], + true, ); expect(mockedEntityDeleteService.deleteEntity).toHaveBeenCalledWith( severalTestEntities[2], + true, ); }); diff --git a/src/app/core/entity/entity-actions/entity-actions.service.ts b/src/app/core/entity/entity-actions/entity-actions.service.ts index 716b8b4d52..7fd7772ddc 100644 --- a/src/app/core/entity/entity-actions/entity-actions.service.ts +++ b/src/app/core/entity/entity-actions/entity-actions.service.ts @@ -102,9 +102,11 @@ export class EntityActionsService { $localize`:Entity action progress dialog:Processing ...`, ); let result = new CascadingActionResult(); + for (let entity of entities) { - result.mergeResults(await this.entityDelete.deleteEntity(entity)); + result.mergeResults(await this.entityDelete.deleteEntity(entity, true)); } + progressDialogRef.close(); if (result.potentiallyRetainingPII.length > 0) { diff --git a/src/app/core/entity/entity-actions/entity-anonymize.service.ts b/src/app/core/entity/entity-actions/entity-anonymize.service.ts index daacd9406a..02b6c0c47a 100644 --- a/src/app/core/entity/entity-actions/entity-anonymize.service.ts +++ b/src/app/core/entity/entity-actions/entity-anonymize.service.ts @@ -20,8 +20,8 @@ import { asArray } from "../../../utils/utils"; }) export class EntityAnonymizeService extends CascadingEntityAction { constructor( - protected entityMapper: EntityMapperService, - protected schemaService: EntitySchemaService, + protected override entityMapper: EntityMapperService, + protected override schemaService: EntitySchemaService, private fileService: FileService, ) { super(entityMapper, schemaService); diff --git a/src/app/core/entity/entity-actions/entity-delete.service.spec.ts b/src/app/core/entity/entity-actions/entity-delete.service.spec.ts index b2cad683d4..dcf757fd14 100644 --- a/src/app/core/entity/entity-actions/entity-delete.service.spec.ts +++ b/src/app/core/entity/entity-actions/entity-delete.service.spec.ts @@ -16,27 +16,58 @@ import { } from "./cascading-entity-action.spec"; import { expectEntitiesToMatch } from "../../../utils/expect-entity-data.spec"; import { Note } from "../../../child-dev-project/notes/model/note"; -import { Child } from "../../../child-dev-project/children/model/child"; import { DefaultDatatype } from "../default-datatype/default.datatype"; import { EventAttendanceMapDatatype } from "../../../child-dev-project/attendance/model/event-attendance.datatype"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { KeycloakAuthService } from "../../session/auth/keycloak/keycloak-auth.service"; +import { throwError } from "rxjs"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { ConfirmationDialogService } from "../../common-components/confirmation-dialog/confirmation-dialog.service"; +import { createEntityOfType } from "../../demo-data/create-entity-of-type"; describe("EntityDeleteService", () => { let service: EntityDeleteService; let entityMapper: MockEntityMapperService; + let snackBarSpy: jasmine.SpyObj; + let mockConfirmationDialog: jasmine.SpyObj; + let mockAuthService: jasmine.SpyObj; + beforeEach(() => { entityMapper = mockEntityMapper(allEntities.map((e) => e.copy())); + mockAuthService = jasmine.createSpyObj(["deleteUser"]); + mockAuthService.deleteUser.and.returnValue( + throwError(() => { + new Error(); + }), + ); + + mockConfirmationDialog = jasmine.createSpyObj([ + "getConfirmation", + "showProgressDialog", + ]); + mockConfirmationDialog.getConfirmation.and.resolveTo(true); + mockConfirmationDialog.showProgressDialog.and.returnValue( + jasmine.createSpyObj(["close"]), + ); + TestBed.configureTestingModule({ imports: [CoreTestingModule], providers: [ EntityDeleteService, { provide: EntityMapperService, useValue: entityMapper }, + { provide: KeycloakAuthService, useValue: mockAuthService }, + { provide: MatSnackBar, useValue: snackBarSpy }, { provide: DefaultDatatype, useClass: EventAttendanceMapDatatype, multi: true, }, + { + provide: ConfirmationDialogService, + useValue: mockConfirmationDialog, + }, ], }); @@ -81,6 +112,18 @@ describe("EntityDeleteService", () => { ); }); + it("should delete several entities and show dialog if keycloak deletion fails", async () => { + // given + mockAuthService.deleteUser.and.returnValue(throwError(() => new Error())); + let userEntity = createEntityOfType("User"); + + // when + await service.deleteEntity(userEntity, true); + + // then + expect(mockConfirmationDialog.getConfirmation).toHaveBeenCalledTimes(1); + }); + it("should not cascade delete the 'composite'-type entity that still references additional other entities but remove id", async () => { const result = await service.deleteEntity( ENTITIES.ReferencedAsOneOfMultipleComposites1, @@ -178,12 +221,15 @@ describe("EntityDeleteService", () => { it("should remove multiple ref ids from related note", async () => { const schemaField = Note.schema.get("relatedEntities"); const originalSchemaAdditional = schemaField.additional; - schemaField.additional = [Child.ENTITY_TYPE]; + schemaField.additional = [TestEntity.ENTITY_TYPE]; + const schemaField2 = Note.schema.get("children"); + const originalSchema2Additional = schemaField2.additional; + schemaField2.additional = TestEntity.ENTITY_TYPE; - const primary = new Child(); + const primary = new TestEntity(); const note = new Note(); note.subject = "test"; - note.children = [primary.getId(), "Child:some-other"]; + note.children = [primary.getId(), TestEntity.ENTITY_TYPE + ":some-other"]; note.relatedEntities = [primary.getId()]; const originalNote = note.copy(); await entityMapper.save(primary); @@ -194,7 +240,9 @@ describe("EntityDeleteService", () => { const actualNote = entityMapper.get(Note.ENTITY_TYPE, note.getId()) as Note; expect(actualNote.relatedEntities).toEqual([]); - expect(actualNote.children).toEqual(["Child:some-other"]); + expect(actualNote.children).toEqual([ + TestEntity.ENTITY_TYPE + ":some-other", + ]); expect(result.originalEntitiesBeforeChange.length).toBe(2); expectEntitiesToMatch(result.originalEntitiesBeforeChange, [ @@ -204,5 +252,6 @@ describe("EntityDeleteService", () => { // restore original schema schemaField.additional = originalSchemaAdditional; + schemaField2.additional = originalSchema2Additional; }); }); diff --git a/src/app/core/entity/entity-actions/entity-delete.service.ts b/src/app/core/entity/entity-actions/entity-delete.service.ts index 55c62bc823..8df198085b 100644 --- a/src/app/core/entity/entity-actions/entity-delete.service.ts +++ b/src/app/core/entity/entity-actions/entity-delete.service.ts @@ -6,6 +6,9 @@ import { CascadingActionResult, CascadingEntityAction, } from "./cascading-entity-action"; +import { OkButton } from "../../common-components/confirmation-dialog/confirmation-dialog/confirmation-dialog.component"; +import { KeycloakAuthService } from "../../session/auth/keycloak/keycloak-auth.service"; +import { ConfirmationDialogService } from "../../common-components/confirmation-dialog/confirmation-dialog.service"; /** * Safely delete an entity including handling references with related entities. @@ -16,8 +19,10 @@ import { }) export class EntityDeleteService extends CascadingEntityAction { constructor( - protected entityMapper: EntityMapperService, - protected schemaService: EntitySchemaService, + protected override entityMapper: EntityMapperService, + protected override schemaService: EntitySchemaService, + private keycloakAuthService: KeycloakAuthService, + private confirmationDialog: ConfirmationDialogService, ) { super(entityMapper, schemaService); } @@ -29,9 +34,28 @@ export class EntityDeleteService extends CascadingEntityAction { * to support an undo action. * * @param entity + * @param showKeycloakWarning if keycloak deleteUser request fails, a popup warning is shown to the user * @private */ - async deleteEntity(entity: Entity): Promise { + async deleteEntity( + entity: Entity, + showKeycloakWarning = false, + ): Promise { + if ("User" === entity.getType()) { + this.keycloakAuthService.deleteUser(entity.getId()).subscribe({ + next: () => {}, + error: () => { + if (showKeycloakWarning) { + this.confirmationDialog.getConfirmation( + $localize`:delete account in keycloak related error title:Keycloak User could not be deleted`, + $localize`:delete account in keycloak related error dialog:User Account could not be deleted in Keycloak. Please delete user manually in Keycloak.`, + OkButton, + ); + } + }, + }); + } + const cascadeResult = await this.cascadeActionToRelatedEntities( entity, (e) => this.deleteEntity(e), diff --git a/src/app/core/entity/entity-config.service.spec.ts b/src/app/core/entity/entity-config.service.spec.ts index 8f5e21bfe0..52aaa341be 100644 --- a/src/app/core/entity/entity-config.service.spec.ts +++ b/src/app/core/entity/entity-config.service.spec.ts @@ -14,7 +14,7 @@ import { EntityMapperService } from "./entity-mapper/entity-mapper.service"; import { mockEntityMapper } from "./entity-mapper/mock-entity-mapper-service"; import { EntityConfig } from "./entity-config"; import { EntitySchemaField } from "./schema/entity-schema-field"; -import { Child } from "../../child-dev-project/children/model/child"; +import { TestEntity } from "../../utils/test-utils/TestEntity"; describe("EntityConfigService", () => { let service: EntityConfigService; @@ -84,29 +84,29 @@ describe("EntityConfigService", () => { }); it("should reset attribute to basic class config if custom attribute disappears from config doc", () => { - const originalLabel = Child.schema.get("name").label; + const originalLabel = TestEntity.schema.get("name").label; const customLabel = "custom label"; const mockEntityConfigs: (EntityConfig & { _id: string })[] = [ { - _id: "entity:Child", + _id: "entity:" + TestEntity.ENTITY_TYPE, attributes: { name: { label: customLabel } }, }, ]; mockConfigService.getAllConfigs.and.returnValue(mockEntityConfigs); service.setupEntitiesFromConfig(); - expect(Child.schema.get("name").label).toEqual(customLabel); + expect(TestEntity.schema.get("name").label).toEqual(customLabel); mockConfigService.getAllConfigs.and.returnValue([ { - _id: "entity:Child", + _id: "entity:" + TestEntity.ENTITY_TYPE, attributes: { /* undo custom label */ }, }, ]); service.setupEntitiesFromConfig(); - expect(Child.schema.get("name").label).toEqual(originalLabel); + expect(TestEntity.schema.get("name").label).toEqual(originalLabel); }); it("should allow to configure the `.toString` method", () => { diff --git a/src/app/core/entity/entity-config.service.ts b/src/app/core/entity/entity-config.service.ts index 0eed6dbd00..70aeeeab6d 100644 --- a/src/app/core/entity/entity-config.service.ts +++ b/src/app/core/entity/entity-config.service.ts @@ -26,6 +26,7 @@ export class EntityConfigService { /** @deprecated will become private, use the service to access the data */ static readonly PREFIX_ENTITY_CONFIG = "entity:"; + // TODO remove this? /** original initial entity schemas without overrides from config */ private coreEntitySchemas = new Map(); @@ -86,8 +87,8 @@ export class EntityConfigService { const schema = this.deepCopySchema(parentClass.schema); class DynamicClass extends parentClass { - static schema = schema; - static ENTITY_TYPE = id; + static override schema = schema; + static override ENTITY_TYPE = id; } this.entities.set(id, DynamicClass); @@ -127,7 +128,7 @@ export class EntityConfigService { ) { const entityConfig = configAttributes || this.getEntityConfig(entityType); for (const [key, value] of Object.entries(entityConfig?.attributes ?? {})) { - value._isCustomizedField = true; + delete value["_isCustomizedField"]; // clean up previous flag that is not deprecated addPropertySchema(entityType.prototype, key, value); } diff --git a/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts b/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts index 4bd176b2c6..c320d321ea 100644 --- a/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts +++ b/src/app/core/entity/entity-mapper/entity-mapper.service.spec.ts @@ -23,7 +23,6 @@ import { DatabaseEntity } from "../database-entity.decorator"; import { Database } from "../../database/database"; import { CoreTestingModule } from "../../../utils/core-testing.module"; import { CurrentUserSubject } from "../../session/current-user-subject"; -import { User } from "../../user/user"; import { TEST_USER } from "../../user/demo-user-generator.service"; describe("EntityMapperService", () => { @@ -94,11 +93,11 @@ describe("EntityMapperService", () => { }); it("returns empty array when loading non existing entity type ", async () => { - class TestEntity extends Entity { - static ENTITY_TYPE = "TestEntity"; + class TempTestEntity extends Entity { + static override ENTITY_TYPE = "TestEntity"; } - const result = await entityMapper.loadType(TestEntity); + const result = await entityMapper.loadType(TempTestEntity); expect(result).toBeEmpty(); }); @@ -255,7 +254,8 @@ describe("EntityMapperService", () => { it("sets the entityCreated property on save if it is a new entity & entityUpdated on subsequent saves", async () => { jasmine.clock().install(); - TestBed.inject(CurrentUserSubject).next(new User(TEST_USER)); + const currentUser = new Entity(TEST_USER); + TestBed.inject(CurrentUserSubject).next(currentUser); const id = "test_created"; const entity = new Entity(id); @@ -265,13 +265,9 @@ describe("EntityMapperService", () => { const createdEntity = await entityMapper.load(Entity, id); expect(createdEntity.created?.at.getTime()).toEqual(mockTime1); - expect(createdEntity.created?.by).toEqual( - `${User.ENTITY_TYPE}:${TEST_USER}`, - ); + expect(createdEntity.created?.by).toEqual(currentUser.getId()); expect(createdEntity.updated?.at.getTime()).toEqual(mockTime1); - expect(createdEntity.updated?.by).toEqual( - `${User.ENTITY_TYPE}:${TEST_USER}`, - ); + expect(createdEntity.updated?.by).toEqual(currentUser.getId()); const mockTime2 = mockTime1 + 1; jasmine.clock().mockDate(new Date(mockTime2)); diff --git a/src/app/core/entity/entity-mapper/mock-entity-mapper-service.spec.ts b/src/app/core/entity/entity-mapper/mock-entity-mapper-service.spec.ts index d743dd52b8..583aa23a28 100644 --- a/src/app/core/entity/entity-mapper/mock-entity-mapper-service.spec.ts +++ b/src/app/core/entity/entity-mapper/mock-entity-mapper-service.spec.ts @@ -2,8 +2,8 @@ import { mockEntityMapper, MockEntityMapperService, } from "./mock-entity-mapper-service"; -import { Child } from "../../../child-dev-project/children/model/child"; import { expectObservable } from "../../../utils/test-utils/observable-utils"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("MockEntityMapperServicer", () => { let service: MockEntityMapperService; @@ -12,20 +12,20 @@ describe("MockEntityMapperServicer", () => { }); it("should publish a update for a newly added entity", (done) => { - const child = new Child(); + const child = new TestEntity(); - expectObservable(service.receiveUpdates(Child)) + expectObservable(service.receiveUpdates(TestEntity)) .first.toBeResolvedTo({ type: "new", entity: child }) .then(() => done()); service.add(child); }); it("should publish a update for a already existing entities", (done) => { - const child = new Child(); + const child = new TestEntity(); service.add(child); child.name = "Updated name"; - expectObservable(service.receiveUpdates(Child)) + expectObservable(service.receiveUpdates(TestEntity)) .first.toBeResolvedTo({ type: "update", entity: child }) .then(() => done()); service.add(child); diff --git a/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts b/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts index dc9d9cfbc6..d14893e1aa 100644 --- a/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts +++ b/src/app/core/entity/entity-mapper/mock-entity-mapper-service.ts @@ -113,40 +113,56 @@ export class MockEntityMapperService extends EntityMapperService { } } - public async load( + public override async load( entityType: EntityConstructor | string, id: string, ): Promise { - const ctor = this.resolveConstructor(entityType); - const type = new ctor().getType(); + let type = this.getTypeViaRegistry(entityType); return this.get(type, id) as T; } - async loadType( + override async loadType( entityType: EntityConstructor | string, ): Promise { - const ctor = this.resolveConstructor(entityType); - const type = new ctor().getType(); + let type = this.getTypeViaRegistry(entityType); return this.getAll(type); } - async save( + private getTypeViaRegistry(entityType: EntityConstructor | string): string { + let type: string; + try { + const ctor = this.resolveConstructor(entityType); + type = new ctor().getType(); + } catch (e) { + console.error(e); + } + if (!type && typeof entityType === "string") { + console.warn( + "No constructor found for type; fallback for MockEntityMapper still allows to load", + entityType, + ); + type = entityType; + } + return type; + } + + override async save( entity: T, forceUpdate: boolean = false, ): Promise { this.add(entity); } - async saveAll(entities: Entity[]): Promise { + override async saveAll(entities: Entity[]): Promise { this.addAll(entities); } - remove(entity: T): Promise { + override remove(entity: T): Promise { this.delete(entity); return Promise.resolve(); } - receiveUpdates( + override receiveUpdates( entityType: EntityConstructor | string, ): Observable> { let name = diff --git a/src/app/core/entity/entity-special-loader/entity-special-loader.service.spec.ts b/src/app/core/entity/entity-special-loader/entity-special-loader.service.spec.ts new file mode 100644 index 0000000000..80ab89b683 --- /dev/null +++ b/src/app/core/entity/entity-special-loader/entity-special-loader.service.spec.ts @@ -0,0 +1,47 @@ +import { TestBed } from "@angular/core/testing"; + +import { + EntitySpecialLoaderService, + LoaderMethod, +} from "./entity-special-loader.service"; +import { ChildrenService } from "../../../child-dev-project/children/children.service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { HistoricalDataService } from "./historical-data/historical-data.service"; + +describe("EntitySpecialLoaderService", () => { + let service: EntitySpecialLoaderService; + + let mockChildrenService: jasmine.SpyObj; + let mockHistoricalDataService: jasmine.SpyObj; + + beforeEach(() => { + mockChildrenService = jasmine.createSpyObj(ChildrenService, [ + "getChildren", + ]); + mockHistoricalDataService = jasmine.createSpyObj(HistoricalDataService, [ + "getHistoricalDataFor", + ]); + + TestBed.configureTestingModule({ + providers: [ + { provide: ChildrenService, useValue: mockChildrenService }, + { provide: HistoricalDataService, useValue: mockHistoricalDataService }, + ], + }); + service = TestBed.inject(EntitySpecialLoaderService); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + it("should load via ChildrenService", async () => { + const testData = [new TestEntity()] as any[]; + mockChildrenService.getChildren.and.resolveTo(testData); + + const actual = await service.loadData(LoaderMethod.ChildrenService); + + expect(actual).toEqual(testData); + expect(mockChildrenService.getChildren).toHaveBeenCalled(); + }); +}); diff --git a/src/app/core/entity/entity-special-loader/entity-special-loader.service.ts b/src/app/core/entity/entity-special-loader/entity-special-loader.service.ts new file mode 100644 index 0000000000..3e64862e3d --- /dev/null +++ b/src/app/core/entity/entity-special-loader/entity-special-loader.service.ts @@ -0,0 +1,43 @@ +import { Injectable } from "@angular/core"; +import { Entity } from "../model/entity"; +import { ChildrenService } from "../../../child-dev-project/children/children.service"; +import { HistoricalDataService } from "./historical-data/historical-data.service"; + +export enum LoaderMethod { + ChildrenService = "ChildrenService", + HistoricalDataService = "HistoricalDataService", +} + +/** + * Load data in a specially combined or indexed way as an alternative to the simple EntityMapperService. + * + * This service might be refactored or removed when generic configurable indexes are implemented (#581, #262) + */ +@Injectable({ + providedIn: "root", +}) +export class EntitySpecialLoaderService { + constructor( + private childrenService: ChildrenService, + private historicalDataService: HistoricalDataService, + ) {} + + loadData( + loaderMethod: LoaderMethod, + ): Promise { + if (loaderMethod === LoaderMethod.ChildrenService) { + return this.childrenService.getChildren() as Promise; + } + } + + async loadDataFor( + loaderMethod: LoaderMethod, + entity: Entity, + ): Promise { + if (loaderMethod === LoaderMethod.HistoricalDataService) { + return this.historicalDataService.getHistoricalDataFor( + entity.getId(), + ) as Promise; + } + } +} diff --git a/src/app/features/historical-data/historical-data.service.spec.ts b/src/app/core/entity/entity-special-loader/historical-data/historical-data.service.spec.ts similarity index 70% rename from src/app/features/historical-data/historical-data.service.spec.ts rename to src/app/core/entity/entity-special-loader/historical-data/historical-data.service.spec.ts index 7eaddb9e72..7b64caa059 100644 --- a/src/app/features/historical-data/historical-data.service.spec.ts +++ b/src/app/core/entity/entity-special-loader/historical-data/historical-data.service.spec.ts @@ -1,13 +1,12 @@ import { TestBed, waitForAsync } from "@angular/core/testing"; import { HistoricalDataService } from "./historical-data.service"; -import { EntityMapperService } from "../../core/entity/entity-mapper/entity-mapper.service"; -import { Entity } from "../../core/entity/model/entity"; -import { HistoricalEntityData } from "./model/historical-entity-data"; -import { expectEntitiesToMatch } from "../../utils/expect-entity-data.spec"; -import { Database } from "../../core/database/database"; +import { EntityMapperService } from "../../entity-mapper/entity-mapper.service"; +import { Entity } from "../../model/entity"; +import { Database } from "../../../database/database"; import moment from "moment"; -import { DatabaseTestingModule } from "../../utils/database-testing.module"; +import { DatabaseTestingModule } from "../../../../utils/database-testing.module"; +import { createEntityOfType } from "../../../demo-data/create-entity-of-type"; describe("HistoricalDataService", () => { let service: HistoricalDataService; @@ -28,31 +27,31 @@ describe("HistoricalDataService", () => { it("should load data related to a entity", async () => { const entityMapper = TestBed.inject(EntityMapperService); const entity = new Entity(); - const relatedData = new HistoricalEntityData(); + const relatedData = createEntityOfType("HistoricalEntityData"); relatedData.relatedEntity = entity.getId(); - const unrelatedData = new HistoricalEntityData(); + const unrelatedData = createEntityOfType("HistoricalEntityData"); unrelatedData.relatedEntity = "anotherEntity"; await entityMapper.save(relatedData); await entityMapper.save(unrelatedData); const results = await service.getHistoricalDataFor(entity.getId()); - expectEntitiesToMatch(results, [relatedData]); + expect(results.map((x) => x.getId())).toEqual([relatedData.getId()]); }); it("should return the historical data sorted by date", async () => { const entityMapper = TestBed.inject(EntityMapperService); const entity = new Entity(); - const firstData = new HistoricalEntityData(); + const firstData = createEntityOfType("HistoricalEntityData"); firstData.relatedEntity = entity.getId(); firstData.date = new Date(); - const secondData = new HistoricalEntityData(); + const secondData = createEntityOfType("HistoricalEntityData"); secondData.relatedEntity = entity.getId(); secondData.date = moment().subtract(1, "day").toDate(); - const thirdData = new HistoricalEntityData(); + const thirdData = createEntityOfType("HistoricalEntityData"); thirdData.relatedEntity = entity.getId(); thirdData.date = moment().subtract(10, "days").toDate(); - const unrelatedData = new HistoricalEntityData(); + const unrelatedData = createEntityOfType("HistoricalEntityData"); unrelatedData.relatedEntity = "anotherEntity"; unrelatedData.date = moment().subtract(2, "days").toDate(); await entityMapper.save(firstData); diff --git a/src/app/features/historical-data/historical-data.service.ts b/src/app/core/entity/entity-special-loader/historical-data/historical-data.service.ts similarity index 56% rename from src/app/features/historical-data/historical-data.service.ts rename to src/app/core/entity/entity-special-loader/historical-data/historical-data.service.ts index 3f7b78d8be..60c3562c79 100644 --- a/src/app/features/historical-data/historical-data.service.ts +++ b/src/app/core/entity/entity-special-loader/historical-data/historical-data.service.ts @@ -1,12 +1,19 @@ import { Injectable } from "@angular/core"; -import { HistoricalEntityData } from "./model/historical-entity-data"; -import { DatabaseIndexingService } from "../../core/entity/database-indexing/database-indexing.service"; +import { DatabaseIndexingService } from "../../database-indexing/database-indexing.service"; +import { Entity } from "../../model/entity"; +import { EntityRegistry } from "../../database-entity.decorator"; +/** + * @deprecated Will be replaced by generic index generation (#262) + */ @Injectable({ providedIn: "root", }) export class HistoricalDataService { - constructor(private dbIndexingService: DatabaseIndexingService) { + constructor( + private dbIndexingService: DatabaseIndexingService, + private entityRegistry: EntityRegistry, + ) { this.createHistoricalDataIndex(); } @@ -16,7 +23,7 @@ export class HistoricalDataService { views: { by_entity: { map: `(doc) => { - if (doc._id.startsWith("${HistoricalEntityData.ENTITY_TYPE}")) { + if (doc._id.startsWith("HistoricalEntityData")) { emit([doc.relatedEntity, new Date(doc.date).getTime()]); } }`, @@ -26,9 +33,9 @@ export class HistoricalDataService { return this.dbIndexingService.createIndex(designDoc); } - getHistoricalDataFor(entityId: string): Promise { + getHistoricalDataFor(entityId: string): Promise { return this.dbIndexingService.queryIndexDocs( - HistoricalEntityData, + this.entityRegistry.get("HistoricalEntityData"), "historicalData_index/by_entity", { startkey: [entityId, "\uffff"], diff --git a/src/app/core/entity/model/entity-update.spec.ts b/src/app/core/entity/model/entity-update.spec.ts index 8a043cfb4f..f944fc71d9 100644 --- a/src/app/core/entity/model/entity-update.spec.ts +++ b/src/app/core/entity/model/entity-update.spec.ts @@ -1,7 +1,7 @@ import { Entity } from "./entity"; import { applyUpdate } from "./entity-update"; -class TestEntity extends Entity { +class TestUpdateEntity extends Entity { value: number; constructor(id: string, value: number) { @@ -9,23 +9,23 @@ class TestEntity extends Entity { this.value = value; } - getType(): string { + override getType(): string { return "TestEntity"; } } describe("entity-update", () => { - let existingEntities: TestEntity[] = []; + let existingEntities: TestUpdateEntity[] = []; beforeEach(() => { existingEntities = ["n1", "n2", "n3", "n5"].map( - (id) => new TestEntity(id, 1), + (id) => new TestUpdateEntity(id, 1), ); }); it("updates the entity-list when a new entity should be inserted", () => { - const newEntities = applyUpdate(existingEntities, { - entity: new TestEntity("n6", 1), + const newEntities = applyUpdate(existingEntities, { + entity: new TestUpdateEntity("n6", 1), type: "new", }); expect(newEntities).toHaveSize(existingEntities.length + 1); @@ -34,8 +34,8 @@ describe("entity-update", () => { it("updates the entity-list when an existing entity should be updated", () => { const indexOfN2 = existingEntities.findIndex((e) => e.getId(true) === "n2"); - const newEntities = applyUpdate(existingEntities, { - entity: new TestEntity("n2", 2), + const newEntities = applyUpdate(existingEntities, { + entity: new TestUpdateEntity("n2", 2), type: "update", }); expect(newEntities).toHaveSize(existingEntities.length); @@ -44,8 +44,8 @@ describe("entity-update", () => { it("deletes an element from the entity-list when an entity should be deleted", () => { const oldLength = existingEntities.length; - const newEntities = applyUpdate(existingEntities, { - entity: new TestEntity("n2", 3), + const newEntities = applyUpdate(existingEntities, { + entity: new TestUpdateEntity("n2", 3), type: "remove", }); expect(newEntities).toHaveSize(oldLength - 1); @@ -53,12 +53,15 @@ describe("entity-update", () => { }); it("does not change the list when the passed updated-entity is illegal", () => { - const newEntities = applyUpdate(existingEntities, undefined); + const newEntities = applyUpdate( + existingEntities, + undefined, + ); expect(newEntities).toEqual(existingEntities); }); it("does not change the list when the passed entity is illegal", () => { - const newEntities = applyUpdate(existingEntities, { + const newEntities = applyUpdate(existingEntities, { entity: undefined, type: "new", }); @@ -66,10 +69,10 @@ describe("entity-update", () => { }); it("does not change the list when an updated entity is not in the list", () => { - const newEntities = applyUpdate( + const newEntities = applyUpdate( existingEntities, { - entity: new TestEntity("n6", 1), + entity: new TestUpdateEntity("n6", 1), type: "update", }, false, @@ -79,19 +82,19 @@ describe("entity-update", () => { it("does not mutate the original array", () => { const original = [...existingEntities]; - applyUpdate(existingEntities, { - entity: new TestEntity("n7", 1), + applyUpdate(existingEntities, { + entity: new TestUpdateEntity("n7", 1), type: "new", }); - applyUpdate(existingEntities, { - entity: new TestEntity("n2", 10), + applyUpdate(existingEntities, { + entity: new TestUpdateEntity("n2", 10), type: "update", }); - applyUpdate(existingEntities, { - entity: new TestEntity("n1", 0), + applyUpdate(existingEntities, { + entity: new TestUpdateEntity("n1", 0), type: "remove", }); - applyUpdate(existingEntities, undefined); + applyUpdate(existingEntities, undefined); expect(existingEntities).toEqual(original); }); }); diff --git a/src/app/core/entity/model/entity.spec.ts b/src/app/core/entity/model/entity.spec.ts index 63673b3142..4f27e519ec 100644 --- a/src/app/core/entity/model/entity.spec.ts +++ b/src/app/core/entity/model/entity.spec.ts @@ -1,20 +1,3 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - import { Entity, EntityConstructor } from "./entity"; import { EntitySchemaService } from "../schema/entity-schema.service"; import { DatabaseField } from "../database-field.decorator"; @@ -34,14 +17,14 @@ describe("Entity", () => { it("rawData() returns only data matching the schema", function () { @DatabaseEntity("TestWithIgnoredFieldEntity") - class TestEntity extends Entity { + class TestWithIgnoredFieldEntity extends Entity { @DatabaseField() text: string = "text"; @DatabaseField() defaultText: string = "default"; otherText: string = "other Text"; } const id = "test1"; - const entity = new TestEntity(id); + const entity = new TestWithIgnoredFieldEntity(id); const data = entitySchemaService.transformEntityToDatabaseFormat(entity); @@ -120,18 +103,18 @@ describe("Entity", () => { it("should convert toString using toStringAttributes config or special [anonymized] label", () => { @DatabaseEntity("TestEntityForToString") - class TestEntity extends Entity { - static toStringAttributes = ["firstname", "lastname"]; - static label = "TestEntity"; + class TestEntityForToString extends Entity { + static override toStringAttributes = ["firstname", "lastname"]; + static override label = "TestEntity"; firstname = "John"; lastname = "Doe"; } - const testEntity = new TestEntity(); + const testEntity = new TestEntityForToString(); expect(testEntity.toString()).toBe("John Doe"); - const anonymizedEntity = new TestEntity(); + const anonymizedEntity = new TestEntityForToString(); anonymizedEntity.firstname = undefined; delete anonymizedEntity.lastname; anonymizedEntity.anonymized = true; diff --git a/src/app/core/entity/model/entity.ts b/src/app/core/entity/model/entity.ts index 60a93a4858..62e1df0023 100644 --- a/src/app/core/entity/model/entity.ts +++ b/src/app/core/entity/model/entity.ts @@ -162,9 +162,7 @@ export class Entity { } } - static getBlockComponent(): string { - return; - } + static blockComponent?: string; /** * whether this entity type can contain "personally identifiable information" (PII) diff --git a/src/app/core/entity/schema/entity-schema-field.ts b/src/app/core/entity/schema/entity-schema-field.ts index de250139dc..4ee72fdbb8 100644 --- a/src/app/core/entity/schema/entity-schema-field.ts +++ b/src/app/core/entity/schema/entity-schema-field.ts @@ -127,14 +127,6 @@ export interface EntitySchemaField { * "retain-anonymized" triggers a special dataType action to retain the data partially in a special, anonymized form. */ anonymize?: "retain" | "retain-anonymized"; - - /** - * indicates that this field has been created/modified through the config system - * and is not matching the original entity class definition. - * - * Set automatically during config initialization. - */ - _isCustomizedField?: boolean; } /** diff --git a/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts b/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts index f025a88fc7..bd0c9793a1 100644 --- a/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts +++ b/src/app/core/export/data-transformation-service/data-transformation.service.spec.ts @@ -4,14 +4,15 @@ import { DataTransformationService } from "./data-transformation.service"; import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service"; import { Database } from "../../database/database"; import { Note } from "../../../child-dev-project/notes/model/note"; -import { Child } from "../../../child-dev-project/children/model/child"; -import { School } from "../../../child-dev-project/schools/model/school"; import { ChildSchoolRelation } from "../../../child-dev-project/children/model/childSchoolRelation"; import { ExportColumnConfig } from "./export-column-config"; import { defaultAttendanceStatusTypes } from "../../config/default-config/default-attendance-status-types"; import moment from "moment"; import { DatabaseTestingModule } from "../../../utils/database-testing.module"; import { RecurringActivity } from "../../../child-dev-project/attendance/model/recurring-activity"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { Entity } from "../../entity/model/entity"; +import { createEntityOfType } from "../../demo-data/create-entity-of-type"; describe("DataTransformationService", () => { let service: DataTransformationService; @@ -56,12 +57,12 @@ describe("DataTransformationService", () => { it("should load fields from related entity for joint export", async () => { const child1 = await createChildInDB("John"); const child2 = await createChildInDB("Jane"); - const school1 = await createSchoolInDB("School with student", [child1]); - const school2 = await createSchoolInDB("School without student", []); - const school3 = await createSchoolInDB("School with multiple students", [ - child1, - child2, - ]); + const school1 = await createTestEntityInDB("School with student", [child1]); + const school2 = await createTestEntityInDB("School without student", []); + const school3 = await createTestEntityInDB( + "School with multiple students", + [child1, child2], + ); const query1 = ":getRelated(ChildSchoolRelation, schoolId).childId:toEntities(Child).name"; @@ -170,7 +171,7 @@ describe("DataTransformationService", () => { it("should not omit rows where the subQueries are run on an empty array", async () => { const childWithoutSchool = await createChildInDB("child without school"); const childWithSchool = await createChildInDB("child with school"); - const school = await createSchoolInDB("test school", [childWithSchool]); + const school = await createTestEntityInDB("test school", [childWithSchool]); const note = await createNoteInDB( "Note", [childWithoutSchool, childWithSchool], @@ -188,7 +189,7 @@ describe("DataTransformationService", () => { query: ".participant:toEntities(Child).name", }, { - query: ".school:toEntities(School)", + query: ".school:toEntities(TestEntity)", subQueries: [ { label: "Name", query: "name" }, { label: "school_id", query: "entityId" }, @@ -308,8 +309,8 @@ describe("DataTransformationService", () => { }); it("should work when using the count function", async () => { - await createNoteInDB("first", [new Child(), new Child()]); - await createNoteInDB("second", [new Child()]); + await createNoteInDB("first", [new TestEntity(), new TestEntity()]); + await createNoteInDB("second", [new TestEntity()]); const result = await service.queryAndTransformData([ { @@ -330,13 +331,13 @@ describe("DataTransformationService", () => { }); it("should allow to group results", async () => { - await createSchoolInDB("sameName"); - await createSchoolInDB("sameName"); - await createSchoolInDB("otherName"); + await createTestEntityInDB("sameName"); + await createTestEntityInDB("sameName"); + await createTestEntityInDB("otherName"); const result = await service.queryAndTransformData([ { - query: `${School.ENTITY_TYPE}:toArray`, + query: `${TestEntity.ENTITY_TYPE}:toArray`, groupBy: { label: "Name", property: "name" }, subQueries: [{ query: ":count", label: "Amount" }], }, @@ -361,11 +362,11 @@ describe("DataTransformationService", () => { label: "Group", }, { - query: `${Child.ENTITY_TYPE}:toArray:count`, + query: `Child:toArray:count`, label: "Count", }, { - query: `${Child.ENTITY_TYPE}:toArray`, + query: `Child:toArray`, groupBy: { label: "Group", property: "name" }, subQueries: [{ query: ":count", label: "Count" }], }, @@ -380,8 +381,8 @@ describe("DataTransformationService", () => { ); }); - async function createChildInDB(name: string): Promise { - const child = new Child(); + async function createChildInDB(name: string): Promise { + const child = createEntityOfType("Child"); child.name = name; await entityMapper.save(child); return child; @@ -389,7 +390,7 @@ describe("DataTransformationService", () => { async function createNoteInDB( subject: string, - children: Child[] = [], + children: Entity[] = [], attendanceStatus: string[] = [], ): Promise { const note = new Note(); @@ -405,11 +406,11 @@ describe("DataTransformationService", () => { return note; } - async function createSchoolInDB( + async function createTestEntityInDB( schoolName: string, - students: Child[] = [], - ): Promise { - const school = new School(); + students: Entity[] = [], + ): Promise { + const school = new TestEntity(); school.name = schoolName; await entityMapper.save(school); @@ -426,8 +427,8 @@ describe("DataTransformationService", () => { async function createActivityInDB( activityTitle: string, - participants: Child[] = [], - groups: School[] = [], + participants: Entity[] = [], + groups: TestEntity[] = [], ): Promise { const activity = new RecurringActivity(); activity.title = activityTitle; diff --git a/src/app/core/export/download-service/download.service.spec.ts b/src/app/core/export/download-service/download.service.spec.ts index 89a277d69a..cc46ead9f7 100644 --- a/src/app/core/export/download-service/download.service.spec.ts +++ b/src/app/core/export/download-service/download.service.spec.ts @@ -7,17 +7,16 @@ import { ConfigurableEnumValue } from "../../basic-datatypes/configurable-enum/c import { DatabaseField } from "../../entity/database-field.decorator"; import moment from "moment"; import { EntityMapperService } from "app/core/entity/entity-mapper/entity-mapper.service"; -import { School } from "app/child-dev-project/schools/model/school"; -import { Child } from "app/child-dev-project/children/model/child"; import { mockEntityMapper } from "app/core/entity/entity-mapper/mock-entity-mapper-service"; import { EntityDatatype } from "../../basic-datatypes/entity/entity.datatype"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("DownloadService", () => { let service: DownloadService; let mockDataTransformationService: jasmine.SpyObj; let mockedEntityMapper; - const testSchool = School.create({ name: "Test School" }); - const testChild = Child.create("Test Child"); + const testSchool = TestEntity.create({ name: "Test School" }); + const testChild = TestEntity.create("Test Child"); beforeEach(() => { mockDataTransformationService = jasmine.createSpyObj([ diff --git a/src/app/core/export/query.service.spec.ts b/src/app/core/export/query.service.spec.ts index 06799e63ae..a6f3b6f958 100644 --- a/src/app/core/export/query.service.spec.ts +++ b/src/app/core/export/query.service.spec.ts @@ -5,9 +5,7 @@ import { AttendanceReport, QueryService, } from "./query.service"; -import { Child } from "../../child-dev-project/children/model/child"; import { EntityMapperService } from "../entity/entity-mapper/entity-mapper.service"; -import { School } from "../../child-dev-project/schools/model/school"; import { RecurringActivity } from "../../child-dev-project/attendance/model/recurring-activity"; import { EventNote } from "../../child-dev-project/attendance/model/event-note"; import moment from "moment"; @@ -23,11 +21,16 @@ import { AttendanceStatusType } from "../../child-dev-project/attendance/model/a import { DatabaseTestingModule } from "../../utils/database-testing.module"; import { ChildrenService } from "../../child-dev-project/children/children.service"; import { AttendanceService } from "../../child-dev-project/attendance/attendance.service"; +import { Entity, EntityConstructor } from "../entity/model/entity"; +import { entityRegistry } from "../entity/database-entity.decorator"; describe("QueryService", () => { let service: QueryService; let entityMapper: EntityMapperService; + let School: EntityConstructor; + let Child: EntityConstructor; + const presentAttendanceStatus = defaultAttendanceStatusTypes.find( (status) => status.countAs === "PRESENT", ); @@ -48,6 +51,9 @@ describe("QueryService", () => { }); service = TestBed.inject(QueryService); entityMapper = TestBed.inject(EntityMapperService); + + School = entityRegistry.get("School"); + Child = entityRegistry.get("Child"); })); afterEach(() => TestBed.inject(Database).destroy()); @@ -88,7 +94,7 @@ describe("QueryService", () => { await createSchool([maleChildNormal, femaleChildNormal]); const maleChildrenOnPrivateSchoolsQuery = ` - ${School.ENTITY_TYPE}:toArray[*privateSchool=true] + School:toArray[*privateSchool=true] :getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId) [*isActive=true].childId:unique:toEntities(${Child.ENTITY_TYPE}) :filterByObjectAttribute(gender, id, M)`; @@ -99,7 +105,7 @@ describe("QueryService", () => { expectEntitiesToMatch(maleChildrenOnPrivateSchools, [maleChildPrivate]); const childrenVisitingAnySchoolQuery = ` - ${School.ENTITY_TYPE}:toArray + School:toArray :getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId) [*isActive=true].childId:unique:toEntities(${Child.ENTITY_TYPE})`; const childrenVisitingAnySchool = await queryData( @@ -215,7 +221,7 @@ describe("QueryService", () => { ); const femaleParticipantsPrivateSchoolQuery = ` - ${School.ENTITY_TYPE}:toArray[*privateSchool=true] + School:toArray[*privateSchool=true] :getRelated(${RecurringActivity.ENTITY_TYPE}, linkedGroups) :getRelated(${EventNote.ENTITY_TYPE}, relatesTo) :getParticipantsWithAttendance(PRESENT):unique @@ -228,7 +234,7 @@ describe("QueryService", () => { ]); const participantsNotPrivateSchoolQuery = ` - ${School.ENTITY_TYPE}:toArray[*privateSchool!=true] + School:toArray[*privateSchool!=true] :getRelated(${RecurringActivity.ENTITY_TYPE}, linkedGroups) :getRelated(${EventNote.ENTITY_TYPE}, relatesTo) :getParticipantsWithAttendance(PRESENT):unique @@ -326,7 +332,7 @@ describe("QueryService", () => { query = "Child:toArray:getRelated(ChildSchoolRelation, childId)"; await expectAsync(queryData(query)).toBeResolvedTo([]); - expect(loadSpy).not.toHaveBeenCalledWith(School); + expect(loadSpy).not.toHaveBeenCalledWith("School"); expect(loadSpy).not.toHaveBeenCalledWith(ChildSchoolRelation); expect(loadSpy).toHaveBeenCalledWith(Child); }); @@ -404,7 +410,7 @@ describe("QueryService", () => { await expectAsync(queryData(query)).toBeResolvedTo(["M"]); - child.gender = genders.find(({ id }) => id === "F"); + child["gender"] = genders.find(({ id }) => id === "F"); await entityMapper.save(child); await expectAsync(queryData(query)).toBeResolvedTo(["F"]); @@ -577,7 +583,7 @@ describe("QueryService", () => { `${EventNote.ENTITY_TYPE}:toArray:getIds(children):toEntities(${Child.ENTITY_TYPE}).gender`, ); - expect(result).toEqual([maleChild.gender]); + expect(result).toEqual([maleChild["gender"]]); }); it("does not throw an error if no query is provided", () => { @@ -650,18 +656,18 @@ describe("QueryService", () => { async function createChild( gender: "M" | "F" | string = "F", religion?: "muslim" | "christian", - ): Promise { + ): Promise { const child = new Child(); - child.gender = genders.find((g) => g.id === gender); + child["gender"] = genders.find((g) => g.id === gender); child["religion"] = religion; await entityMapper.save(child); return child; } async function createSchool( - children: Child[] = [], + children: Entity[] = [], privateSchool?: boolean, - ): Promise { + ): Promise { const school = new School(); school["privateSchool"] = privateSchool; await entityMapper.save(school); @@ -677,7 +683,7 @@ describe("QueryService", () => { async function createNote( date: Date, - children: { child: Child; status: AttendanceStatusType }[] = [], + children: { child: Entity; status: AttendanceStatusType }[] = [], activity?: RecurringActivity, ): Promise { const event = new EventNote(); @@ -695,7 +701,7 @@ describe("QueryService", () => { } async function createActivity( - schools: School[], + schools: Entity[], category = schoolClass, ): Promise { const activity = new RecurringActivity(); diff --git a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts index 4884e8ca00..55587f416c 100644 --- a/src/app/core/filter/filter-generator/filter-generator.service.spec.ts +++ b/src/app/core/filter/filter-generator/filter-generator.service.spec.ts @@ -6,11 +6,9 @@ import { BooleanFilterConfig, PrebuiltFilterConfig, } from "../../entity-list/EntityListConfig"; -import { School } from "../../../child-dev-project/schools/model/school"; import { Note } from "../../../child-dev-project/notes/model/note"; import { defaultInteractionTypes } from "../../config/default-config/default-interaction-types"; import { ChildSchoolRelation } from "../../../child-dev-project/children/model/childSchoolRelation"; -import { Child } from "../../../child-dev-project/children/model/child"; import moment from "moment"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { FilterService } from "../filter.service"; @@ -21,6 +19,8 @@ import { BooleanFilter } from "../filters/booleanFilter"; import { ConfigurableEnumFilter } from "../filters/configurableEnumFilter"; import { EntityFilter } from "../filters/entityFilter"; import { FormFieldConfig } from "../../common-components/entity-form/FormConfig"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { EntitySchemaField } from "../../entity/schema/entity-schema-field"; describe("FilterGeneratorService", () => { let service: FilterGeneratorService; @@ -40,26 +40,30 @@ describe("FilterGeneratorService", () => { it("should create a boolean filter", async () => { const filterConfig: BooleanFilterConfig = { - id: "privateSchool", - true: "Private", - false: "Government", + id: "test", + true: "On", + false: "Off", type: "boolean", }; - const schema = School.schema.get("privateSchool"); + const fieldSchema: EntitySchemaField = { + dataType: "boolean", + label: "test Property", + }; + TestEntity.schema.set("test", fieldSchema); const filter = ( - await service.generate([filterConfig], School, []) - )[0] as BooleanFilter; + await service.generate([filterConfig], TestEntity, []) + )[0] as BooleanFilter; - expect(filter.label).toEqual(schema.label); - expect(filter.name).toEqual("privateSchool"); + expect(filter.label).toEqual(fieldSchema.label); + expect(filter.name).toEqual("test"); expect( filter.options.map((option) => { return { key: option.key, label: option.label }; }), ).toEqual([ - { key: "true", label: "Private" }, - { key: "false", label: "Government" }, + { key: "true", label: "On" }, + { key: "false", label: "Off" }, ]); }); @@ -134,9 +138,9 @@ describe("FilterGeneratorService", () => { }); it("should create an entity filter", async () => { - const school1 = new School(); + const school1 = new TestEntity(); school1.name = "First School"; - const school2 = new School(); + const school2 = new TestEntity(); school2.name = "Second School"; await TestBed.inject(EntityMapperService).saveAll([school1, school2]); const csr1 = new ChildSchoolRelation(); @@ -148,10 +152,12 @@ describe("FilterGeneratorService", () => { const csr4 = new ChildSchoolRelation(); csr4.schoolId = school1.getId(); const schema = ChildSchoolRelation.schema.get("schoolId"); + const originalSchemaAdditional = schema.additional; + schema.additional = TestEntity.ENTITY_TYPE; const filterOptions = ( await service.generate([{ id: "schoolId" }], ChildSchoolRelation, []) - )[0] as EntityFilter; + )[0] as EntityFilter; expect(filterOptions.label).toEqual(schema.label); expect(filterOptions.name).toEqual("schoolId"); @@ -164,27 +170,29 @@ describe("FilterGeneratorService", () => { filterOptions.options.find((opt) => opt.key === school2.getId()); expect(school2Filter.label).toEqual(school2.name); expect(filter(allRelations, school2Filter)).toEqual([csr2, csr3]); + + schema.additional = originalSchemaAdditional; }); it("should create filters with all possible options on default", async () => { - const child1 = new Child(); - child1["religion"] = "muslim"; - const child2 = new Child(); - child2["religion"] = "christian"; - const child3 = new Child(); - child3["religion"] = "muslim"; - const schema = Child.schema.get("religion"); + const child1 = new TestEntity(); + child1["other"] = "muslim"; + const child2 = new TestEntity(); + child2["other"] = "christian"; + const child3 = new TestEntity(); + child3["other"] = "muslim"; + const schema = TestEntity.schema.get("other"); const filter = ( - await service.generate([{ id: "religion" }], Child, [ + await service.generate([{ id: "other" }], TestEntity, [ child1, child2, child3, ]) - )[0] as SelectableFilter; + )[0] as SelectableFilter; expect(filter.label).toEqual(schema.label); - expect(filter.name).toEqual("religion"); + expect(filter.name).toEqual("other"); const comparableOptions = filter.options.map((option) => { return { key: option.key, label: option.label }; }); diff --git a/src/app/core/filter/filter.service.spec.ts b/src/app/core/filter/filter.service.spec.ts index 4ec78b85f6..f2f229e398 100644 --- a/src/app/core/filter/filter.service.spec.ts +++ b/src/app/core/filter/filter.service.spec.ts @@ -7,8 +7,8 @@ import { ConfigurableEnumService } from "../basic-datatypes/configurable-enum/co import { createTestingConfigurableEnumService } from "../basic-datatypes/configurable-enum/configurable-enum-testing"; import moment from "moment"; import { DataFilter } from "./filters/filters"; -import { Child } from "../../child-dev-project/children/model/child"; import { ChildSchoolRelation } from "../../child-dev-project/children/model/childSchoolRelation"; +import { TestEntity } from "../../utils/test-utils/TestEntity"; describe("FilterService", () => { let service: FilterService; @@ -70,7 +70,7 @@ describe("FilterService", () => { }); it("should support patching with array values", () => { - const child = new Child(); + const child = new TestEntity(); const filter = { children: { $elemMatch: { $eq: child.getId() } }, } as DataFilter; @@ -83,14 +83,14 @@ describe("FilterService", () => { it("should not set properties without a schema", () => { const filter = { - childId: `${Child.ENTITY_TYPE}:some-id`, + childId: `${TestEntity.ENTITY_TYPE}:some-id`, isActive: false, } as DataFilter; const relation = new ChildSchoolRelation(); service.alignEntityWithFilter(relation, filter); - expect(relation.childId).toEqual(`${Child.ENTITY_TYPE}:some-id`); + expect(relation.childId).toEqual(`${TestEntity.ENTITY_TYPE}:some-id`); expect(relation.isActive).toBeTrue(); }); diff --git a/src/app/core/filter/filter/filter.component.ts b/src/app/core/filter/filter/filter.component.ts index 1f83cc9874..ca84947f4d 100644 --- a/src/app/core/filter/filter/filter.component.ts +++ b/src/app/core/filter/filter/filter.component.ts @@ -59,14 +59,16 @@ export class FilterComponent implements OnChanges { @Output() filterObjChange = new EventEmitter>(); filterSelections: Filter[] = []; - urlPath = getUrlWithoutParams(this.router); + urlPath: string; constructor( private filterGenerator: FilterGeneratorService, private filterService: FilterService, private router: Router, private route: ActivatedRoute, - ) {} + ) { + this.urlPath = getUrlWithoutParams(this.router); + } async ngOnChanges(changes: SimpleChanges) { if (changes.filterConfig || changes.entityType || changes.entities) { diff --git a/src/app/core/filter/filters/dateFilter.ts b/src/app/core/filter/filters/dateFilter.ts index 53735398e8..5623ed1a8f 100644 --- a/src/app/core/filter/filters/dateFilter.ts +++ b/src/app/core/filter/filters/dateFilter.ts @@ -15,8 +15,8 @@ export class DateFilter extends Filter { override component = DateRangeFilterComponent; constructor( - public name: string, - public label: string = name, + public override name: string, + public override label: string = name, public rangeOptions: DateRangeFilterConfigOption[], ) { super(name, label); diff --git a/src/app/core/filter/filters/filters.ts b/src/app/core/filter/filters/filters.ts index 8d09c85928..2e06c14640 100644 --- a/src/app/core/filter/filters/filters.ts +++ b/src/app/core/filter/filters/filters.ts @@ -95,9 +95,9 @@ export class SelectableFilter extends Filter { * (optional, defaults to the name of the selection) */ constructor( - public name: string, + public override name: string, public options: FilterSelectionOption[], - public label: string = name, + public override label: string = name, ) { super(name, label); this.selectedOptionValues = []; diff --git a/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts b/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts index aa3f0c19f7..e30409d4df 100644 --- a/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts +++ b/src/app/core/form-dialog/dialog-buttons/dialog-buttons.component.spec.ts @@ -11,12 +11,12 @@ import { EntityFormService } from "../../common-components/entity-form/entity-fo import { Entity } from "../../entity/model/entity"; import { MatDialogRef } from "@angular/material/dialog"; import { AlertService } from "../../alerts/alert.service"; -import { Child } from "../../../child-dev-project/children/model/child"; import { Router } from "@angular/router"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { FormGroup } from "@angular/forms"; import { firstValueFrom, Subject } from "rxjs"; import { UnsavedChangesService } from "../../entity-details/form/unsaved-changes.service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("DialogButtonsComponent", () => { let component: DialogButtonsComponent; @@ -81,16 +81,18 @@ describe("DialogButtonsComponent", () => { }); it("should create the details route", () => { - const child = new Child(); + const child = new TestEntity(); child._rev = "existing"; component.entity = child; TestBed.inject(Router).resetConfig([ - { path: "child/:id", redirectTo: "/" }, + { path: "test-entity/:id", redirectTo: "/" }, ]); component.ngOnInit(); - expect(component.detailsRoute).toBe(`${Child.route}/${child.getId(true)}`); + expect(component.detailsRoute).toBe( + `${TestEntity.route}/${child.getId(true)}`, + ); }); it("should close the dialog if a entity is deleted", async () => { diff --git a/src/app/core/form-dialog/row-details/row-details.component.html b/src/app/core/form-dialog/row-details/row-details.component.html index b4520176aa..55ba05d3a8 100644 --- a/src/app/core/form-dialog/row-details/row-details.component.html +++ b/src/app/core/form-dialog/row-details/row-details.component.html @@ -5,6 +5,7 @@ - - + + diff --git a/src/app/core/form-dialog/row-details/row-details.component.spec.ts b/src/app/core/form-dialog/row-details/row-details.component.spec.ts index 391500ce30..efddc9144a 100644 --- a/src/app/core/form-dialog/row-details/row-details.component.spec.ts +++ b/src/app/core/form-dialog/row-details/row-details.component.spec.ts @@ -1,4 +1,10 @@ -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { + ComponentFixture, + fakeAsync, + TestBed, + tick, + waitForAsync, +} from "@angular/core/testing"; import { DetailsComponentData, @@ -9,13 +15,26 @@ import { Entity } from "../../entity/model/entity"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { EntityAbility } from "../../permissions/ability/entity-ability"; import { NEVER } from "rxjs"; +import { + EntityForm, + EntityFormService, +} from "../../common-components/entity-form/entity-form.service"; +import { FormBuilder } from "@angular/forms"; describe("RowDetailsComponent", () => { let component: RowDetailsComponent; let fixture: ComponentFixture; let detailsComponentData: DetailsComponentData; + let mockFormService: jasmine.SpyObj; + beforeEach(waitForAsync(() => { + mockFormService = jasmine.createSpyObj(["createEntityForm"]); + mockFormService.createEntityForm.and.returnValue( + Promise.resolve({ + formGroup: new FormBuilder().group({}), + } as EntityForm), + ); detailsComponentData = { entity: new Entity(), columns: [], @@ -23,6 +42,7 @@ describe("RowDetailsComponent", () => { TestBed.configureTestingModule({ imports: [RowDetailsComponent, MockedTestingModule.withState()], providers: [ + { provide: EntityFormService, useValue: mockFormService }, { provide: MAT_DIALOG_DATA, useValue: detailsComponentData }, { provide: MatDialogRef, @@ -39,8 +59,9 @@ describe("RowDetailsComponent", () => { fixture.detectChanges(); } - it("should create", () => { + it("should create", fakeAsync(() => { initComponent(); + tick(); expect(component).toBeTruthy(); - }); + })); }); diff --git a/src/app/core/form-dialog/row-details/row-details.component.ts b/src/app/core/form-dialog/row-details/row-details.component.ts index ce39d29cbc..59210421b4 100644 --- a/src/app/core/form-dialog/row-details/row-details.component.ts +++ b/src/app/core/form-dialog/row-details/row-details.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject } from "@angular/core"; +import { Component, Inject, OnInit } from "@angular/core"; import { FormFieldConfig } from "../../common-components/entity-form/FormConfig"; import { MAT_DIALOG_DATA, MatDialogModule } from "@angular/material/dialog"; import { Entity } from "../../entity/model/entity"; @@ -58,7 +58,7 @@ export interface DetailsComponentData { ], standalone: true, }) -export class RowDetailsComponent { +export class RowDetailsComponent implements OnInit { form: EntityForm; fieldGroups: FieldGroup[]; @@ -68,27 +68,36 @@ export class RowDetailsComponent { constructor( @Inject(MAT_DIALOG_DATA) public data: DetailsComponentData, private formService: EntityFormService, - ) { - this.init(data); + ) {} + + ngOnInit(): void { + this.init(this.data) + .then() + .catch((reason) => console.log(reason)); } - private init(data: DetailsComponentData) { - this.form = this.formService.createFormGroup(data.columns, data.entity); + private async init(data: DetailsComponentData) { + this.form = await this.formService.createEntityForm( + data.columns, + data.entity, + ); this.enableSaveWithoutChangesIfNew(data.entity); this.fieldGroups = data.columns.map((col) => ({ fields: [col] })); this.tempEntity = this.data.entity; - this.form.valueChanges.pipe(untilDestroyed(this)).subscribe((value) => { - const dynamicConstructor: any = data.entity.getConstructor(); - this.tempEntity = Object.assign(new dynamicConstructor(), value); - }); + this.form.formGroup.valueChanges + .pipe(untilDestroyed(this)) + .subscribe((value) => { + const dynamicConstructor: any = data.entity.getConstructor(); + this.tempEntity = Object.assign(new dynamicConstructor(), value); + }); this.viewOnlyColumns = data.viewOnlyColumns; } private enableSaveWithoutChangesIfNew(entity: Entity) { if (entity.isNew) { // could check here that at least some fields hold a value but the naive heuristic to allow save of all new seems ok - this.form.markAsDirty(); + this.form.formGroup.markAsDirty(); } } } diff --git a/src/app/core/import/import-column-mapping/import-column-mapping.component.spec.ts b/src/app/core/import/import-column-mapping/import-column-mapping.component.spec.ts index 39a634b572..212fb40405 100644 --- a/src/app/core/import/import-column-mapping/import-column-mapping.component.spec.ts +++ b/src/app/core/import/import-column-mapping/import-column-mapping.component.spec.ts @@ -4,11 +4,11 @@ import { ImportColumnMappingComponent } from "./import-column-mapping.component" import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { MatDialog } from "@angular/material/dialog"; import { DiscreteImportConfigComponent } from "../../basic-datatypes/discrete/discrete-import-config/discrete-import-config.component"; -import { Child } from "../../../child-dev-project/children/model/child"; import { ColumnMapping } from "../column-mapping"; import { of } from "rxjs"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; -describe("ImportMapColumnsComponent", () => { +describe("ImportColumnMappingComponent", () => { let component: ImportColumnMappingComponent; let fixture: ComponentFixture; @@ -30,33 +30,36 @@ describe("ImportMapColumnsComponent", () => { { name: "second", gender: "female" }, { name: "third", gender: "female" }, ]; - component.entityType = "Child"; + component.entityType = TestEntity.ENTITY_TYPE; component.columnMapping = [{ column: "name" }, { column: "gender" }]; const openSpy = spyOn(TestBed.inject(MatDialog), "open"); openSpy.and.returnValue({ afterClosed: () => of(undefined) } as any); const genderColumn = component.columnMapping[1]; - genderColumn.propertyName = "gender"; + genderColumn.propertyName = "category"; await component.openMappingComponent(genderColumn); - expect(openSpy).toHaveBeenCalledWith(DiscreteImportConfigComponent, { - data: { - col: genderColumn, - values: ["male", "female"], - entityType: Child, - }, - disableClose: true, - }); + expect(openSpy).toHaveBeenCalledWith( + DiscreteImportConfigComponent, + jasmine.objectContaining({ + data: { + col: genderColumn, + values: ["male", "female"], + entityType: TestEntity, + }, + disableClose: true, + }), + ); }); it("should emit changes after popup is closed", async () => { spyOn(TestBed.inject(MatDialog), "open").and.returnValue({ afterClosed: () => of(undefined), } as any); - component.entityType = "Child"; + component.entityType = TestEntity.ENTITY_TYPE; const columnMapping: ColumnMapping = { column: "test", - propertyName: "gender", + propertyName: "category", }; await component.openMappingComponent(columnMapping); diff --git a/src/app/core/import/import-column-mapping/import-column-mapping.component.ts b/src/app/core/import/import-column-mapping/import-column-mapping.component.ts index ef1be6389b..4edebf6cfa 100644 --- a/src/app/core/import/import-column-mapping/import-column-mapping.component.ts +++ b/src/app/core/import/import-column-mapping/import-column-mapping.component.ts @@ -110,6 +110,7 @@ export class ImportColumnMappingComponent implements OnChanges { values: [...uniqueValues], entityType: this.entityCtor, }, + width: "80vw", disableClose: true, }) .afterClosed() diff --git a/src/app/core/import/import-entity-type/import-entity-type.component.spec.ts b/src/app/core/import/import-entity-type/import-entity-type.component.spec.ts index 8fc01d848e..b38eb99335 100644 --- a/src/app/core/import/import-entity-type/import-entity-type.component.spec.ts +++ b/src/app/core/import/import-entity-type/import-entity-type.component.spec.ts @@ -12,7 +12,7 @@ describe("ImportSelectTypeComponent", () => { let fixture: ComponentFixture; class TestEntity extends Entity { - static _isCustomizedType = true; // set by config service applying custom definitions + static override _isCustomizedType = true; // set by config service applying custom definitions } let mockRegistry: EntityRegistry; diff --git a/src/app/core/import/import-history/import-history.stories.ts b/src/app/core/import/import-history/import-history.stories.ts index 50cf40bfcf..eb7b5e603f 100644 --- a/src/app/core/import/import-history/import-history.stories.ts +++ b/src/app/core/import/import-history/import-history.stories.ts @@ -1,10 +1,10 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { StorybookBaseModule } from "../../../utils/storybook-base.module"; import { ImportHistoryComponent } from "./import-history.component"; -import { User } from "../../user/user"; import { IMPORT_SAMPLE_PREVIOUS_IMPORTS } from "../import/import-sample-raw-data"; import { importProvidersFrom } from "@angular/core"; import { TEST_USER } from "../../user/demo-user-generator.service"; +import { Entity } from "../../entity/model/entity"; export default { title: "Features/Import/Import History", @@ -15,7 +15,7 @@ export default { importProvidersFrom( StorybookBaseModule.withData([ ...IMPORT_SAMPLE_PREVIOUS_IMPORTS, - Object.assign(new User(TEST_USER), { name: TEST_USER }), + Object.assign(new Entity(TEST_USER), { name: TEST_USER }), ]), ), ], diff --git a/src/app/core/import/import-review-data/import-review-data.component.spec.ts b/src/app/core/import/import-review-data/import-review-data.component.spec.ts index d2f0e16def..1d06363d65 100644 --- a/src/app/core/import/import-review-data/import-review-data.component.spec.ts +++ b/src/app/core/import/import-review-data/import-review-data.component.spec.ts @@ -10,7 +10,7 @@ import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { MatDialog } from "@angular/material/dialog"; import { of } from "rxjs"; import { ImportService } from "../import.service"; -import { School } from "../../../child-dev-project/schools/model/school"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("ImportReviewDataComponent", () => { let component: ImportReviewDataComponent; @@ -36,13 +36,13 @@ describe("ImportReviewDataComponent", () => { fixture = TestBed.createComponent(ImportReviewDataComponent); component = fixture.componentInstance; - component.entityType = School.ENTITY_TYPE; + component.entityType = TestEntity.ENTITY_TYPE; fixture.detectChanges(); }); it("should parse data whenever it changes", fakeAsync(() => { - const testEntities = [new School("1")]; + const testEntities = [new TestEntity("1")]; mockImportService.transformRawDataToEntities.and.resolveTo(testEntities); component.columnMapping = [ { column: "x", propertyName: "name" }, diff --git a/src/app/core/import/import.service.spec.ts b/src/app/core/import/import.service.spec.ts index 5438fff73f..68ee37dd54 100644 --- a/src/app/core/import/import.service.spec.ts +++ b/src/app/core/import/import.service.spec.ts @@ -10,7 +10,6 @@ import { expectEntitiesToMatch, } from "../../utils/expect-entity-data.spec"; import moment from "moment"; -import { Child } from "../../child-dev-project/children/model/child"; import { RecurringActivity } from "../../child-dev-project/attendance/model/recurring-activity"; import { ChildSchoolRelation } from "../../child-dev-project/children/model/childSchoolRelation"; import { mockEntityMapper } from "../entity/entity-mapper/mock-entity-mapper-service"; @@ -18,6 +17,8 @@ import { CoreTestingModule } from "../../utils/core-testing.module"; import { EntityRegistry } from "../entity/database-entity.decorator"; import { DatabaseField } from "../entity/database-field.decorator"; import { EntityDatatype } from "../basic-datatypes/entity/entity.datatype"; +import { TestEntity } from "../../utils/test-utils/TestEntity"; +import { createEntityOfType } from "../demo-data/create-entity-of-type"; describe("ImportService", () => { let service: ImportService; @@ -70,10 +71,10 @@ describe("ImportService", () => { } spyOn(TestBed.inject(EntityRegistry), "get").and.callFake( (entityType: string) => - entityType === "ImportTestTarget" ? ImportTestTarget : Child, + entityType === "ImportTestTarget" ? ImportTestTarget : TestEntity, ); - const child = Child.create("Child Name"); + const child = TestEntity.create("Child Name"); await TestBed.inject(EntityMapperService).save(child); const rawData: any[] = [ @@ -133,7 +134,10 @@ describe("ImportService", () => { }); it("should link imported data to other entities", async () => { - const testEntities: Entity[] = [new Child("1"), new Child("2")]; + const testEntities: Entity[] = [ + createEntityOfType("Child", "1"), + createEntityOfType("Child", "2"), + ]; const activity = new RecurringActivity("3"); const entityMapper = TestBed.inject(EntityMapperService); await entityMapper.save(activity); @@ -177,7 +181,9 @@ describe("ImportService", () => { ].map((e) => Object.assign(new ChildSchoolRelation(), e)); const activity = new RecurringActivity("3"); activity.participants = ["3", "2", "1"]; - const children = ["1", "2", "3"].map((id) => new Child(id)); + const children = ["1", "2", "3"].map((id) => + createEntityOfType("Child", id), + ); const entityMapper = TestBed.inject(EntityMapperService); await entityMapper.saveAll([ ...children, @@ -198,9 +204,15 @@ describe("ImportService", () => { it("should not fail undo if some entities have already been removed", async () => { const importMeta = new ImportMetadata(); - importMeta.config = { entityType: "Child", columnMapping: undefined }; - importMeta.ids = ["Child:1", "Child:2"]; - const children = ["1", "2", "3"].map((id) => new Child(id)); + importMeta.config = { + entityType: TestEntity.ENTITY_TYPE, + columnMapping: undefined, + }; + importMeta.ids = [ + TestEntity.ENTITY_TYPE + ":1", + TestEntity.ENTITY_TYPE + ":2", + ]; + const children = ["1", "2", "3"].map((id) => new TestEntity(id)); const entityMapper = TestBed.inject(EntityMapperService); await entityMapper.saveAll([...children, importMeta]); diff --git a/src/app/core/import/import.service.ts b/src/app/core/import/import.service.ts index 6971e0e124..a72ea3ba9e 100644 --- a/src/app/core/import/import.service.ts +++ b/src/app/core/import/import.service.ts @@ -1,8 +1,6 @@ import { Injectable } from "@angular/core"; import { EntityMapperService } from "../entity/entity-mapper/entity-mapper.service"; -import { Child } from "../../child-dev-project/children/model/child"; import { RecurringActivity } from "../../child-dev-project/attendance/model/recurring-activity"; -import { School } from "../../child-dev-project/schools/model/school"; import { Entity } from "../entity/model/entity"; import { ImportMetadata, ImportSettings } from "./import-metadata"; import { ColumnMapping } from "./column-mapping"; @@ -25,12 +23,13 @@ export class ImportService { }; }; } = { - [Child.ENTITY_TYPE]: { + // TODO: generalize this somehow by analyzing schemas? + ["Child"]: { [RecurringActivity.ENTITY_TYPE]: { create: this.linkToActivity.bind(this), undo: this.undoActivityLink.bind(this), }, - [School.ENTITY_TYPE]: { + ["School"]: { create: this.linkToSchool.bind(this), undo: this.undoSchoolLink.bind(this), }, diff --git a/src/app/core/import/import/import-sample-raw-data.ts b/src/app/core/import/import/import-sample-raw-data.ts index 0b287a54f2..324da26083 100644 --- a/src/app/core/import/import/import-sample-raw-data.ts +++ b/src/app/core/import/import/import-sample-raw-data.ts @@ -1,4 +1,3 @@ -import { School } from "../../../child-dev-project/schools/model/school"; import { RecurringActivity } from "../../../child-dev-project/attendance/model/recurring-activity"; import { AdditionalImportAction } from "../import-additional-actions/additional-import-action"; import { Entity } from "../../entity/model/entity"; @@ -6,6 +5,7 @@ import { ColumnMapping } from "../column-mapping"; import { genders } from "../../../child-dev-project/children/model/genders"; import { ImportMetadata } from "../import-metadata"; import { TEST_USER } from "../../user/demo-user-generator.service"; +import { createEntityOfType } from "../../demo-data/create-entity-of-type"; /** * Sample raw data that can be used in Storybook and tests. @@ -38,8 +38,8 @@ Object.assign( ); export const IMPORT_SAMPLE_LINKABLE_DATA: Entity[] = [ - School.create({ name: "Sample School" }), - School.create({ name: "ABCD School" }), + Object.assign(createEntityOfType("School"), { name: "Sample School" }), + Object.assign(createEntityOfType("School"), { name: "ABCD School" }), RecurringActivity.create("Activity X"), RecurringActivity.create("Activity Y"), ]; diff --git a/src/app/core/import/import/import.stories.ts b/src/app/core/import/import/import.stories.ts index f386a02b37..c0d5107931 100644 --- a/src/app/core/import/import/import.stories.ts +++ b/src/app/core/import/import/import.stories.ts @@ -8,9 +8,9 @@ import { IMPORT_SAMPLE_PREVIOUS_IMPORTS, IMPORT_SAMPLE_RAW_DATA, } from "./import-sample-raw-data"; -import { User } from "../../user/user"; import { importProvidersFrom } from "@angular/core"; import { TEST_USER } from "../../user/demo-user-generator.service"; +import { Entity } from "../../entity/model/entity"; export default { title: "Features/Import/> Overall Module", @@ -22,7 +22,7 @@ export default { StorybookBaseModule.withData([ ...IMPORT_SAMPLE_LINKABLE_DATA, ...IMPORT_SAMPLE_PREVIOUS_IMPORTS, - Object.assign(new User(TEST_USER), { name: TEST_USER }), + Object.assign(new Entity(TEST_USER), { name: TEST_USER }), ]), ), ], diff --git a/src/app/core/language/date-adapter-with-formatting.ts b/src/app/core/language/date-adapter-with-formatting.ts index 5ce16923fe..8eea96faa8 100644 --- a/src/app/core/language/date-adapter-with-formatting.ts +++ b/src/app/core/language/date-adapter-with-formatting.ts @@ -22,7 +22,7 @@ export class DateAdapterWithFormatting extends NativeDateAdapter { * @param value * @param parseFormat */ - parse(value: any, parseFormat?: any): Date | null { + override parse(value: any, parseFormat?: any): Date | null { if (value && typeof value == "string") { return moment(value, parseFormat, this.locale, true).toDate(); } diff --git a/src/app/core/language/language-select/language-select.component.ts b/src/app/core/language/language-select/language-select.component.ts index 0705d52c36..e0ba40be47 100644 --- a/src/app/core/language/language-select/language-select.component.ts +++ b/src/app/core/language/language-select/language-select.component.ts @@ -35,12 +35,14 @@ export class LanguageSelectComponent { /** * The region code of the currently selected language/region */ - siteRegionCode = this.translationService.currentRegionCode(); + siteRegionCode: string; constructor( private translationService: LanguageService, @Inject(LOCATION_TOKEN) private location: Location, - ) {} + ) { + this.siteRegionCode = this.translationService.currentRegionCode(); + } changeLocale(lang: string) { localStorage.setItem(LANGUAGE_LOCAL_STORAGE_KEY, lang); diff --git a/src/app/core/logging/logging.service.ts b/src/app/core/logging/logging.service.ts index dd1fcd9bab..7ea7e65621 100644 --- a/src/app/core/logging/logging.service.ts +++ b/src/app/core/logging/logging.service.ts @@ -51,7 +51,7 @@ export class LoggingService { return () => loginState.subscribe((newState) => { if (newState === LoginState.LOGGED_IN) { - const username = sessionInfo.value.name; + const username = sessionInfo.value?.id; Logging.setLoggingContextUser(username); } else { Logging.setLoggingContextUser(undefined); diff --git a/src/app/core/permissions/ability/ability.service.spec.ts b/src/app/core/permissions/ability/ability.service.spec.ts index cafcb446c5..df70de4f7e 100644 --- a/src/app/core/permissions/ability/ability.service.spec.ts +++ b/src/app/core/permissions/ability/ability.service.spec.ts @@ -2,11 +2,9 @@ import { fakeAsync, TestBed, tick, waitForAsync } from "@angular/core/testing"; import { AbilityService } from "./ability.service"; import { BehaviorSubject, Subject } from "rxjs"; -import { Child } from "../../../child-dev-project/children/model/child"; import { Note } from "../../../child-dev-project/notes/model/note"; import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service"; import { PermissionEnforcerService } from "../permission-enforcer/permission-enforcer.service"; -import { User } from "../../user/user"; import { defaultInteractionTypes } from "../../config/default-config/default-interaction-types"; import { EntityAbility } from "./entity-ability"; import { DatabaseRule, DatabaseRules } from "../permission-types"; @@ -19,7 +17,8 @@ import { DefaultDatatype } from "../../entity/default-datatype/default.datatype" import { EventAttendanceMapDatatype } from "../../../child-dev-project/attendance/model/event-attendance.datatype"; import { SessionSubject } from "../../session/auth/session-info"; import { TEST_USER } from "../../user/demo-user-generator.service"; -import { Entity } from "../../entity/model/entity"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { CurrentUserSubject } from "../../session/current-user-subject"; describe("AbilityService", () => { let service: AbilityService; @@ -28,8 +27,8 @@ describe("AbilityService", () => { let entityMapper: jasmine.SpyObj; const rules: DatabaseRules = { user_app: [ - { subject: "Child", action: "read" }, - { subject: "Note", action: "manage", inverted: true }, + { subject: TestEntity.ENTITY_TYPE, action: "read" }, + { subject: Note.ENTITY_TYPE, action: "manage", inverted: true }, ], admin_app: [{ subject: "all", action: "manage" }], }; @@ -50,9 +49,16 @@ describe("AbilityService", () => { useValue: new BehaviorSubject({ name: TEST_USER, roles: ["user_app"], - entityId: Entity.createPrefixedId(User.ENTITY_TYPE, TEST_USER), + entityId: TestEntity.createPrefixedId( + TestEntity.ENTITY_TYPE, + TEST_USER, + ), }), }, + { + provide: CurrentUserSubject, + useValue: new BehaviorSubject(new TestEntity(TEST_USER)), + }, { provide: DefaultDatatype, useClass: EventAttendanceMapDatatype, @@ -137,6 +143,7 @@ describe("AbilityService", () => { spyOn(ability, "update"); TestBed.inject(SessionSubject).next({ name: "testAdmin", + id: "1", roles: ["user_app", "admin_app"], }); @@ -161,17 +168,18 @@ describe("AbilityService", () => { }); tick(); - expect(ability.can("read", Child)).toBeTrue(); - expect(ability.can("create", Child)).toBeFalse(); - expect(ability.can("manage", Child)).toBeFalse(); - expect(ability.can("read", new Child())).toBeTrue(); - expect(ability.can("create", new Child())).toBeFalse(); + expect(ability.can("read", TestEntity)).toBeTrue(); + expect(ability.can("create", TestEntity)).toBeFalse(); + expect(ability.can("manage", TestEntity)).toBeFalse(); + expect(ability.can("read", new TestEntity())).toBeTrue(); + expect(ability.can("create", new TestEntity())).toBeFalse(); expect(ability.can("manage", Note)).toBeFalse(); expect(ability.can("manage", new Note())).toBeFalse(); expect(ability.can("create", new Note())).toBeFalse(); TestBed.inject(SessionSubject).next({ name: "testAdmin", + id: "1", roles: ["user_app", "admin_app"], }); @@ -180,8 +188,8 @@ describe("AbilityService", () => { entityUpdates.next({ entity: updatedConfig, type: "update" }); tick(); - expect(ability.can("manage", Child)).toBeTrue(); - expect(ability.can("manage", new Child())).toBeTrue(); + expect(ability.can("manage", TestEntity)).toBeTrue(); + expect(ability.can("manage", new TestEntity())).toBeTrue(); expect(ability.can("manage", Note)).toBeTrue(); expect(ability.can("manage", new Note())).toBeTrue(); })); @@ -220,23 +228,18 @@ describe("AbilityService", () => { const config = new Config(Config.PERMISSION_KEY, { user_app: [ { - subject: "User", + subject: "TestEntity", action: "manage", - conditions: { name: placeholder }, + conditions: { _id: placeholder }, }, ], }); entityUpdates.next({ entity: config, type: "update" }); tick(); - const userEntity = new User(); - userEntity.name = Entity.createPrefixedId(User.ENTITY_TYPE, TEST_USER); + const userEntity = new TestEntity(TEST_USER); expect(ability.can("manage", userEntity)).toBeTrue(); - const anotherUser = new User(); - anotherUser.name = Entity.createPrefixedId( - User.ENTITY_TYPE, - "another user", - ); + const anotherUser = new TestEntity(); expect(ability.cannot("manage", anotherUser)).toBeTrue(); } @@ -276,6 +279,7 @@ describe("AbilityService", () => { TestBed.inject(SessionSubject).next({ name: "new-user", + id: "1", roles: ["invalid_role"], }); const warnSpy = spyOn(Logging, "warn"); @@ -307,6 +311,7 @@ describe("AbilityService", () => { TestBed.inject(SessionSubject).next({ name: "admin", + id: "1", roles: ["user_app", "admin_app"], }); diff --git a/src/app/core/permissions/ability/entity-ability.ts b/src/app/core/permissions/ability/entity-ability.ts index 7ab35bd3c8..769701f36e 100644 --- a/src/app/core/permissions/ability/entity-ability.ts +++ b/src/app/core/permissions/ability/entity-ability.ts @@ -24,7 +24,7 @@ export class EntityAbility extends Ability<[EntityAction, string | any]> { super([]); } - can( + override can( action: EntityAction, entity: EntitySubject, field?: string, @@ -46,7 +46,11 @@ export class EntityAbility extends Ability<[EntityAction, string | any]> { return super.can(action, this.getSubject(entity), field); } - cannot(action: EntityAction, entity: EntitySubject, field?: string): boolean { + override cannot( + action: EntityAction, + entity: EntitySubject, + field?: string, + ): boolean { return super.cannot(action, this.getSubject(entity), field); } diff --git a/src/app/core/permissions/permission-directive/disable-entity-operation.directive.spec.ts b/src/app/core/permissions/permission-directive/disable-entity-operation.directive.spec.ts index 0aedaabac6..c463b0bd32 100644 --- a/src/app/core/permissions/permission-directive/disable-entity-operation.directive.spec.ts +++ b/src/app/core/permissions/permission-directive/disable-entity-operation.directive.spec.ts @@ -2,8 +2,8 @@ import { DisableEntityOperationDirective } from "./disable-entity-operation.dire import { Component, ElementRef, ViewChild } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; import { Entity } from "../../entity/model/entity"; -import { Child } from "../../../child-dev-project/children/model/child"; import { EntityAbility } from "../ability/entity-ability"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("DisableEntityOperationDirective", () => { let testComponent: ComponentFixture; @@ -49,7 +49,7 @@ describe("DisableEntityOperationDirective", () => { ).toBeFalse(); (mockAbility.cannot as jasmine.Spy).and.returnValue(true); - testComponent.componentInstance.entityConstructor = Child; + testComponent.componentInstance.entityConstructor = TestEntity; testComponent.detectChanges(); expect( diff --git a/src/app/core/permissions/permission-enforcer/permission-enforcer.service.spec.ts b/src/app/core/permissions/permission-enforcer/permission-enforcer.service.spec.ts index 6607cc20f6..1a47c7fb68 100644 --- a/src/app/core/permissions/permission-enforcer/permission-enforcer.service.spec.ts +++ b/src/app/core/permissions/permission-enforcer/permission-enforcer.service.spec.ts @@ -5,8 +5,6 @@ import { DatabaseRule } from "../permission-types"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service"; import { Database } from "../../database/database"; -import { Child } from "../../../child-dev-project/children/model/child"; -import { School } from "../../../child-dev-project/schools/model/school"; import { AbilityService } from "../ability/ability.service"; import { AnalyticsService } from "../../analytics/analytics.service"; import { Subject } from "rxjs"; @@ -15,12 +13,14 @@ import { UpdatedEntity } from "../../entity/model/entity-update"; import { LOCATION_TOKEN } from "../../../utils/di-tokens"; import { mockEntityMapper } from "../../entity/entity-mapper/mock-entity-mapper-service"; import { TEST_USER } from "../../user/demo-user-generator.service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { createEntityOfType } from "../../demo-data/create-entity-of-type"; describe("PermissionEnforcerService", () => { let service: PermissionEnforcerService; const userRules: DatabaseRule[] = [ { subject: "all", action: "manage" }, - { subject: "Child", action: "read", inverted: true }, + { subject: TestEntity.ENTITY_TYPE, action: "read", inverted: true }, ]; let entityUpdates: Subject>; let entityMapper: EntityMapperService; @@ -68,7 +68,7 @@ describe("PermissionEnforcerService", () => { })); it("should reset page if entity with write restriction exists (inverted)", fakeAsync(() => { - entityMapper.save(new Child()); + entityMapper.save(new TestEntity()); tick(); updateRulesAndTriggerEnforcer(userRules); @@ -79,7 +79,7 @@ describe("PermissionEnforcerService", () => { })); it("should reset page if entity without read permission exists (non-inverted)", fakeAsync(() => { - entityMapper.save(new Child()); + entityMapper.save(new TestEntity()); tick(); updateRulesAndTriggerEnforcer([{ subject: "School", action: "manage" }]); @@ -90,13 +90,13 @@ describe("PermissionEnforcerService", () => { })); it("should reset page if entity exists for which relevant rule is a read restriction ", fakeAsync(() => { - entityMapper.save(new Child()); + entityMapper.save(new TestEntity()); tick(); updateRulesAndTriggerEnforcer([ { subject: "all", action: "manage" }, { - subject: ["Child", "School"], + subject: [TestEntity.ENTITY_TYPE, "School"], action: ["read", "update"], inverted: true, }, @@ -109,14 +109,14 @@ describe("PermissionEnforcerService", () => { })); it("should not reset page if only entities with read permission exist", fakeAsync(() => { - entityMapper.save(new Child()); - entityMapper.save(new School()); + entityMapper.save(new TestEntity()); + entityMapper.save(new TestEntity()); tick(); updateRulesAndTriggerEnforcer([ - { subject: "School", action: ["read", "update"] }, + { subject: TestEntity.ENTITY_TYPE, action: ["read", "update"] }, { subject: "all", action: "delete", inverted: true }, - { subject: ["Note", "Child"], action: "read" }, + { subject: ["Note"], action: "read" }, ]); tick(); @@ -128,7 +128,7 @@ describe("PermissionEnforcerService", () => { updateRulesAndTriggerEnforcer(userRules); tick(); - entityMapper.save(new Child()); + entityMapper.save(new TestEntity()); tick(); updateRulesAndTriggerEnforcer(userRules); @@ -139,7 +139,8 @@ describe("PermissionEnforcerService", () => { })); it("should reset if roles changed since last check and entities without permissions exist", fakeAsync(() => { - entityMapper.save(new School()); + const entityCurrentlyAccessible = createEntityOfType("School"); + entityMapper.save(entityCurrentlyAccessible); tick(); updateRulesAndTriggerEnforcer(userRules); @@ -149,7 +150,7 @@ describe("PermissionEnforcerService", () => { expect(mockLocation.reload).not.toHaveBeenCalled(); const extendedRules = userRules.concat({ - subject: "School", + subject: entityCurrentlyAccessible.getType(), action: "manage", inverted: true, }); @@ -162,11 +163,15 @@ describe("PermissionEnforcerService", () => { })); it("should reset if read rule with condition is added", fakeAsync(() => { - entityMapper.save(Child.create("permitted")); - entityMapper.save(Child.create("not-permitted")); + entityMapper.save(TestEntity.create("permitted")); + entityMapper.save(TestEntity.create("not-permitted")); updateRulesAndTriggerEnforcer([ - { subject: "Child", action: "read", conditions: { name: "permitted" } }, + { + subject: TestEntity.ENTITY_TYPE, + action: "read", + conditions: { name: "permitted" }, + }, ]); tick(); @@ -175,7 +180,7 @@ describe("PermissionEnforcerService", () => { })); it("should track a migration event in analytics service when destroying the local db", fakeAsync(() => { - entityMapper.save(new Child()); + entityMapper.save(new TestEntity()); tick(); updateRulesAndTriggerEnforcer(userRules); @@ -191,7 +196,7 @@ describe("PermissionEnforcerService", () => { it("should not fail if a non-entity rule exists", fakeAsync(() => { const rules: DatabaseRule[] = [ - { subject: "Child", action: "manage" }, + { subject: TestEntity.ENTITY_TYPE, action: "manage" }, { subject: "org.couchdb.user", action: "read", inverted: true }, ]; updateRulesAndTriggerEnforcer(rules); diff --git a/src/app/core/permissions/permission-enforcer/permission-enforcer.service.ts b/src/app/core/permissions/permission-enforcer/permission-enforcer.service.ts index 5a93532405..e77da25e55 100644 --- a/src/app/core/permissions/permission-enforcer/permission-enforcer.service.ts +++ b/src/app/core/permissions/permission-enforcer/permission-enforcer.service.ts @@ -60,7 +60,7 @@ export class PermissionEnforcerService { } private getUserStorageKey() { - return `${this.sessionInfo.value.name}-${PermissionEnforcerService.LOCALSTORAGE_KEY}`; + return `${this.sessionInfo.value.id}-${PermissionEnforcerService.LOCALSTORAGE_KEY}`; } private getSubjectsWithReadRestrictions( diff --git a/src/app/core/permissions/permission-guard/user-role.guard.spec.ts b/src/app/core/permissions/permission-guard/user-role.guard.spec.ts index 3e3fad1956..be60ef989b 100644 --- a/src/app/core/permissions/permission-guard/user-role.guard.spec.ts +++ b/src/app/core/permissions/permission-guard/user-role.guard.spec.ts @@ -10,10 +10,12 @@ describe("UserRoleGuard", () => { let sessionInfo: SessionSubject; const normalUser: SessionInfo = { name: "normalUser", + id: "1", roles: ["user_app"], }; const adminUser: SessionInfo = { name: "admin", + id: "2", roles: ["admin", "user_app"], }; diff --git a/src/app/core/session/auth/keycloak/account-page/account-page.component.spec.ts b/src/app/core/session/auth/keycloak/account-page/account-page.component.spec.ts index bb133e7d46..2f01897f1f 100644 --- a/src/app/core/session/auth/keycloak/account-page/account-page.component.spec.ts +++ b/src/app/core/session/auth/keycloak/account-page/account-page.component.spec.ts @@ -6,7 +6,7 @@ import { } from "@angular/core/testing"; import { AccountPageComponent } from "./account-page.component"; -import { KeycloakAuthService, KeycloakUser } from "../keycloak-auth.service"; +import { KeycloakAuthService, KeycloakUserDto } from "../keycloak-auth.service"; import { of, throwError } from "rxjs"; import { MockedTestingModule } from "../../../../../utils/mocked-testing.module"; import { HttpErrorResponse } from "@angular/common/http"; @@ -47,7 +47,7 @@ describe("AccountPageComponent", () => { it("should show the email if its already set", fakeAsync(() => { const email = "mail@exmaple.com"; - mockAuthService.getUserinfo.and.resolveTo({ email } as KeycloakUser); + mockAuthService.getUserinfo.and.resolveTo({ email } as KeycloakUserDto); component.ngOnInit(); tick(); diff --git a/src/app/core/session/auth/keycloak/keycloak-auth.service.spec.ts b/src/app/core/session/auth/keycloak/keycloak-auth.service.spec.ts index 91aa316413..03b407d1b6 100644 --- a/src/app/core/session/auth/keycloak/keycloak-auth.service.spec.ts +++ b/src/app/core/session/auth/keycloak/keycloak-auth.service.spec.ts @@ -3,7 +3,7 @@ import { fakeAsync, TestBed, tick } from "@angular/core/testing"; import { KeycloakAuthService } from "./keycloak-auth.service"; import { HttpClient } from "@angular/common/http"; import { KeycloakEventType, KeycloakService } from "keycloak-angular"; -import { Subject } from "rxjs"; +import { of, Subject } from "rxjs"; /** * Check {@link https://jwt.io} to decode the token. @@ -29,7 +29,7 @@ describe("KeycloakAuthService", () => { let mockKeycloak: jasmine.SpyObj; beforeEach(() => { - mockHttpClient = jasmine.createSpyObj(["post"]); + mockHttpClient = jasmine.createSpyObj(["get", "post", "delete"]); mockKeycloak = jasmine.createSpyObj( ["updateToken", "getToken", "login", "init"], { keycloakEvents$: new Subject() }, @@ -55,8 +55,10 @@ describe("KeycloakAuthService", () => { it("should return user object after successful login check", () => { return expectAsync(service.login()).toBeResolvedTo({ name: "test", + id: "881ba191-0d27-4dff-9bc4-2c9e561ac900", roles: ["user_app"], entityId: "User:test", + email: undefined, }); }); @@ -66,7 +68,9 @@ describe("KeycloakAuthService", () => { mockKeycloak.getToken.and.resolveTo(tokenWithoutUsername); return expectAsync(service.login()).toBeResolvedTo({ name: "8440add0-97a9-43ed-af0b-116c0fab7e90", + id: "8440add0-97a9-43ed-af0b-116c0fab7e90", roles: ["user_app"], + email: undefined, }); }); @@ -78,6 +82,28 @@ describe("KeycloakAuthService", () => { ); }); + it("should delete user by username", fakeAsync(() => { + // given + mockHttpClient.get.and.returnValue( + of({ + id: "user-id", + }), + ); + + mockHttpClient.delete.and.returnValue(of("")); + + // when + service.deleteUser("foo-user").subscribe(() => { + // then + expect(mockHttpClient.get).toHaveBeenCalledWith( + "https://accounts.aam-digital.net/account/foo-user", + ); + expect(mockHttpClient.delete).toHaveBeenCalledWith( + "https://accounts.aam-digital.net/account/user-id", + ); + }); + })); + it("should add the Bearer token to a request", async () => { await service.login(); diff --git a/src/app/core/session/auth/keycloak/keycloak-auth.service.ts b/src/app/core/session/auth/keycloak/keycloak-auth.service.ts index 44a3dab347..0eda99d25d 100644 --- a/src/app/core/session/auth/keycloak/keycloak-auth.service.ts +++ b/src/app/core/session/auth/keycloak/keycloak-auth.service.ts @@ -6,9 +6,9 @@ import { SessionInfo } from "../session-info"; import { KeycloakEventType, KeycloakService } from "keycloak-angular"; import { Logging } from "../../../logging/logging.service"; import { Entity } from "../../../entity/model/entity"; -import { User } from "../../../user/user"; import { ParsedJWT, parseJwt } from "../../../../session/session-utils"; import { RemoteLoginNotAvailableError } from "./remote-login-not-available.error"; +import { switchMap } from "rxjs/operators"; /** * Handles the remote session with keycloak @@ -97,16 +97,19 @@ export class KeycloakAuthService { const sessionInfo: SessionInfo = { name: parsedToken.username ?? parsedToken.sub, + id: parsedToken.sub, roles: parsedToken["_couchdb.roles"], + email: parsedToken.email, }; if (parsedToken.username) { sessionInfo.entityId = parsedToken.username.includes(":") ? parsedToken.username - : Entity.createPrefixedId(User.ENTITY_TYPE, parsedToken.username); + : // fallback for legacy config: manually add "User" entity prefix + Entity.createPrefixedId("User", parsedToken.username); } else { Logging.debug( - `User not linked with an entity (userId: ${sessionInfo.name})`, + `User not linked with an entity (userId: ${sessionInfo.id} | ${sessionInfo.name})`, ); } @@ -151,9 +154,9 @@ export class KeycloakAuthService { }); } - async getUserinfo(): Promise { + async getUserinfo(): Promise { const user = await this.keycloak.getKeycloakInstance().loadUserInfo(); - return user as KeycloakUser; + return user as KeycloakUserDto; } setEmail(email: string): Observable { @@ -162,19 +165,29 @@ export class KeycloakAuthService { }); } - createUser(user: Partial): Observable { + createUser(user: Partial): Observable { return this.httpClient.post(`${environment.account_url}/account`, user); } - updateUser(userId: string, user: Partial): Observable { + deleteUser(username: string): Observable { + return this.getUser(username).pipe( + switchMap((value) => + this.httpClient.delete( + `${environment.account_url}/account/${value.id}`, + ), + ), + ); + } + + updateUser(userId: string, user: Partial): Observable { return this.httpClient.put( `${environment.account_url}/account/${userId}`, user, ); } - getUser(username: string): Observable { - return this.httpClient.get( + getUser(username: string): Observable { + return this.httpClient.get( `${environment.account_url}/account/${username}`, ); } @@ -210,10 +223,12 @@ export interface Role { } /** - * Extract of Keycloak user object. + * Extract of Keycloak user object as provided by the external Keycloak Service. * See {@link https://www.keycloak.org/docs-api/19.0.3/rest-api/index.html#_userrepresentation} + * + * These fields overlap with our internal `SessionInfo` interface that is seen as abstracted from Keycloak. */ -export interface KeycloakUser { +export interface KeycloakUserDto { id: string; username: string; email: string; diff --git a/src/app/core/session/auth/local/local-auth.service.spec.ts b/src/app/core/session/auth/local/local-auth.service.spec.ts index 2e5a1f127f..eafcb17cf2 100644 --- a/src/app/core/session/auth/local/local-auth.service.spec.ts +++ b/src/app/core/session/auth/local/local-auth.service.spec.ts @@ -18,6 +18,7 @@ describe("LocalAuthService", () => { localStorage.clear(); testUser = { name: TEST_USER, + id: "101", roles: ["user_app"], }; service.saveUser(testUser); diff --git a/src/app/core/session/auth/session-info.ts b/src/app/core/session/auth/session-info.ts index 31089b8d19..75656b7b2f 100644 --- a/src/app/core/session/auth/session-info.ts +++ b/src/app/core/session/auth/session-info.ts @@ -6,8 +6,14 @@ import { BehaviorSubject } from "rxjs"; * This is retrieved during the login process and will always be present once state changes to `LOGGED_IN`. */ export interface SessionInfo { + /* + * The user account id from the auth server (e.g. User ID in Keycloak) + */ + id: string; + /** * Name of user account. + * @deprecated Use id or entityId instead as this is an unpredictable mix from different sources */ name: string; diff --git a/src/app/core/session/login/login.component.spec.ts b/src/app/core/session/login/login.component.spec.ts index 18ecd9c933..05c972ea44 100644 --- a/src/app/core/session/login/login.component.spec.ts +++ b/src/app/core/session/login/login.component.spec.ts @@ -83,7 +83,7 @@ describe("LoginComponent", () => { }); it("should show offline login if remote login fails", fakeAsync(() => { - const mockUsers = [{ name: "test", roles: [] }]; + const mockUsers: SessionInfo[] = [{ name: "test", id: "101", roles: [] }]; spyOn(sessionManager, "getOfflineUsers").and.returnValue(mockUsers); spyOn(sessionManager, "remoteLoginAvailable").and.returnValue(true); const remoteLoginSubject = new Subject(); @@ -102,7 +102,7 @@ describe("LoginComponent", () => { })); it("should show offline login after 5 seconds", fakeAsync(() => { - const mockUsers = [{ name: "test", roles: [] }]; + const mockUsers: SessionInfo[] = [{ name: "test", id: "101", roles: [] }]; spyOn(sessionManager, "getOfflineUsers").and.returnValue(mockUsers); loginState.next(LoginState.LOGGED_OUT); diff --git a/src/app/core/session/login/login.component.ts b/src/app/core/session/login/login.component.ts index e55c9a163b..7104f6e97d 100644 --- a/src/app/core/session/login/login.component.ts +++ b/src/app/core/session/login/login.component.ts @@ -56,7 +56,7 @@ import { race, timer } from "rxjs"; }) export class LoginComponent implements OnInit { offlineUsers: SessionInfo[] = []; - enableOfflineLogin = !this.sessionManager.remoteLoginAvailable(); + enableOfflineLogin: boolean; loginInProgress = false; constructor( @@ -66,6 +66,8 @@ export class LoginComponent implements OnInit { public loginState: LoginStateSubject, public siteSettingsService: SiteSettingsService, ) { + this.enableOfflineLogin = !this.sessionManager.remoteLoginAvailable(); + sessionManager .remoteLogin() .then(() => sessionManager.clearRemoteSessionIfNecessary()); diff --git a/src/app/core/session/session-service/session-manager.service.spec.ts b/src/app/core/session/session-service/session-manager.service.spec.ts index 1760d9508f..8f0afa19df 100644 --- a/src/app/core/session/session-service/session-manager.service.spec.ts +++ b/src/app/core/session/session-service/session-manager.service.spec.ts @@ -35,10 +35,9 @@ import { NAVIGATOR_TOKEN } from "../../../utils/di-tokens"; import { CurrentUserSubject } from "../current-user-subject"; import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service"; import { mockEntityMapper } from "../../entity/entity-mapper/mock-entity-mapper-service"; -import { User } from "../../user/user"; import { TEST_USER } from "../../user/demo-user-generator.service"; -import { Child } from "../../../child-dev-project/children/model/child"; import { Config } from "../../config/config"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("SessionManagerService", () => { let service: SessionManagerService; @@ -53,7 +52,7 @@ describe("SessionManagerService", () => { let initIndexedSpy: jasmine.Spy; beforeEach(waitForAsync(() => { - dbUser = { name: TEST_USER, roles: ["user_app"] }; + dbUser = { name: TEST_USER, id: "99", roles: ["user_app"] }; mockKeycloak = jasmine.createSpyObj(["login", "logout", "addAuthHeader"]); mockKeycloak.login.and.resolveTo(dbUser); mockNavigator = { onLine: true }; @@ -109,6 +108,7 @@ describe("SessionManagerService", () => { it("should update the session info once authenticated", async () => { const updatedUser: SessionInfo = { name: TEST_USER, + id: "101", roles: dbUser.roles.concat("admin"), }; mockKeycloak.login.and.resolveTo(updatedUser); @@ -124,14 +124,15 @@ describe("SessionManagerService", () => { it("should initialize current user as the entity to which a login is connected", async () => { const entityMapper = TestBed.inject(EntityMapperService); - const loggedInUser = new User(TEST_USER); - const otherUser = new User("other_user"); + const loggedInUser = new TestEntity(TEST_USER); + const otherUser = new TestEntity("other_user"); await entityMapper.saveAll([loggedInUser, otherUser]); const currentUser = TestBed.inject(CurrentUserSubject); // first login with existing user entity mockKeycloak.login.and.resolveTo({ name: TEST_USER, + id: "101", roles: [], entityId: loggedInUser.getId(), }); @@ -142,10 +143,11 @@ describe("SessionManagerService", () => { await service.logout(); expect(currentUser.value).toBeUndefined(); - const adminUser = new User("admin-user"); + const adminUser = new TestEntity("admin-user"); // login, user entity not available yet mockKeycloak.login.and.resolveTo({ name: "admin-user", + id: "101", roles: ["admin"], entityId: adminUser.getId(), }); @@ -160,7 +162,11 @@ describe("SessionManagerService", () => { it("should not initialize the user entity if no entityId is set", async () => { const loadSpy = spyOn(TestBed.inject(EntityMapperService), "load"); - mockKeycloak.login.and.resolveTo({ name: "some-user", roles: [] }); + mockKeycloak.login.and.resolveTo({ + name: "some-user", + id: "101", + roles: [], + }); await service.remoteLogin(); expect(loadSpy).not.toHaveBeenCalled(); @@ -169,14 +175,15 @@ describe("SessionManagerService", () => { }); it("should allow other entities to log in", async () => { - const loggedInChild = new Child("123"); + const loggedInChild = new TestEntity("123"); const childSession: SessionInfo = { name: loggedInChild.getId(), + id: "101", roles: [], entityId: loggedInChild.getId(), }; mockKeycloak.login.and.resolveTo(childSession); - const otherChild = new Child("456"); + const otherChild = new TestEntity("456"); await TestBed.inject(EntityMapperService).saveAll([ loggedInChild, otherChild, diff --git a/src/app/core/session/session-service/session-manager.service.ts b/src/app/core/session/session-service/session-manager.service.ts index 4d7f22293c..524b0c5388 100644 --- a/src/app/core/session/session-service/session-manager.service.ts +++ b/src/app/core/session/session-service/session-manager.service.ts @@ -113,8 +113,8 @@ export class SessionManagerService { const entityType = Entity.extractTypeFromId(entityId); this.entityMapper .load(entityType, entityId) - .then((res) => this.currentUser.next(res)) - .catch(() => undefined); + .catch(() => undefined) + .then((res) => this.currentUser.next(res)); this.updateSubscription = this.entityMapper .receiveUpdates(entityType) .pipe( diff --git a/src/app/core/site-settings/site-settings.ts b/src/app/core/site-settings/site-settings.ts index f328eb8aa7..35be3a3250 100644 --- a/src/app/core/site-settings/site-settings.ts +++ b/src/app/core/site-settings/site-settings.ts @@ -11,7 +11,7 @@ import { ConfigurableEnumValue } from "../basic-datatypes/configurable-enum/conf @DatabaseEntity("SiteSettings") export class SiteSettings extends Entity { static ENTITY_ID = "global"; - static label = $localize`Site settings`; + static override label = $localize`Site settings`; static create(value: Partial): SiteSettings { return Object.assign(new SiteSettings(), value); @@ -60,7 +60,7 @@ export class SiteSettings extends Entity { super(SiteSettings.ENTITY_ID); } - toString() { + override toString() { return this.getConstructor().label; } } diff --git a/src/app/core/support/support/support.component.html b/src/app/core/support/support/support.component.html index 65c08b6c1c..77cd314daf 100644 --- a/src/app/core/support/support/support.component.html +++ b/src/app/core/support/support/support.component.html @@ -15,11 +15,21 @@

Technical User Support Details

Session - {{ sessionInfo?.name ?? "-" }} + {{ sessionInfo?.id ?? "-" }} Username - {{ currentUser?.toString() ?? "-" }} + {{ sessionInfo?.email ?? "-" }} + + + User Profile + + @if (currentUser) { + {{ sessionInfo?.entityId }} ({{ currentUser?.toString() }}) + } @else { + - + } + App version diff --git a/src/app/core/support/support/support.component.spec.ts b/src/app/core/support/support/support.component.spec.ts index a11bce91fc..6c74464e5a 100644 --- a/src/app/core/support/support/support.component.spec.ts +++ b/src/app/core/support/support/support.component.spec.ts @@ -11,9 +11,13 @@ import { BehaviorSubject, of } from "rxjs"; import { SwUpdate } from "@angular/service-worker"; import { LOCATION_TOKEN, WINDOW_TOKEN } from "../../../utils/di-tokens"; import { ConfirmationDialogService } from "../../common-components/confirmation-dialog/confirmation-dialog.service"; -import { HttpClient } from "@angular/common/http"; +import { + HttpClient, + provideHttpClient, + withInterceptorsFromDi, +} from "@angular/common/http"; import { MatDialogModule } from "@angular/material/dialog"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { provideHttpClientTesting } from "@angular/common/http/testing"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; import { PouchDatabase } from "../../database/pouch-database"; import { BackupService } from "../../admin/backup/backup.service"; @@ -21,9 +25,9 @@ import { DownloadService } from "../../export/download-service/download.service" import { SyncService } from "../../database/sync.service"; import { KeycloakAuthService } from "../../session/auth/keycloak/keycloak-auth.service"; import { SyncStateSubject } from "../../session/session-type"; -import { User } from "../../user/user"; +import { Entity } from "../../entity/model/entity"; import { CurrentUserSubject } from "../../session/current-user-subject"; -import { SessionSubject } from "../../session/auth/session-info"; +import { SessionInfo, SessionSubject } from "../../session/auth/session-info"; import { TEST_USER } from "../../user/demo-user-generator.service"; class MockDeleteRequest { @@ -36,8 +40,8 @@ class MockDeleteRequest { describe("SupportComponent", () => { let component: SupportComponent; let fixture: ComponentFixture; - const testUser = { name: TEST_USER, roles: [] }; - const userEntity = new User(TEST_USER); + const testUser: SessionInfo = { name: TEST_USER, id: TEST_USER, roles: [] }; + const userEntity = new Entity(TEST_USER); const mockSW = { isEnabled: false }; let mockDB: jasmine.SpyObj; const mockWindow = { @@ -62,12 +66,7 @@ describe("SupportComponent", () => { } as any); mockLocation = {}; await TestBed.configureTestingModule({ - imports: [ - SupportComponent, - MatDialogModule, - HttpClientTestingModule, - NoopAnimationsModule, - ], + imports: [SupportComponent, MatDialogModule, NoopAnimationsModule], providers: [ { provide: SessionSubject, @@ -84,6 +83,8 @@ describe("SupportComponent", () => { { provide: BackupService, useValue: null }, { provide: DownloadService, useValue: null }, SyncStateSubject, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), ], }).compileComponents(); }); diff --git a/src/app/core/support/support/support.component.ts b/src/app/core/support/support/support.component.ts index 1e750dfb0b..8eadd92999 100644 --- a/src/app/core/support/support/support.component.ts +++ b/src/app/core/support/support/support.component.ts @@ -36,7 +36,7 @@ export class SupportComponent implements OnInit { storageInfo: string; swStatus: string; swLog = "not available"; - userAgent = this.window.navigator.userAgent; + userAgent: string; appVersion: string; dbInfo: string; @@ -55,6 +55,7 @@ export class SupportComponent implements OnInit { ) {} ngOnInit() { + this.userAgent = this.window.navigator.userAgent; this.sessionInfo = this.sessionSubject.value; this.currentUser = this.currentUserSubject.value; this.appVersion = environment.appVersion; @@ -130,7 +131,11 @@ export class SupportComponent implements OnInit { sendReport() { // This is sent even without submitting the crash report. Sentry.captureMessage("report information", { - user: { name: this.sessionInfo?.name }, + user: { + id: this.sessionInfo?.id, + email: this.sessionInfo?.email, + name: this.sessionInfo?.name, + }, level: "debug", extra: { currentUser: this.currentUser?.getId(), @@ -148,7 +153,7 @@ export class SupportComponent implements OnInit { Sentry.showReportDialog({ user: { name: this.sessionInfo?.name, - email: "example@email.com", + email: this.sessionInfo?.email, }, title: $localize`:Title user feedback dialog:Support request`, subtitle: $localize`:Subtitle user feedback dialog:Please describe the problem you are facing.`, diff --git a/src/app/core/ui/dialog-view/dialog-view.component.ts b/src/app/core/ui/dialog-view/dialog-view.component.ts index 97d17e497a..c020b9357f 100644 --- a/src/app/core/ui/dialog-view/dialog-view.component.ts +++ b/src/app/core/ui/dialog-view/dialog-view.component.ts @@ -73,7 +73,7 @@ export class DialogViewComponent extends AbstractViewComponent { }; } - componentInjector: Injector | undefined; + declare componentInjector: Injector | undefined; } export interface DialogViewData { diff --git a/src/app/core/ui/latest-changes/changelog/MarkedRendererCustom.ts b/src/app/core/ui/latest-changes/changelog/MarkedRendererCustom.ts index 70f9d0a5f6..202d4bd586 100644 --- a/src/app/core/ui/latest-changes/changelog/MarkedRendererCustom.ts +++ b/src/app/core/ui/latest-changes/changelog/MarkedRendererCustom.ts @@ -1,7 +1,7 @@ import { MarkedRenderer } from "ngx-markdown"; export class MarkedRendererCustom extends MarkedRenderer { - public heading( + public override heading( text: string, level: 1 | 2 | 3 | 4 | 5 | 6, raw: string, @@ -20,7 +20,7 @@ export class MarkedRendererCustom extends MarkedRenderer { } } - public list(body: string, ordered: boolean, start: number): string { + public override list(body: string, ordered: boolean, start: number): string { if (ordered) { return `
    ${body}
`; } else { diff --git a/src/app/core/ui/primary-action/primary-action.component.ts b/src/app/core/ui/primary-action/primary-action.component.ts index 57e2141e4f..7a60a2ddad 100644 --- a/src/app/core/ui/primary-action/primary-action.component.ts +++ b/src/app/core/ui/primary-action/primary-action.component.ts @@ -38,6 +38,6 @@ export class PrimaryActionComponent { } private createNewNote() { - return new Note(Date.now().toString()); + return new Note(); } } diff --git a/src/app/core/ui/routed-view/routed-view.component.html b/src/app/core/ui/routed-view/routed-view.component.html index e08eea64f0..b191bfbdb1 100644 --- a/src/app/core/ui/routed-view/routed-view.component.html +++ b/src/app/core/ui/routed-view/routed-view.component.html @@ -1,4 +1,4 @@ -
+
{ let service: SearchService; - const childToStringBefore = Child.toStringAttributes; + const childToStringBefore = TestEntity.toStringAttributes; const csrToStringBefore = ChildSchoolRelation.toStringAttributes; beforeEach(() => { @@ -19,7 +19,7 @@ describe("SearchService", () => { }); afterEach(() => { - Child.toStringAttributes = childToStringBefore; + TestEntity.toStringAttributes = childToStringBefore; ChildSchoolRelation.toStringAttributes = csrToStringBefore; return TestBed.inject(Database).destroy(); }); @@ -47,9 +47,9 @@ describe("SearchService", () => { it("should allow to search for toStringAttributes that are not the entityId", async () => { ChildSchoolRelation.toStringAttributes = ["entityId"]; - Child.toStringAttributes = ["name"]; - const c1 = Child.create("first"); - const c2 = Child.create("second"); + TestEntity.toStringAttributes = ["name"]; + const c1 = TestEntity.create("first"); + const c2 = TestEntity.create("second"); const r = new ChildSchoolRelation("relation"); await runSearchTest("firs", [c1], [c1, c2, r]); @@ -57,51 +57,51 @@ describe("SearchService", () => { }); it("should only index on database properties", async () => { - Child.toStringAttributes = ["schoolId", "name"]; - const child = Child.create("test"); - child.schoolId = ["someSchool"]; + TestEntity.toStringAttributes = ["schoolId", "name"]; + const child = TestEntity.create("test"); + child["schoolId"] = ["someSchool"]; await runSearchTest("someSchool", [], [child]); await runSearchTest("test", [child]); }); it("should not fail if toStringAttribute is not set", async () => { - Child.toStringAttributes = ["projectNumber", "name"]; - const child = Child.create("test"); + TestEntity.toStringAttributes = ["other", "name"]; + const child = TestEntity.create("test"); await runSearchTest("test", [child], [child]); }); it("should include properties that are marked searchable", async () => { - Child.toStringAttributes = ["name"]; - Child.schema.get("projectNumber").searchable = true; - const child = Child.create("test"); - child.projectNumber = "number"; + TestEntity.toStringAttributes = ["name"]; + TestEntity.schema.get("other").searchable = true; + const child = TestEntity.create("test"); + child.other = "number"; await runSearchTest("tes", [child], [child]); await runSearchTest("numb", [child]); - delete Child.schema.get("projectNumber").searchable; + delete TestEntity.schema.get("other").searchable; }); it("should support search terms with multiple words", async () => { - Child.toStringAttributes = ["name", "projectNumber"]; - const child = Child.create("test"); - child.projectNumber = "number"; + TestEntity.toStringAttributes = ["name", "other"]; + const child = TestEntity.create("test"); + child.other = "number"; await runSearchTest("tes num", [child], [child]); }); it("should allows searches for properties with multiple words", async () => { - Child.toStringAttributes = ["name"]; - const child = Child.create("test name"); + TestEntity.toStringAttributes = ["name"]; + const child = TestEntity.create("test name"); await runSearchTest("nam", [child], [child]); }); it("should not return the same entity multiple times", async () => { - Child.toStringAttributes = ["name"]; - const child = Child.create("Peter Petersilie"); + TestEntity.toStringAttributes = ["name"]; + const child = TestEntity.create("Peter Petersilie"); await runSearchTest("peter", [child], [child]); }); diff --git a/src/app/core/user/demo-user-generator.service.ts b/src/app/core/user/demo-user-generator.service.ts index 86121b23cc..9a4be58a1e 100644 --- a/src/app/core/user/demo-user-generator.service.ts +++ b/src/app/core/user/demo-user-generator.service.ts @@ -1,7 +1,8 @@ import { DemoDataGenerator } from "../demo-data/demo-data-generator"; import { Injectable } from "@angular/core"; -import { User } from "./user"; import { faker } from "../demo-data/faker"; +import { Entity } from "../entity/model/entity"; +import { createEntityOfType } from "../demo-data/create-entity-of-type"; export const TEST_USER = "demo"; @@ -9,7 +10,7 @@ export const TEST_USER = "demo"; * Generate demo users for the application with its DemoDataModule. */ @Injectable() -export class DemoUserGeneratorService extends DemoDataGenerator { +export class DemoUserGeneratorService extends DemoDataGenerator { /** the username of the basic account generated by this demo service */ static DEFAULT_USERNAME = TEST_USER; static ADMIN_USERNAME = "demo-admin"; @@ -28,12 +29,18 @@ export class DemoUserGeneratorService extends DemoDataGenerator { /** * Generate User entities to be loaded by the DemoDataModule. */ - public generateEntities(): User[] { + public generateEntities(): Entity[] { const users = []; - const demoUser = new User(DemoUserGeneratorService.DEFAULT_USERNAME); + const demoUser = createEntityOfType( + "User", + DemoUserGeneratorService.DEFAULT_USERNAME, + ); demoUser.name = DemoUserGeneratorService.DEFAULT_USERNAME; - const demoAdmin = new User(DemoUserGeneratorService.ADMIN_USERNAME); + const demoAdmin = createEntityOfType( + "User", + DemoUserGeneratorService.ADMIN_USERNAME, + ); demoAdmin.name = DemoUserGeneratorService.ADMIN_USERNAME; users.push(demoUser, demoAdmin); @@ -43,9 +50,9 @@ export class DemoUserGeneratorService extends DemoDataGenerator { userNames.add(faker.person.firstName()); } for (const name of userNames) { - const user = new User(name); + const user = createEntityOfType("User", name); user.name = name; - user["phone"] = faker.phone.number(); + user.phone = faker.phone.number(); users.push(user); } diff --git a/src/app/core/user/user-account/user-account.component.html b/src/app/core/user/user-account/user-account.component.html index faaa13a4be..bff639537c 100644 --- a/src/app/core/user/user-account/user-account.component.html +++ b/src/app/core/user/user-account/user-account.component.html @@ -1,45 +1,46 @@ - - -
+
+ Username + + + + {{ (sessionInfo | async)?.name }} +
-
-
-

Your profile:

+
+ +

Your profile:

+ @if (currentUser | async) { -
+ } @else if ((sessionInfo | async)?.entityId) { +
+ Couldn't find the record that your user account is linked to ({{ + (sessionInfo | async)?.entityId + }}). +
+ } @else { +
Your user account is not linked to any record.
+ } diff --git a/src/app/core/user/user-account/user-account.component.ts b/src/app/core/user/user-account/user-account.component.ts index 4b976a7f2d..af44634c80 100644 --- a/src/app/core/user/user-account/user-account.component.ts +++ b/src/app/core/user/user-account/user-account.component.ts @@ -50,21 +50,16 @@ import { SessionSubject } from "../../session/auth/session-info"; standalone: true, }) export class UserAccountComponent implements OnInit { - /** user to be edited */ - username: string; - passwordChangeDisabled = false; - tooltipText; - entityId = this.sessionInfo.value.entityId; + tooltipText: string; constructor( - private currentUser: CurrentUserSubject, - private sessionInfo: SessionSubject, + protected currentUser: CurrentUserSubject, + protected sessionInfo: SessionSubject, ) {} ngOnInit() { this.checkIfPasswordChangeAllowed(); - this.username = this.currentUser.value?.toString(); } checkIfPasswordChangeAllowed() { diff --git a/src/app/core/user/user-security/user-security.component.spec.ts b/src/app/core/user/user-security/user-security.component.spec.ts index 56fc17c158..55d97e08bf 100644 --- a/src/app/core/user/user-security/user-security.component.spec.ts +++ b/src/app/core/user/user-security/user-security.component.spec.ts @@ -11,13 +11,13 @@ import { MockedTestingModule } from "../../../utils/mocked-testing.module"; import { HttpClient, HttpErrorResponse } from "@angular/common/http"; import { KeycloakAuthService, - KeycloakUser, + KeycloakUserDto, Role, } from "../../session/auth/keycloak/keycloak-auth.service"; import { BehaviorSubject, of, throwError } from "rxjs"; -import { User } from "../user"; import { SessionSubject } from "../../session/auth/session-info"; import { environment } from "../../../../environments/environment"; +import { Entity } from "../../entity/model/entity"; describe("UserSecurityComponent", () => { let component: UserSecurityComponent; @@ -33,8 +33,8 @@ describe("UserSecurityComponent", () => { name: "Not Assigned Role", description: "this role is not assigned to the user", }; - const user = Object.assign(new User(), { username: "test-user" }); - let keycloakUser: KeycloakUser; + const user = Object.assign(new Entity(), { username: "test-user" }); + let keycloakUser: KeycloakUserDto; beforeEach(async () => { keycloakUser = { diff --git a/src/app/core/user/user-security/user-security.component.ts b/src/app/core/user/user-security/user-security.component.ts index f22cb71fd3..c1d9fa1e03 100644 --- a/src/app/core/user/user-security/user-security.component.ts +++ b/src/app/core/user/user-security/user-security.component.ts @@ -3,12 +3,13 @@ import { DynamicComponent } from "../../config/dynamic-components/dynamic-compon import { FormBuilder, FormControl, + FormGroup, ReactiveFormsModule, Validators, } from "@angular/forms"; import { KeycloakAuthService, - KeycloakUser, + KeycloakUserDto, Role, } from "../../session/auth/keycloak/keycloak-auth.service"; import { AlertService } from "../../alerts/alert.service"; @@ -45,13 +46,9 @@ import { environment } from "../../../../environments/environment"; }) export class UserSecurityComponent implements OnInit { @Input() entity: Entity; - form = this.fb.group({ - username: [{ value: "", disabled: true }], - email: ["", [Validators.required, Validators.email]], - roles: new FormControl([], Validators.required), - }); + form: FormGroup; availableRoles: Role[] = []; - user: KeycloakUser; + user: KeycloakUserDto; editing = true; userIsPermitted = false; @@ -62,6 +59,12 @@ export class UserSecurityComponent implements OnInit { private alertService: AlertService, private http: HttpClient, ) { + this.form = this.fb.group({ + username: [{ value: "", disabled: true }], + email: ["", [Validators.required, Validators.email]], + roles: new FormControl([], Validators.required), + }); + if ( sessionInfo.value?.roles.includes( KeycloakAuthService.ACCOUNT_MANAGER_ROLE, @@ -107,7 +110,7 @@ export class UserSecurityComponent implements OnInit { }); } - private assignUser(user: KeycloakUser) { + private assignUser(user: KeycloakUserDto) { this.user = user; this.initializeForm(); if (this.user) { @@ -162,7 +165,7 @@ export class UserSecurityComponent implements OnInit { this.form.get("email").value }`, ); - this.user = user as KeycloakUser; + this.user = user as KeycloakUserDto; this.disableForm(); }, error: ({ error }) => this.form.setErrors({ failed: error.message }), @@ -184,7 +187,10 @@ export class UserSecurityComponent implements OnInit { } } - private updateKeycloakUser(update: Partial, message: string) { + private updateKeycloakUser( + update: Partial, + message: string, + ) { this.authService.updateUser(this.user.id, update).subscribe({ next: () => { this.alertService.addInfo(message); @@ -199,7 +205,7 @@ export class UserSecurityComponent implements OnInit { }); } - getFormValues(): Partial { + getFormValues(): Partial { if (this.form.invalid) { this.form.markAllAsTouched(); return; diff --git a/src/app/core/user/user-security/user-security.stories.ts b/src/app/core/user/user-security/user-security.stories.ts index c5f00cabae..3efe102b08 100644 --- a/src/app/core/user/user-security/user-security.stories.ts +++ b/src/app/core/user/user-security/user-security.stories.ts @@ -1,7 +1,5 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { UserSecurityComponent } from "./user-security.component"; -import { StorybookBaseModule } from "../../../utils/storybook-base.module"; -import { User } from "../user"; import { importProvidersFrom } from "@angular/core"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { KeycloakAuthService } from "app/core/session/auth/keycloak/keycloak-auth.service"; @@ -11,6 +9,7 @@ import { SessionInfo, SessionSubject, } from "app/core/session/auth/session-info"; +import { Entity } from "app/core/entity/model/entity"; export default { title: "Core/Admin/User Security", @@ -34,6 +33,7 @@ export default { useValue: new BehaviorSubject({ roles: [KeycloakAuthService.ACCOUNT_MANAGER_ROLE], name: "tester", + id: "tester", }), }, ], @@ -49,5 +49,5 @@ const Template: StoryFn = ( export const NotRegistered = Template.bind({}); NotRegistered.args = { - entity: new User(), + entity: new Entity(), }; diff --git a/src/app/core/user/user.spec.ts b/src/app/core/user/user.spec.ts deleted file mode 100644 index 17323fdd8b..0000000000 --- a/src/app/core/user/user.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { User } from "./user"; -import { testEntitySubclass } from "../entity/model/entity.spec"; - -describe("User", () => { - testEntitySubclass("User", User, { - _id: "User:tester", - - name: "tester", - }); -}); diff --git a/src/app/core/user/user.ts b/src/app/core/user/user.ts deleted file mode 100644 index 81542769b3..0000000000 --- a/src/app/core/user/user.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - -import { Entity } from "../entity/model/entity"; -import { DatabaseEntity } from "../entity/database-entity.decorator"; -import { DatabaseField } from "../entity/database-field.decorator"; -import { IconName } from "@fortawesome/fontawesome-svg-core"; - -/** - * Entity representing a User object including password. - * - * Note that in addition to the User Entity there also is a "regular" CouchDB user with the same name and password - * in the CouchDB _users database which is used for remote database authentication. - * - * @Deprecated: This entity type does not provide special functionality anymore and will be replaced by a config-only type in the future - */ -@DatabaseEntity("User") -export class User extends Entity { - static toStringAttributes = ["name"]; - static icon: IconName = "user"; - static label = $localize`:label for entity:User`; - static labelPlural = $localize`:label (plural) for entity:Users`; - static override hasPII = true; - - /** username used for login and identification */ - @DatabaseField({ - label: $localize`:Label of username:Username`, - validators: { required: true, uniqueId: "User" }, - }) - name: string; -} diff --git a/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.spec.ts b/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.spec.ts index 8af565bf07..88ccfade9f 100644 --- a/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.spec.ts +++ b/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.spec.ts @@ -7,7 +7,6 @@ import { import { BirthdayDashboardComponent } from "./birthday-dashboard.component"; import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; -import { Child } from "../../../../child-dev-project/children/model/child"; import moment from "moment"; import { ConfigService } from "../../../../core/config/config.service"; import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; @@ -19,6 +18,7 @@ import { } from "../../../../core/entity/entity-mapper/mock-entity-mapper-service"; import { DatabaseEntity } from "../../../../core/entity/database-entity.decorator"; import { DateWithAge } from "../../../../core/basic-datatypes/date-with-age/dateWithAge"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; describe("BirthdayDashboardComponent", () => { let component: BirthdayDashboardComponent; @@ -39,6 +39,9 @@ describe("BirthdayDashboardComponent", () => { beforeEach(() => { fixture = TestBed.createComponent(BirthdayDashboardComponent); component = fixture.componentInstance; + + component.entities = { [TestEntity.ENTITY_TYPE]: "dateOfBirth" }; + fixture.detectChanges(); }); @@ -51,13 +54,13 @@ describe("BirthdayDashboardComponent", () => { .subtract(10, "years") .add(5, "days") .startOf("day"); - const child1 = new Child(); + const child1 = new TestEntity(); child1.dateOfBirth = new DateWithAge(birthdaySoon.toDate()); const birthdayFarAway = moment() .subtract(15, "years") .add(5, "weeks") .startOf("day"); - const child2 = new Child(); + const child2 = new TestEntity(); child2.dateOfBirth = new DateWithAge(birthdayFarAway.toDate()); entityMapper.saveAll([child1, child2]); @@ -75,13 +78,13 @@ describe("BirthdayDashboardComponent", () => { .subtract(12, "years") .add(5, "days") .startOf("day"); - const child1 = new Child(); + const child1 = new TestEntity(); child1.dateOfBirth = new DateWithAge(firstBirthday.toDate()); const secondBirthday = moment() .subtract(15, "years") .add(2, "weeks") .startOf("day"); - const child2 = new Child(); + const child2 = new TestEntity(); child2.dateOfBirth = new DateWithAge(secondBirthday.toDate()); entityMapper.saveAll([child1, child2]); @@ -110,7 +113,7 @@ describe("BirthdayDashboardComponent", () => { e2.birthday = new DateWithAge( moment().subtract(12, "year").add(3, "day").toDate(), ); - const e3 = new Child(); + const e3 = new TestEntity(); e3.dateOfBirth = new DateWithAge( moment().subtract(8, "year").add(2, "day").toDate(), ); @@ -118,7 +121,7 @@ describe("BirthdayDashboardComponent", () => { component.entities = { BirthdayEntity: "birthday", - Child: "dateOfBirth", + [TestEntity.ENTITY_TYPE]: "dateOfBirth", }; component.ngOnInit(); tick(); diff --git a/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.ts b/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.ts index 5f0202b3f1..6e736f5a4c 100644 --- a/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.ts +++ b/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.component.ts @@ -1,6 +1,5 @@ import { Component, Input, OnInit } from "@angular/core"; import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; -import { Child } from "../../../../child-dev-project/children/model/child"; import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; import { MatTableModule } from "@angular/material/table"; import { Entity } from "../../../../core/entity/model/entity"; @@ -33,8 +32,8 @@ export class BirthdayDashboardComponent extends DashboardWidget implements BirthdayDashboardConfig, OnInit { - static getRequiredEntities(config: BirthdayDashboardConfig) { - return config?.entities ? Object.keys(config.entities) : Child.ENTITY_TYPE; + static override getRequiredEntities(config: BirthdayDashboardConfig) { + return config?.entities ? Object.keys(config.entities) : "Child"; } private readonly today: Date; @@ -46,7 +45,7 @@ export class BirthdayDashboardComponent * "entities": { "Child": "dateOfBirth" } * ``` */ - @Input() entities: EntityPropertyMap = { [Child.ENTITY_TYPE]: "dateOfBirth" }; + @Input() entities: EntityPropertyMap = { ["Child"]: "dateOfBirth" }; /** * Birthdays that are less than "threshold" days away are shown. diff --git a/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.stories.ts b/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.stories.ts index ecb3afdeed..214a30839d 100644 --- a/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.stories.ts +++ b/src/app/features/dashboard-widgets/birthday-dashboard-widget/birthday-dashboard/birthday-dashboard.stories.ts @@ -1,28 +1,28 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { BirthdayDashboardComponent } from "./birthday-dashboard.component"; import { StorybookBaseModule } from "../../../../utils/storybook-base.module"; -import { Child } from "../../../../child-dev-project/children/model/child"; import moment from "moment"; import { importProvidersFrom } from "@angular/core"; import { DateWithAge } from "app/core/basic-datatypes/date-with-age/dateWithAge"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; -const child1 = Child.create("First Child"); +const child1 = TestEntity.create("First Child"); child1.dateOfBirth = new DateWithAge( moment().subtract(10, "years").add("10", "days").toDate(), ); -const child2 = Child.create("Second Child"); +const child2 = TestEntity.create("Second Child"); child2.dateOfBirth = new DateWithAge( moment().subtract(9, "years").add("1", "month").toDate(), ); -const child3 = Child.create("Third Child"); +const child3 = TestEntity.create("Third Child"); child3.dateOfBirth = new DateWithAge( moment().subtract(11, "years").add("40", "days").toDate(), ); -const child4 = Child.create("Fifth Child"); +const child4 = TestEntity.create("Fifth Child"); child4.dateOfBirth = new DateWithAge( moment().subtract(8, "years").add("3", "months").toDate(), ); -const child5 = Child.create("Sixth Child"); +const child5 = TestEntity.create("Sixth Child"); child5.dateOfBirth = new DateWithAge( moment().subtract(10, "years").add("100", "days").toDate(), ); diff --git a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.spec.ts b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.spec.ts index fd0af4c1e9..d9582e91a1 100644 --- a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.spec.ts +++ b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.spec.ts @@ -1,16 +1,6 @@ -import { - ComponentFixture, - fakeAsync, - TestBed, - tick, - waitForAsync, -} from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { EntityCountDashboardComponent } from "./entity-count-dashboard.component"; -import { - Center, - Child, -} from "../../../../child-dev-project/children/model/child"; import { ConfigurableEnumValue } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; import { @@ -18,33 +8,34 @@ import { MockEntityMapperService, } from "../../../../core/entity/entity-mapper/mock-entity-mapper-service"; import { MockedTestingModule } from "../../../../utils/mocked-testing.module"; -import { RecurringActivity } from "../../../../child-dev-project/attendance/model/recurring-activity"; -import { defaultInteractionTypes } from "../../../../core/config/default-config/default-interaction-types"; -import { EducationalMaterial } from "../../../../child-dev-project/children/educational-material/model/educational-material"; import { Note } from "../../../../child-dev-project/notes/model/note"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; +import { Entity } from "../../../../core/entity/model/entity"; describe("EntityCountDashboardComponent", () => { let component: EntityCountDashboardComponent; let fixture: ComponentFixture; let entityMapper: MockEntityMapperService; - function createChild(center: Center) { - const child = new Child(); - child.center = center; + function createChild(c: ConfigurableEnumValue) { + const child = new TestEntity(); + child.category = c; return child; } - beforeEach(waitForAsync(() => { + beforeEach(async () => { entityMapper = mockEntityMapper(); - TestBed.configureTestingModule({ + await TestBed.configureTestingModule({ imports: [EntityCountDashboardComponent, MockedTestingModule.withState()], providers: [{ provide: EntityMapperService, useValue: entityMapper }], }).compileComponents(); - })); - beforeEach(() => { fixture = TestBed.createComponent(EntityCountDashboardComponent); component = fixture.componentInstance; + + component.entityType = TestEntity.ENTITY_TYPE; + component.groupBy = "category"; + fixture.detectChanges(); }); @@ -90,10 +81,15 @@ describe("EntityCountDashboardComponent", () => { it("should groupBy enum values and display label", async () => { const testGroupBy = "test"; - Child.schema.set(testGroupBy, { dataType: "configurable-enum" }); + TestEntity.schema.set(testGroupBy, { dataType: "configurable-enum" }); component.groupBy = testGroupBy; - const children = [new Child(), new Child(), new Child(), new Child()]; + const children = [ + new TestEntity(), + new TestEntity(), + new TestEntity(), + new TestEntity(), + ]; const c1: ConfigurableEnumValue = { label: "foo", id: "01" }; const c2: ConfigurableEnumValue = { label: "bar", id: "02" }; children[0][testGroupBy] = c1; @@ -115,24 +111,24 @@ describe("EntityCountDashboardComponent", () => { id: c2.id, }); - Child.schema.delete(testGroupBy); + TestEntity.schema.delete(testGroupBy); }); it("should groupBy entity references and display an entity-block", async () => { - const testGroupBy = "child"; + const testGroupBy = "ref"; component.groupBy = testGroupBy; - component.entityType = EducationalMaterial.ENTITY_TYPE; + component.entityType = TestEntity.ENTITY_TYPE; - const c1 = new Child(); - const x0 = new EducationalMaterial(); - const x1 = new EducationalMaterial(); + const c1 = new Entity("ref-1"); + const x0 = new TestEntity(); + const x1 = new TestEntity(); x1[testGroupBy] = c1.getId(); entityMapper.addAll([x0, x1, c1]); await component.ngOnInit(); - expect(component.groupedByEntity).toBe(Child.ENTITY_TYPE); + expect(component.groupedByEntity).toBe(TestEntity.ENTITY_TYPE); expect(component.entityGroupCounts).toHaveSize(2); expect(component.entityGroupCounts).toContain({ label: "", @@ -178,31 +174,4 @@ describe("EntityCountDashboardComponent", () => { id: "link-2", }); }); - - it("should also work with other entities", fakeAsync(() => { - spyOn(entityMapper, "loadType").and.callThrough(); - const type1 = defaultInteractionTypes[1]; - const type2 = defaultInteractionTypes[2]; - const ra1 = new RecurringActivity(); - ra1.type = type1; - const ra2 = new RecurringActivity(); - ra2.type = type1; - const ra3 = new RecurringActivity(); - ra3.type = type2; - const entity = RecurringActivity; - entityMapper.addAll([ra1, ra2, ra3]); - - component.entityType = RecurringActivity.ENTITY_TYPE; - component.groupBy = "type"; - component.ngOnInit(); - - expect(entityMapper.loadType).toHaveBeenCalledWith(entity); - tick(); - expect(component.totalEntities).toBe(3); - expect(component.entityGroupCounts).toEqual([ - { label: type1.label, id: type1.id, value: 2 }, - { label: type2.label, id: type2.id, value: 1 }, - ]); - expect(component.label).toBe(RecurringActivity.labelPlural); - })); }); diff --git a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.ts b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.ts index 89f96f5bae..4b62714021 100644 --- a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.ts +++ b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.component.ts @@ -1,7 +1,6 @@ import { Component, Input, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { ConfigurableEnumValue } from "../../../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; -import { Child } from "../../../../child-dev-project/children/model/child"; import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator"; import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; import { @@ -45,8 +44,8 @@ export class EntityCountDashboardComponent extends DashboardWidget implements EntityCountDashboardConfig, OnInit { - static getRequiredEntities(config: EntityCountDashboardConfig) { - return config?.entity || Child.ENTITY_TYPE; + static override getRequiredEntities(config: EntityCountDashboardConfig) { + return config?.entity || "Child"; } /** @@ -57,7 +56,7 @@ export class EntityCountDashboardComponent this._entity = this.entities.get(value); } - private _entity: EntityConstructor = Child; + private _entity: EntityConstructor; /** * The property of the Child entities to group counts by. * @@ -86,6 +85,10 @@ export class EntityCountDashboardComponent } async ngOnInit() { + if (!this._entity) { + this.entityType = "Child"; + } + const groupByType = this._entity.schema.get(this.groupBy); this.groupedByEntity = groupByType.dataType === EntityDatatype.dataType diff --git a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts index b53a96679b..b0c5adcfb9 100644 --- a/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts +++ b/src/app/features/dashboard-widgets/entity-count-dashboard-widget/entity-count-dashboard/entity-count-dashboard.stories.ts @@ -2,8 +2,8 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { StorybookBaseModule } from "../../../../utils/storybook-base.module"; import { importProvidersFrom } from "@angular/core"; import { EntityCountDashboardComponent } from "./entity-count-dashboard.component"; -import { Child } from "../../../../child-dev-project/children/model/child"; -import { centersUnique } from "../../../../child-dev-project/children/demo-data-generators/fixtures/centers"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; +import { genders } from "../../../../child-dev-project/children/model/genders"; export default { title: "Features/Dashboard Widgets/Entity Count Dashboard", @@ -13,11 +13,11 @@ export default { providers: [ importProvidersFrom( StorybookBaseModule.withData([ - Object.assign(new Child(), { center: centersUnique[0] }), - Object.assign(new Child(), { center: centersUnique[1] }), - Object.assign(new Child(), { center: centersUnique[1] }), - Object.assign(new Child(), { center: centersUnique[1] }), - Object.assign(new Child(), { center: centersUnique[2] }), + TestEntity.create({ category: genders[0] }), + TestEntity.create({ category: genders[1] }), + TestEntity.create({ category: genders[1] }), + TestEntity.create({ category: genders[1] }), + TestEntity.create({ category: genders[2] }), ]), ), ], diff --git a/src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts b/src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts index a8b5f5bfca..985607595e 100644 --- a/src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts +++ b/src/app/features/dashboard-widgets/progress-dashboard-widget/edit-progress-dashboard/edit-progress-dashboard.component.ts @@ -1,10 +1,11 @@ -import { Component, Inject } from "@angular/core"; +import { Component, Inject, OnInit } from "@angular/core"; import { MAT_DIALOG_DATA, MatDialogModule } from "@angular/material/dialog"; import { ProgressDashboardConfig, ProgressDashboardPart, } from "../progress-dashboard/progress-dashboard-config"; import { + FormArray, FormBuilder, FormControl, FormGroup, @@ -20,7 +21,7 @@ import { DialogCloseComponent } from "../../../../core/common-components/dialog- import { MatButtonModule } from "@angular/material/button"; import { FontAwesomeModule } from "@fortawesome/angular-fontawesome"; import { MatTooltipModule } from "@angular/material/tooltip"; -import { TypedForm } from "../../../../core/common-components/entity-form/entity-form.service"; +import { TypedFormGroup } from "../../../../core/common-components/entity-form/entity-form.service"; export interface EditProgressDashboardComponentData { title: string; @@ -45,7 +46,7 @@ export interface EditProgressDashboardComponentData { ], standalone: true, }) -export class EditProgressDashboardComponent { +export class EditProgressDashboardComponent implements OnInit { /** * This marks the control as invalid when the whole form has an error */ @@ -53,20 +54,26 @@ export class EditProgressDashboardComponent { isErrorState: (control: FormControl | null) => !control?.parent?.valid, }; - title = new FormControl(this.data.title, [Validators.required]); - parts = this.fb.array( - this.data.parts.map((part) => this.createPartForm(part)), - ); - outputData = new FormGroup({ - title: this.title, - parts: this.parts, - }); + title: FormControl; + parts: FormArray; + outputData: FormGroup; constructor( @Inject(MAT_DIALOG_DATA) private data: ProgressDashboardConfig, private fb: FormBuilder, ) {} + ngOnInit(): void { + this.title = new FormControl(this.data.title, [Validators.required]); + this.parts = this.fb.array( + this.data.parts.map((part) => this.createPartForm(part)), + ); + this.outputData = new FormGroup({ + title: this.title, + parts: this.parts, + }); + } + createPartForm(part: ProgressDashboardPart) { return this.fb.group( { @@ -87,7 +94,7 @@ export class EditProgressDashboardComponent { } currentLessThanTarget( - control: TypedForm, + control: TypedFormGroup, ): ValidationErrors | null { const current = control.get("currentValue"); const target = control.get("targetValue"); diff --git a/src/app/features/dashboard-widgets/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts b/src/app/features/dashboard-widgets/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts index 73eb66a0b7..841f74e859 100644 --- a/src/app/features/dashboard-widgets/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts +++ b/src/app/features/dashboard-widgets/progress-dashboard-widget/progress-dashboard/progress-dashboard.component.ts @@ -36,7 +36,7 @@ export class ProgressDashboardComponent extends DashboardWidget implements OnInit { - static getRequiredEntities() { + static override getRequiredEntities() { return ProgressDashboardConfig.ENTITY_TYPE; } diff --git a/src/app/features/file/display-img/display-img.component.spec.ts b/src/app/features/file/display-img/display-img.component.spec.ts index 1c4672cc94..aa80f8445f 100644 --- a/src/app/features/file/display-img/display-img.component.spec.ts +++ b/src/app/features/file/display-img/display-img.component.spec.ts @@ -1,9 +1,9 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { DisplayImgComponent } from "./display-img.component"; -import { Child } from "../../../child-dev-project/children/model/child"; import { of } from "rxjs"; import { FileService } from "../file.service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("DisplayImgComponent", () => { let component: DisplayImgComponent; @@ -27,8 +27,8 @@ describe("DisplayImgComponent", () => { }); it("should reset picture if child has none", () => { - const withPicture = new Child(); - withPicture.photo = "some-picture"; + const withPicture = new TestEntity(); + withPicture["photo"] = "some-picture"; component.entity = withPicture; component.imgProperty = "photo"; @@ -39,7 +39,7 @@ describe("DisplayImgComponent", () => { mockFileService.loadFile.calls.reset(); // without picture - component.entity = new Child(); + component.entity = new TestEntity(); component.ngOnChanges({ entity: undefined }); diff --git a/src/app/features/file/edit-file/edit-file.component.ts b/src/app/features/file/edit-file/edit-file.component.ts index b0de4d521a..c08d6898e2 100644 --- a/src/app/features/file/edit-file/edit-file.component.ts +++ b/src/app/features/file/edit-file/edit-file.component.ts @@ -60,7 +60,7 @@ export class EditFileComponent extends EditComponent implements OnInit { super(); } - ngOnInit() { + override ngOnInit() { super.ngOnInit(); this.initialValue = this.formControl.value; this.formControl.statusChanges diff --git a/src/app/features/file/edit-photo/edit-photo.component.ts b/src/app/features/file/edit-photo/edit-photo.component.ts index 6b6823cd8d..c5e1661a3f 100644 --- a/src/app/features/file/edit-photo/edit-photo.component.ts +++ b/src/app/features/file/edit-photo/edit-photo.component.ts @@ -40,12 +40,12 @@ export class EditPhotoComponent extends EditFileComponent implements OnInit { alertService: AlertService, entityMapper: EntityMapperService, private dialog: MatDialog, - @Inject(NAVIGATOR_TOKEN) protected navigator: Navigator, + @Inject(NAVIGATOR_TOKEN) navigator: Navigator, ) { super(fileService, alertService, entityMapper, navigator); } - async onFileSelected(file: File): Promise { + override async onFileSelected(file: File): Promise { const cvs = await resizeImage(file, this.compression); this.imgPath = cvs.toDataURL(); const blob = await new Promise((res) => cvs.toBlob(res)); @@ -56,7 +56,7 @@ export class EditPhotoComponent extends EditFileComponent implements OnInit { return super.onFileSelected(reducedFile); } - ngOnInit() { + override ngOnInit() { super.ngOnInit(); this.compression = this.additional ?? this.compression; if (this.formControl.value) { @@ -69,12 +69,12 @@ export class EditPhotoComponent extends EditFileComponent implements OnInit { } } - delete() { + override delete() { this.resetPreview(this.defaultImage); super.delete(); } - protected resetFile() { + protected override resetFile() { this.resetPreview(this.initialImg); super.resetFile(); } @@ -86,7 +86,7 @@ export class EditPhotoComponent extends EditFileComponent implements OnInit { this.imgPath = resetImage; } - protected deleteExistingFile() { + protected override deleteExistingFile() { URL.revokeObjectURL(this.initialImg as string); this.initialImg = this.defaultImage; super.deleteExistingFile(); diff --git a/src/app/features/file/file.datatype.ts b/src/app/features/file/file.datatype.ts index adb2dce16b..3f9de1d5c1 100644 --- a/src/app/features/file/file.datatype.ts +++ b/src/app/features/file/file.datatype.ts @@ -27,10 +27,10 @@ export class FileDatatype extends StringDatatype { static override dataType = "file"; static override label: string = $localize`:datatype-label:file attachment`; - viewComponent = "ViewFile"; - editComponent = "EditFile"; + override viewComponent = "ViewFile"; + override editComponent = "EditFile"; - async anonymize( + override async anonymize( value: string, schemaField: EntitySchemaField, parent: any, diff --git a/src/app/features/historical-data/historical-data-components.ts b/src/app/features/historical-data/historical-data-components.ts deleted file mode 100644 index c67860d0fd..0000000000 --- a/src/app/features/historical-data/historical-data-components.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ComponentTuple } from "../../dynamic-components"; - -export const historicalDataComponents: ComponentTuple[] = [ - [ - "HistoricalDataComponent", - () => - import("./historical-data/historical-data.component").then( - (c) => c.HistoricalDataComponent, - ), - ], -]; diff --git a/src/app/features/historical-data/historical-data.module.ts b/src/app/features/historical-data/historical-data.module.ts deleted file mode 100644 index a7c62bb56a..0000000000 --- a/src/app/features/historical-data/historical-data.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from "@angular/core"; -import { ComponentRegistry } from "../../dynamic-components"; -import { historicalDataComponents } from "./historical-data-components"; -import { HistoricalEntityData } from "./model/historical-entity-data"; - -@NgModule({}) -export class HistoricalDataModule { - static databaseEntities = [HistoricalEntityData]; - - constructor(components: ComponentRegistry) { - components.addAll(historicalDataComponents); - } -} diff --git a/src/app/features/historical-data/historical-data/historical-data.component.spec.ts b/src/app/features/historical-data/historical-data/historical-data.component.spec.ts deleted file mode 100644 index ed46412877..0000000000 --- a/src/app/features/historical-data/historical-data/historical-data.component.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; - -import { HistoricalDataComponent } from "./historical-data.component"; -import { HistoricalEntityData } from "../model/historical-entity-data"; -import moment from "moment"; -import { HistoricalDataService } from "../historical-data.service"; -import { MockedTestingModule } from "../../../utils/mocked-testing.module"; -import { FormDialogService } from "../../../core/form-dialog/form-dialog.service"; -import { Child } from "../../../child-dev-project/children/model/child"; - -describe("HistoricalDataComponent", () => { - let component: HistoricalDataComponent; - let fixture: ComponentFixture; - let mockHistoricalDataService: jasmine.SpyObj; - - beforeEach(waitForAsync(() => { - mockHistoricalDataService = jasmine.createSpyObj(["getHistoricalDataFor"]); - mockHistoricalDataService.getHistoricalDataFor.and.resolveTo([]); - - TestBed.configureTestingModule({ - imports: [HistoricalDataComponent, MockedTestingModule.withState()], - providers: [ - { provide: HistoricalDataService, useValue: mockHistoricalDataService }, - { provide: FormDialogService, useValue: null }, - ], - }).compileComponents(); - })); - - beforeEach(waitForAsync(() => { - fixture = TestBed.createComponent(HistoricalDataComponent); - component = fixture.componentInstance; - - component.entity = new Child(); - fixture.detectChanges(); - })); - - it("should create", () => { - expect(component).toBeTruthy(); - }); - - it("should load the historical data", async () => { - const relatedData = new HistoricalEntityData(); - relatedData.relatedEntity = component.entity.getId(); - mockHistoricalDataService.getHistoricalDataFor.and.resolveTo([relatedData]); - - await component.ngOnInit(); - - expect(component.data).toEqual([relatedData]); - expect(mockHistoricalDataService.getHistoricalDataFor).toHaveBeenCalledWith( - component.entity.getId(), - ); - }); - - it("should generate new records with a link to the passed entity", () => { - const newEntry = component.createNewRecordFactory()(); - - expect(newEntry.relatedEntity).toBe(component.entity.getId()); - expect(moment(newEntry.date).isSame(new Date(), "day")).toBeTrue(); - }); -}); diff --git a/src/app/features/historical-data/historical-data/historical-data.component.ts b/src/app/features/historical-data/historical-data/historical-data.component.ts deleted file mode 100644 index 9542aeb66a..0000000000 --- a/src/app/features/historical-data/historical-data/historical-data.component.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Component, Input, OnInit } from "@angular/core"; -import { HistoricalEntityData } from "../model/historical-entity-data"; -import { Entity } from "../../../core/entity/model/entity"; -import { HistoricalDataService } from "../historical-data.service"; -import { DynamicComponent } from "../../../core/config/dynamic-components/dynamic-component.decorator"; -import { EntitiesTableComponent } from "../../../core/common-components/entities-table/entities-table.component"; -import { RelatedEntitiesComponent } from "../../../core/entity-details/related-entities/related-entities.component"; -import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; -import { EntityRegistry } from "../../../core/entity/database-entity.decorator"; -import { ScreenWidthObserver } from "../../../utils/media/screen-size-observer.service"; -import { FormFieldConfig } from "../../../core/common-components/entity-form/FormConfig"; -import { FilterService } from "../../../core/filter/filter.service"; - -/** - * A general component that can be included on a entity details page through the config. - * It loads all historical data related to that entity and displays it in a table. - * The columns that are displayed can be configured according to the `ColumnDescription` interface - */ -@DynamicComponent("HistoricalDataComponent") -@Component({ - selector: "app-historical-data", - templateUrl: - "../../../core/entity-details/related-entities/related-entities.component.html", - imports: [EntitiesTableComponent], - standalone: true, -}) -export class HistoricalDataComponent - extends RelatedEntitiesComponent - implements OnInit -{ - @Input() entity: Entity; - entityCtr = HistoricalEntityData; - - /** @deprecated use @Input() columns instead */ - @Input() set config(value: FormFieldConfig[]) { - if (Array.isArray(value)) { - this.columns = value; - } - } - - constructor( - private historicalDataService: HistoricalDataService, - entityMapper: EntityMapperService, - entityRegistry: EntityRegistry, - screenWidthObserver: ScreenWidthObserver, - filterService: FilterService, - ) { - super(entityMapper, entityRegistry, screenWidthObserver, filterService); - } - - override getData() { - return this.historicalDataService.getHistoricalDataFor(this.entity.getId()); - } -} diff --git a/src/app/features/historical-data/historical-data/historical-data.stories.ts b/src/app/features/historical-data/historical-data/historical-data.stories.ts deleted file mode 100644 index 1ae6520166..0000000000 --- a/src/app/features/historical-data/historical-data/historical-data.stories.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; -import { HistoricalEntityData } from "../model/historical-entity-data"; -import { HistoricalDataComponent } from "./historical-data.component"; -import { HistoricalDataService } from "../historical-data.service"; -import { ratingAnswers } from "../model/rating-answers"; -import { StorybookBaseModule } from "../../../utils/storybook-base.module"; -import { importProvidersFrom } from "@angular/core"; -import { FormFieldConfig } from "../../../core/common-components/entity-form/FormConfig"; - -export default { - title: "Features/HistoricalDataComponent", - component: HistoricalDataComponent, - decorators: [ - applicationConfig({ - providers: [ - importProvidersFrom(StorybookBaseModule), - { - provide: HistoricalDataService, - useValue: { - getHistoricalDataFor: () => - Promise.resolve([new Test(), new Test(), new Test()]), - }, - }, - ], - }), - ], -} as Meta; - -const Template: StoryFn = ( - args: HistoricalDataComponent, -) => ({ - component: HistoricalDataComponent, - props: args, -}); - -class Test extends HistoricalEntityData { - date = new Date(); - nameOfObserver = "My name"; - firstQuestion = ratingAnswers[0]; - secondQuestion = ratingAnswers[1]; - thirdQuestion = ratingAnswers[2]; - fourthQuestion = ratingAnswers[3]; - fifthQuestion = ratingAnswers[4]; - sixthQuestion = ratingAnswers[0]; - seventhQuestion = ratingAnswers[1]; - eightQuestion = ratingAnswers[2]; - ninthQuestion = ratingAnswers[3]; - tenthQuestion = ratingAnswers[4]; -} - -export const Primary = Template.bind({}); -Primary.args = { - columns: [ - { - label: "Date", - id: "date", - editComponent: "EditDate", - viewComponent: "DisplayDate", - }, - { - label: "Name of Observer", - editComponent: "EditText", - viewComponent: "DisplayText", - id: "nameOfObserver", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "firstQuestion", - label: "1. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "secondQuestion", - label: "2. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "thirdQuestion", - label: "3. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "fourthQuestion", - label: "4. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "fifthQuestion", - label: "5. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "sixthQuestion", - label: "6. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "seventhQuestion", - label: "7. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "eightQuestion", - label: "8. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "ninthQuestion", - label: "9. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - { - editComponent: "EditConfigurableEnum", - viewComponent: "DisplayConfigurableEnum", - id: "tenthQuestion", - label: "10. Question", - additional: "rating-answer", - description: "Child admits own guilt in conflict situations.", - }, - ] as FormFieldConfig[], - entries: [new Test(), new Test(), new Test()], - entity: new Test(), -}; diff --git a/src/app/features/historical-data/model/historical-entity-data.spec.ts b/src/app/features/historical-data/model/historical-entity-data.spec.ts deleted file mode 100644 index c3a95dc5ac..0000000000 --- a/src/app/features/historical-data/model/historical-entity-data.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { testEntitySubclass } from "../../../core/entity/model/entity.spec"; -import { HistoricalEntityData } from "./historical-entity-data"; - -describe("HistoricalEntityData", () => { - testEntitySubclass("HistoricalEntityData", HistoricalEntityData, { - _id: "HistoricalEntityData:some-id", - date: new Date(), - relatedEntity: "Child:some-other-id", - }); -}); diff --git a/src/app/features/historical-data/model/historical-entity-data.ts b/src/app/features/historical-data/model/historical-entity-data.ts deleted file mode 100644 index 306c0eecab..0000000000 --- a/src/app/features/historical-data/model/historical-entity-data.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Entity } from "../../../core/entity/model/entity"; -import { DatabaseEntity } from "../../../core/entity/database-entity.decorator"; -import { DatabaseField } from "../../../core/entity/database-field.decorator"; -import { PLACEHOLDERS } from "../../../core/entity/schema/entity-schema-field"; -import { Child } from "../../../child-dev-project/children/model/child"; - -/** - * A general class that represents data that is collected for a entity over time. - * Further attributes can be added through the config. - */ -@DatabaseEntity("HistoricalEntityData") -export class HistoricalEntityData extends Entity { - static override hasPII = true; - - @DatabaseField({ - label: $localize`:Label for date of historical data:Date`, - defaultValue: { - mode: "dynamic", - value: PLACEHOLDERS.NOW, - }, - anonymize: "retain-anonymized", - }) - date: Date; - - @DatabaseField({ - dataType: "entity", - additional: Child.ENTITY_TYPE, - entityReferenceRole: "composite", - anonymize: "retain", - }) - relatedEntity: string; -} diff --git a/src/app/features/location/location.datatype.ts b/src/app/features/location/location.datatype.ts index 1144019d27..0798250f39 100644 --- a/src/app/features/location/location.datatype.ts +++ b/src/app/features/location/location.datatype.ts @@ -19,14 +19,14 @@ export class LocationDatatype extends DefaultDatatype< static override dataType = "location"; static override label: string = $localize`:datatype-label:location (address + map)`; - editComponent = "EditLocation"; - viewComponent = "ViewLocation"; + override editComponent = "EditLocation"; + override viewComponent = "ViewLocation"; constructor(private geoService: GeoService) { super(); } - transformToObjectFormat(value: GeoLocation): GeoLocation { + override transformToObjectFormat(value: GeoLocation): GeoLocation { if (typeof value !== "object") { // until we have an extended location datatype that includes a custom address addition field, discard invalid values (e.g. in case datatype was changed) return undefined; @@ -43,7 +43,7 @@ export class LocationDatatype extends DefaultDatatype< return value; } - async importMapFunction(val: any): Promise { + override async importMapFunction(val: any): Promise { if (!val) { return undefined; } diff --git a/src/app/features/location/map/map-properties-popup/map-properties-popup.component.spec.ts b/src/app/features/location/map/map-properties-popup/map-properties-popup.component.spec.ts index 2aeaf6ba23..7147516429 100644 --- a/src/app/features/location/map/map-properties-popup/map-properties-popup.component.spec.ts +++ b/src/app/features/location/map/map-properties-popup/map-properties-popup.component.spec.ts @@ -7,12 +7,14 @@ import { import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; import { + DatabaseEntity, entityRegistry, EntityRegistry, } from "../../../../core/entity/database-entity.decorator"; -import { Child } from "../../../../child-dev-project/children/model/child"; -import { School } from "../../../../child-dev-project/schools/model/school"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; +import { Entity } from "../../../../core/entity/model/entity"; +import { DatabaseField } from "../../../../core/entity/database-field.decorator"; describe("MapPropertiesPopupComponent", () => { let component: MapPropertiesPopupComponent; @@ -20,21 +22,28 @@ describe("MapPropertiesPopupComponent", () => { let properties: LocationProperties = {}; let mockDialogRef: jasmine.SpyObj>; - beforeEach(async () => { - Child.schema.set("address", { + @DatabaseEntity("TestEntityWithAddress") + class TestEntityWithAddress extends Entity { + @DatabaseField({ label: "Address", dataType: "location", - }); - Child.schema.set("otherAddress", { + }) + address; + + @DatabaseField({ label: "Other address", dataType: "location", - }); - properties[Child.ENTITY_TYPE] = ["address"]; - School.schema.set("address", { + }) + otherAddress; + } + + beforeEach(async () => { + properties[TestEntityWithAddress.ENTITY_TYPE] = ["address"]; + TestEntity.schema.set("address", { label: "School address", dataType: "location", }); - properties[School.ENTITY_TYPE] = ["address"]; + properties[TestEntity.ENTITY_TYPE] = ["address"]; mockDialogRef = jasmine.createSpyObj(["close"]); await TestBed.configureTestingModule({ imports: [ @@ -55,9 +64,7 @@ describe("MapPropertiesPopupComponent", () => { }); afterEach(() => { - Child.schema.delete("address"); - Child.schema.delete("otherAddress"); - School.schema.delete("address"); + TestEntity.schema.delete("address"); }); it("should create", () => { @@ -67,7 +74,7 @@ describe("MapPropertiesPopupComponent", () => { it("should display all available properties with their labels", () => { expect(component.entityProperties).toEqual([ { - entity: Child, + entity: TestEntityWithAddress, properties: [ { name: "address", label: "Address" }, { name: "otherAddress", label: "Other address" }, @@ -75,7 +82,7 @@ describe("MapPropertiesPopupComponent", () => { selected: ["address"], }, { - entity: School, + entity: TestEntity, properties: [{ name: "address", label: "School address" }], selected: ["address"], }, @@ -83,14 +90,15 @@ describe("MapPropertiesPopupComponent", () => { }); it("should emit the selected properties", () => { - component.entityProperties.find(({ entity }) => entity === Child).selected = - ["otherAddress"]; + component.entityProperties.find( + ({ entity }) => entity === TestEntityWithAddress, + ).selected = ["otherAddress"]; component.closeDialog(); expect(mockDialogRef.close).toHaveBeenCalledWith({ - [Child.ENTITY_TYPE]: ["otherAddress"], - [School.ENTITY_TYPE]: ["address"], + [TestEntityWithAddress.ENTITY_TYPE]: ["otherAddress"], + [TestEntity.ENTITY_TYPE]: ["address"], }); }); }); diff --git a/src/app/features/location/map/map.component.spec.ts b/src/app/features/location/map/map.component.spec.ts index 15d5d4d82c..21d4dc93a9 100644 --- a/src/app/features/location/map/map.component.spec.ts +++ b/src/app/features/location/map/map.component.spec.ts @@ -9,13 +9,13 @@ import { MapComponent } from "./map.component"; import { ConfigService } from "../../../core/config/config.service"; import * as L from "leaflet"; import { Coordinates } from "../coordinates"; -import { Child } from "../../../child-dev-project/children/model/child"; import { MapConfig } from "../map-config"; import { MatDialog } from "@angular/material/dialog"; import { MapPopupConfig } from "../map-popup/map-popup.component"; import { FontAwesomeTestingModule } from "@fortawesome/angular-fontawesome/testing"; import { EMPTY, of, Subject } from "rxjs"; import { GeoLocation } from "../location.datatype"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("MapComponent", () => { let component: MapComponent; @@ -82,8 +82,8 @@ describe("MapComponent", () => { }); it("should create markers for entities and emit entity when marker is clicked", (done) => { - Child.schema.set("address", { dataType: "location" }); - const child = new Child(); + TestEntity.schema.set("address", { dataType: "location" }); + const child = new TestEntity(); child["address"] = TEST_LOCATION; component.entities = [child]; @@ -98,7 +98,7 @@ describe("MapComponent", () => { }); marker.fireEvent("click"); - Child.schema.delete("address"); + TestEntity.schema.delete("address"); }); it("should open a popup with the same marker data", async () => { @@ -114,9 +114,9 @@ describe("MapComponent", () => { it("should open a popup that allows to change the properties displayed in the map", () => { const emitSpy = spyOn(component.displayedPropertiesChange, "emit"); - Child.schema.set("address", { dataType: "location" }); - Child.schema.set("otherAddress", { dataType: "location" }); - const child = new Child(); + TestEntity.schema.set("address", { dataType: "location" }); + TestEntity.schema.set("otherAddress", { dataType: "location" }); + const child = new TestEntity(); child["address"] = TEST_LOCATION; child["otherAddress"] = { geoLookup: { lon: 99, lat: 99, display_name: "other address" }, @@ -126,11 +126,11 @@ describe("MapComponent", () => { // all location properties are selected on default expect(emitSpy).toHaveBeenCalledWith({ - [Child.ENTITY_TYPE]: ["address", "otherAddress"], + [TestEntity.ENTITY_TYPE]: ["address", "otherAddress"], }); expect(getEntityMarkers()).toHaveSize(2); - const dialogResult = { [Child.ENTITY_TYPE]: ["address"] }; + const dialogResult = { [TestEntity.ENTITY_TYPE]: ["address"] }; mockDialog.open.and.returnValue({ afterClosed: () => of(dialogResult), } as any); @@ -140,27 +140,27 @@ describe("MapComponent", () => { expect(emitSpy).toHaveBeenCalledWith(dialogResult); expect(getEntityMarkers()).toHaveSize(1); - Child.schema.delete("address"); - Child.schema.delete("otherAddress"); + TestEntity.schema.delete("address"); + TestEntity.schema.delete("otherAddress"); }); it("should only show the button to select properties if entities have been set", () => { component.displayedProperties = {}; expect(component.showPropertySelection).toBeFalse(); - component.displayedProperties = { [Child.ENTITY_TYPE]: ["address"] }; + component.displayedProperties = { [TestEntity.ENTITY_TYPE]: ["address"] }; expect(component.showPropertySelection).toBeTrue(); component.displayedProperties = {}; component.showPropertySelection = false; - component.entities = [new Child()]; + component.entities = [new TestEntity()]; expect(component.showPropertySelection).toBeTrue(); }); it("should trigger an update for the markers, once the map popup has been closed", async () => { component.displayedProperties = { - [Child.ENTITY_TYPE]: ["address", "otherAddress"], + [TestEntity.ENTITY_TYPE]: ["address", "otherAddress"], }; const dialogClosed = new Subject(); mockDialog.open.and.returnValue({ afterClosed: () => dialogClosed } as any); @@ -170,11 +170,11 @@ describe("MapComponent", () => { const popupData = mockDialog.open.calls.mostRecent().args[1] .data as MapPopupConfig; const properties = popupData.displayedProperties; - properties[Child.ENTITY_TYPE] = ["otherAddress"]; + properties[TestEntity.ENTITY_TYPE] = ["otherAddress"]; dialogClosed.next(); expect(emitSpy).toHaveBeenCalledWith({ - [Child.ENTITY_TYPE]: ["otherAddress"], + [TestEntity.ENTITY_TYPE]: ["otherAddress"], }); }); diff --git a/src/app/features/location/view-distance/view-distance.component.spec.ts b/src/app/features/location/view-distance/view-distance.component.spec.ts index d15350c687..ade36b2ec0 100644 --- a/src/app/features/location/view-distance/view-distance.component.spec.ts +++ b/src/app/features/location/view-distance/view-distance.component.spec.ts @@ -1,15 +1,15 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ViewDistanceComponent } from "./view-distance.component"; -import { Child } from "../../../child-dev-project/children/model/child"; import { Subject } from "rxjs"; import { Coordinates } from "../coordinates"; import { GeoLocation } from "../location.datatype"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; describe("ViewDistanceComponent", () => { let component: ViewDistanceComponent; let fixture: ComponentFixture; let compareCoordinates: Subject; - let entity: Child; + let entity: TestEntity; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -17,7 +17,7 @@ describe("ViewDistanceComponent", () => { }).compileComponents(); fixture = TestBed.createComponent(ViewDistanceComponent); - entity = new Child(); + entity = new TestEntity(); entity["address"] = { geoLookup: { lat: 52, lon: 13 } } as GeoLocation; compareCoordinates = new Subject(); component = fixture.componentInstance; diff --git a/src/app/features/markdown-page/markdown-page.module.ts b/src/app/features/markdown-page/markdown-page.module.ts index cc696c82bb..129c95473f 100644 --- a/src/app/features/markdown-page/markdown-page.module.ts +++ b/src/app/features/markdown-page/markdown-page.module.ts @@ -18,19 +18,23 @@ import { NgModule } from "@angular/core"; import { ComponentRegistry } from "../../dynamic-components"; import { MarkdownModule } from "ngx-markdown"; -import { HttpClient, HttpClientModule } from "@angular/common/http"; +import { + HttpClient, + provideHttpClient, + withInterceptorsFromDi, +} from "@angular/common/http"; /** * Display any information contained in a markdown file. */ @NgModule({ + exports: [MarkdownModule], imports: [ - HttpClientModule, MarkdownModule.forRoot({ loader: HttpClient, }), ], - exports: [MarkdownModule], + providers: [provideHttpClient(withInterceptorsFromDi())], }) export class MarkdownPageModule { constructor(private components: ComponentRegistry) { diff --git a/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts b/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts index 5aa9e5f45b..a3c2bc19c7 100644 --- a/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts +++ b/src/app/features/matching-entities/matching-entities/matching-entities.component.spec.ts @@ -11,7 +11,6 @@ import { MatchingEntitiesComponent } from "./matching-entities.component"; import { MatchingEntitiesConfig } from "./matching-entities-config"; import { Entity } from "../../../core/entity/model/entity"; import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; -import { Child } from "../../../child-dev-project/children/model/child"; import { ChildSchoolRelation } from "../../../child-dev-project/children/model/childSchoolRelation"; import { ActivatedRoute } from "@angular/router"; import { FormDialogService } from "../../../core/form-dialog/form-dialog.service"; @@ -20,10 +19,11 @@ import { BehaviorSubject, NEVER, Subject } from "rxjs"; import { FormFieldConfig } from "../../../core/common-components/entity-form/FormConfig"; import { Coordinates } from "../../location/coordinates"; import { MockedTestingModule } from "../../../utils/mocked-testing.module"; -import { School } from "../../../child-dev-project/schools/model/school"; import { DynamicComponentConfig } from "../../../core/config/dynamic-components/dynamic-component-config.interface"; import { Note } from "../../../child-dev-project/notes/model/note"; import { GeoLocation } from "../../location/location.datatype"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { DatabaseEntity } from "../../../core/entity/database-entity.decorator"; describe("MatchingEntitiesComponent", () => { let component: MatchingEntitiesComponent; @@ -40,8 +40,8 @@ describe("MatchingEntitiesComponent", () => { newEntityType: ChildSchoolRelation.ENTITY_TYPE, }, matchActionLabel: "match test", - rightSide: { entityType: "Child" }, - leftSide: { entityType: "School" }, + rightSide: { entityType: TestEntity.ENTITY_TYPE }, + leftSide: { entityType: TestEntity.ENTITY_TYPE }, }; const LOCATION_1: GeoLocation = { @@ -65,9 +65,7 @@ describe("MatchingEntitiesComponent", () => { { provide: ConfigService, useValue: mockConfigService }, ], }).compileComponents(); - })); - beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(MatchingEntitiesComponent); component = fixture.componentInstance; })); @@ -109,16 +107,16 @@ describe("MatchingEntitiesComponent", () => { }); it("should assign config entity to the selected entity of the side not having a table with select options", fakeAsync(() => { - const testEntity = new Entity("1"); + const testEntity = new TestEntity("1"); component.entity = testEntity; - component.rightSide = { entityType: "Child" }; + component.rightSide = { entityType: TestEntity.ENTITY_TYPE }; component.onMatch = testConfig.onMatch; fixture.detectChanges(); tick(); expect(component.sideDetails[0].selected).toEqual([testEntity]); - component.leftSide = { entityType: "Child" }; + component.leftSide = { entityType: TestEntity.ENTITY_TYPE }; component.rightSide = {}; component.ngOnInit(); tick(); @@ -127,15 +125,18 @@ describe("MatchingEntitiesComponent", () => { })); it("should init details for template including available entities table and its columns", fakeAsync(() => { - const testEntity = new Entity(); + const testEntity = new TestEntity(); component.entity = testEntity; - component.rightSide = { entityType: "Child" }; + component.rightSide = { entityType: TestEntity.ENTITY_TYPE }; component.onMatch = testConfig.onMatch; component.columns = [ ["_id", "name"], ["_rev", "phone"], ]; - const allChildren: Child[] = [Child.create("1"), Child.create("2")]; + const allChildren: TestEntity[] = [ + TestEntity.create("1"), + TestEntity.create("2"), + ]; const loadTypeSpy = spyOn(TestBed.inject(EntityMapperService), "loadType"); loadTypeSpy.and.resolveTo(allChildren); @@ -152,15 +153,15 @@ describe("MatchingEntitiesComponent", () => { expect(component.sideDetails[0].columns).toEqual(["_id", "_rev"]); expect(component.sideDetails[1].selected).toBeUndefined(); - expect(component.sideDetails[1].entityType).toEqual(Child); - expect(loadTypeSpy).toHaveBeenCalledWith(Child); + expect(component.sideDetails[1].entityType).toEqual(TestEntity); + expect(loadTypeSpy).toHaveBeenCalledWith(TestEntity); expect(component.sideDetails[1].availableEntities).toEqual(allChildren); expect(component.sideDetails[1].columns).toEqual(["name", "phone"]); })); it("should select only one entity at a time in single select mode", fakeAsync(() => { - const matchedEntity = Child.create("matched child"); - const otherMatchedEntity = Child.create("second matched child"); + const matchedEntity = TestEntity.create("matched child"); + const otherMatchedEntity = TestEntity.create("second matched child"); Object.assign(component, testConfig); component.onMatch = { @@ -180,8 +181,8 @@ describe("MatchingEntitiesComponent", () => { })); it("should select multiple entities in multiSelect mode", fakeAsync(() => { - const matchedEntity = Child.create("matched child"); - const otherMatchedEntity = Child.create("second matched child"); + const matchedEntity = TestEntity.create("matched child"); + const otherMatchedEntity = TestEntity.create("second matched child"); Object.assign(component, testConfig); component.onMatch = { @@ -202,8 +203,8 @@ describe("MatchingEntitiesComponent", () => { })); it("should create a new entity onMatch, with single entity property", fakeAsync(() => { - const testEntity = new Entity(); - const matchedEntity = Child.create("matched child"); + const testEntity = new TestEntity(); + const matchedEntity = TestEntity.create("matched child"); component.entity = testEntity; Object.assign(component, testConfig); component.onMatch = { @@ -235,8 +236,8 @@ describe("MatchingEntitiesComponent", () => { it("should create a new entity onMatch, with multiSelect entity-array property", fakeAsync(() => { const testEntity = new Entity(); - const child1 = Child.create("matched child 1"); - const child2 = Child.create("matched child 2"); + const child1 = TestEntity.create("matched child 1"); + const child2 = TestEntity.create("matched child 2"); Object.assign(component, testConfig); component.onMatch = { @@ -270,10 +271,10 @@ describe("MatchingEntitiesComponent", () => { })); it("should create distance column and publish updates", fakeAsync(() => { - Child.schema.set("address", { dataType: "location" }); - component.entity = new Child(); + TestEntity.schema.set("address", { dataType: "location" }); + component.entity = new TestEntity(); component.columns = [[undefined, "distance"]]; - component.leftSide = { entityType: Child }; + component.leftSide = { entityType: TestEntity }; component.onMatch = testConfig.onMatch; fixture.detectChanges(); @@ -295,7 +296,7 @@ describe("MatchingEntitiesComponent", () => { (res) => (newCoordinates = res), ); - const compare = new Child(); + const compare = new TestEntity(); compare["address"] = LOCATION_1; component.sideDetails[0].selectMatch(compare); @@ -304,17 +305,17 @@ describe("MatchingEntitiesComponent", () => { (compare["address"] as GeoLocation)?.geoLookup, ]); - Child.schema.delete("address"); + TestEntity.schema.delete("address"); })); it("should select an entity if it has been selected in the map", fakeAsync(() => { component.entity = new Entity(); - component.rightSide = { entityType: "Child" }; + component.rightSide = { entityType: TestEntity.ENTITY_TYPE }; component.onMatch = testConfig.onMatch; fixture.detectChanges(); tick(); - const child = new Child(); + const child = new TestEntity(); component.entityInMapClicked(child); expect(component.sideDetails[1].selected).toEqual([child]); @@ -324,13 +325,13 @@ describe("MatchingEntitiesComponent", () => { Object.assign(component, testConfig); fixture.detectChanges(); tick(); - const selectedChild = new Child(); + const selectedChild = new TestEntity(); component.sideDetails[1].selectMatch(selectedChild); expect(component.sideDetails[1].selected).toEqual([selectedChild]); const newFixture = TestBed.createComponent(MatchingEntitiesComponent); const newComponent = newFixture.componentInstance; - newComponent.entity = new Entity(); + newComponent.entity = new TestEntity(); Object.assign(newComponent, testConfig); newFixture.detectChanges(); tick(); @@ -340,15 +341,14 @@ describe("MatchingEntitiesComponent", () => { it("should update the distance calculation when the selected map properties change", fakeAsync(() => { Object.assign(component, testConfig); - Child.schema.set("address", { dataType: "location" }); - Child.schema.set("otherAddress", { dataType: "location" }); - School.schema.set("address", { dataType: "location" }); - const leftEntity = new Child(); + TestEntity.schema.set("address", { dataType: "location" }); + TestEntity.schema.set("otherAddress", { dataType: "location" }); + const leftEntity = new TestEntity(); leftEntity["address"] = LOCATION_1; leftEntity["otherAddress"] = LOCATION_2; - const rightEntity1 = new School(); + const rightEntity1 = new TestEntity(); rightEntity1["address"] = LOCATION_1; - const rightEntity2 = new School(); + const rightEntity2 = new TestEntity(); rightEntity2["address"] = LOCATION_2; spyOn(TestBed.inject(EntityMapperService), "loadType").and.resolveTo([ rightEntity1, @@ -361,7 +361,7 @@ describe("MatchingEntitiesComponent", () => { }; component.rightSide = { columns: ["distance"], - entityType: "School", + entityType: TestEntity.ENTITY_TYPE, }; fixture.detectChanges(); tick(); @@ -386,7 +386,7 @@ describe("MatchingEntitiesComponent", () => { lastLeftValue = undefined; lastRightValue = undefined; // select only one property - component.displayedLocationProperties["Child"] = ["address"]; + component.displayedLocationProperties[TestEntity.ENTITY_TYPE] = ["address"]; component.updateMarkersAndDistances(); expect(lastLeftValue).toEqual([]); @@ -407,7 +407,7 @@ describe("MatchingEntitiesComponent", () => { lastLeftValue = undefined; lastRightValue = undefined; //select both properties - component.displayedLocationProperties["Child"] = [ + component.displayedLocationProperties[TestEntity.ENTITY_TYPE] = [ "address", "otherAddress", ]; @@ -415,36 +415,41 @@ describe("MatchingEntitiesComponent", () => { expect(lastLeftValue).toEqual([ (rightEntity1["address"] as GeoLocation)?.geoLookup, + (rightEntity1["otherAddress"] as GeoLocation)?.geoLookup, ]); expect(lastRightValue).toEqual([ (leftEntity["address"] as GeoLocation)?.geoLookup, (leftEntity["otherAddress"] as GeoLocation)?.geoLookup, ]); - Child.schema.delete("otherAddress"); - Child.schema.delete("address"); - School.schema.delete("address"); + TestEntity.schema.delete("otherAddress"); + TestEntity.schema.delete("address"); + TestEntity.schema.delete("address"); flush(); })); + @DatabaseEntity("OtherEntity") + class OtherEntity extends Entity {} + it("should only display filtered entities in the map", fakeAsync(() => { - const c1 = new Child(); - c1.status = "active"; - const c2 = new Child(); - c2.status = "inactive"; - c2.dropoutDate = new Date(); - const c3 = new Child(); - c3.status = "inactive"; - const other = new School(); + const c1 = new TestEntity(); + c1.name = "active"; + const c2 = new TestEntity(); + c2.name = "inactive"; + c2.category = { id: "x", label: "inactive" }; + const c3 = new TestEntity(); + c3.name = "inactive"; + const other = new OtherEntity(); TestBed.inject(EntityMapperService).saveAll([c1, c2, c3, other]); tick(); + component.leftSide = { - entityType: "Child", - prefilter: { dropoutDate: { $exists: false } } as any, - columns: ["status"], + entityType: TestEntity.ENTITY_TYPE, + prefilter: { category: { $exists: false } } as any, + columns: ["name"], }; component.rightSide = { - entityType: "School", + entityType: OtherEntity.ENTITY_TYPE, columns: ["_id"], }; component.onMatch = testConfig.onMatch; @@ -458,7 +463,7 @@ describe("MatchingEntitiesComponent", () => { ]); component.applySelectedFilters(component.sideDetails[0], { - status: "active", + name: "active", } as any); expect(component.filteredMapEntities.map((entity) => entity)).toEqual([ @@ -469,14 +474,14 @@ describe("MatchingEntitiesComponent", () => { it("should display map if location properties are available", fakeAsync(() => { // Clean-up child schema before running test - Child.schema.forEach((schema, name) => { + TestEntity.schema.forEach((schema, name) => { if (schema.dataType === "location") { - Child.schema.delete(name); + TestEntity.schema.delete(name); } }); component.mapVisible = false; - component.entity = new Child(); - component.leftSide = { entityType: Child }; + component.entity = new TestEntity(); + component.leftSide = { entityType: TestEntity.ENTITY_TYPE }; component.onMatch = testConfig.onMatch; fixture.detectChanges(); @@ -484,14 +489,14 @@ describe("MatchingEntitiesComponent", () => { expect(component.mapVisible).toBeFalse(); - Child.schema.set("address", { dataType: "location" }); + TestEntity.schema.set("address", { dataType: "location" }); component.ngOnInit(); tick(); expect(component.mapVisible).toBeTrue(); - Child.schema.delete("address"); + TestEntity.schema.delete("address"); })); it("should not alter the config object", fakeAsync(() => { @@ -500,12 +505,18 @@ describe("MatchingEntitiesComponent", () => { ["name", "name"], ["projectNumber", "distance"], ], - rightSide: { entityType: "School", columns: ["name", "distance"] }, - leftSide: { entityType: "Child", columns: ["name", "distance"] }, + rightSide: { + entityType: TestEntity.ENTITY_TYPE, + columns: ["name", "distance"], + }, + leftSide: { + entityType: TestEntity.ENTITY_TYPE, + columns: ["name", "distance"], + }, onMatch: testConfig.onMatch, }; - Child.schema.set("address", { dataType: "location" }); - School.schema.set("address", { dataType: "location" }); + TestEntity.schema.set("address1", { dataType: "location" }); + TestEntity.schema.set("address2", { dataType: "location" }); const configCopy = JSON.parse(JSON.stringify(config)); routeData.next({ config: configCopy }); @@ -515,8 +526,8 @@ describe("MatchingEntitiesComponent", () => { expect(configCopy).toEqual(config); - Child.schema.delete("address"); - School.schema.delete("address"); + TestEntity.schema.delete("address1"); + TestEntity.schema.delete("address2"); })); it("should infer multiSelect mode from onMatch's entity schema", fakeAsync(() => { diff --git a/src/app/features/matching-entities/matching-entities/matching-entities.stories.ts b/src/app/features/matching-entities/matching-entities/matching-entities.stories.ts index f9e7e67e07..454b79d0b6 100644 --- a/src/app/features/matching-entities/matching-entities/matching-entities.stories.ts +++ b/src/app/features/matching-entities/matching-entities/matching-entities.stories.ts @@ -1,48 +1,44 @@ import { applicationConfig, Meta, StoryFn } from "@storybook/angular"; import { MatchingEntitiesComponent } from "./matching-entities.component"; import { StorybookBaseModule } from "../../../utils/storybook-base.module"; -import { Child } from "../../../child-dev-project/children/model/child"; -import { RecurringActivity } from "../../../child-dev-project/attendance/model/recurring-activity"; import { defaultInteractionTypes } from "../../../core/config/default-config/default-interaction-types"; import { centersUnique } from "../../../child-dev-project/children/demo-data-generators/fixtures/centers"; import { genders } from "../../../child-dev-project/children/model/genders"; import { EntitySchemaField } from "../../../core/entity/schema/entity-schema-field"; import { importProvidersFrom } from "@angular/core"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; const addressSchema: EntitySchemaField = { label: "Address", dataType: "location", }; -RecurringActivity.schema.set("center", Child.schema.get("center")); -RecurringActivity.schema.set("address", addressSchema); -Child.schema.set("address", addressSchema); -Child.schema.set("otherAddress", addressSchema); const entitiesA = [ - Object.assign(RecurringActivity.create("A"), { + Object.assign(createEntityOfType("RecurringActivity", "A"), { type: defaultInteractionTypes[1], center: centersUnique[0], address: { lat: 52.4750412, lon: 13.4319106 }, }), - Object.assign(RecurringActivity.create("B"), { + Object.assign(createEntityOfType("RecurringActivity", "B"), { type: defaultInteractionTypes[2], center: centersUnique[0], address: { lat: 52.4740412, lon: 13.4319106 }, }), - Object.assign(RecurringActivity.create("ABC"), { + Object.assign(createEntityOfType("RecurringActivity", "ABC"), { type: defaultInteractionTypes[1], center: centersUnique[2], address: { lat: 52.4730412, lon: 13.4319106 }, }), ]; const entitiesB = [ - Object.assign(Child.create("sample child"), { + Object.assign(TestEntity.create("sample child"), { gender: genders[1], center: centersUnique[0], address: { lat: 52.4720412, lon: 13.4319106 }, otherAddress: { lat: 52.4720412, lon: 13.4419106 }, }), - Object.assign(Child.create("other child"), { + Object.assign(TestEntity.create("other child"), { gender: genders[2], center: centersUnique[0], address: { lat: 52.4710412, lon: 13.4319106 }, @@ -81,7 +77,7 @@ const columnsMapping = [ export const TwoSidedMatching = Template.bind({}); TwoSidedMatching.args = { leftSide: { - entityType: Child.ENTITY_TYPE, + entityType: "Child", }, rightSide: { entityType: RecurringActivity.ENTITY_TYPE, @@ -102,7 +98,7 @@ LeftMatch.args = { export const RightMatch = Template.bind({}); RightMatch.args = { leftSide: { - entityType: Child.ENTITY_TYPE, + entityType: "Child", }, entity: entitiesA[0], columns: columnsMapping, diff --git a/src/app/features/public-form/public-form.component.spec.ts b/src/app/features/public-form/public-form.component.spec.ts index d82ada44a9..088ceb54c6 100644 --- a/src/app/features/public-form/public-form.component.spec.ts +++ b/src/app/features/public-form/public-form.component.spec.ts @@ -11,25 +11,25 @@ import { MockedTestingModule } from "../../utils/mocked-testing.module"; import { PouchDatabase } from "../../core/database/pouch-database"; import { PublicFormConfig } from "./public-form-config"; import { ActivatedRoute } from "@angular/router"; -import { Child } from "../../child-dev-project/children/model/child"; import { genders } from "../../child-dev-project/children/model/genders"; import { MatSnackBar } from "@angular/material/snack-bar"; import { EntityFormService } from "../../core/common-components/entity-form/entity-form.service"; import { ConfigService } from "../../core/config/config.service"; import { EntityMapperService } from "../../core/entity/entity-mapper/entity-mapper.service"; import { InvalidFormFieldError } from "../../core/common-components/entity-form/invalid-form-field.error"; +import { TestEntity } from "../../utils/test-utils/TestEntity"; describe("PublicFormComponent", () => { - let component: PublicFormComponent; - let fixture: ComponentFixture>; + let component: PublicFormComponent; + let fixture: ComponentFixture>; let initRemoteDBSpy: jasmine.Spy; let testFormConfig: PublicFormConfig; beforeEach(waitForAsync(() => { testFormConfig = new PublicFormConfig("form-id"); testFormConfig.title = "test form"; - testFormConfig.entity = "Child"; - testFormConfig.columns = [["name"], ["gender"]]; + testFormConfig.entity = "TestEntity"; + testFormConfig.columns = [["name"], ["category"]]; TestBed.configureTestingModule({ imports: [PublicFormComponent, MockedTestingModule.withState()], providers: [ @@ -48,7 +48,7 @@ describe("PublicFormComponent", () => { beforeEach(() => { initRemoteDBSpy = spyOn(TestBed.inject(PouchDatabase), "initRemoteDB"); - fixture = TestBed.createComponent(PublicFormComponent); + fixture = TestBed.createComponent(PublicFormComponent); component = fixture.componentInstance; fixture.detectChanges(); }); @@ -64,22 +64,24 @@ describe("PublicFormComponent", () => { it("should initialize component with values from PublicFormConfig once config is ready", fakeAsync(() => { expect(component.entity).toBeUndefined(); testFormConfig.title = "Some test title"; - testFormConfig.entity = "Child"; + testFormConfig.entity = "TestEntity"; initComponent(); tick(); - expect(component.entity.getConstructor()).toBe(Child); + expect(component.entity.getConstructor()).toBe(TestEntity); expect(component.formConfig.title).toBe("Some test title"); })); it("should prefill entity with transformed values", fakeAsync(() => { - testFormConfig.prefilled = { status: "new", gender: "M" }; + testFormConfig.prefilled = { name: "new", category: "M" }; initComponent(); tick(); - expect(component.entity.status).toBe("new"); - expect(component.entity.gender).toBe(genders.find(({ id }) => id === "M")); + expect(component.entity.name).toBe("new"); + expect(component.entity.category).toBe( + genders.find(({ id }) => id === "M"), + ); })); it("should show a snackbar and reset form when the form has been submitted", fakeAsync(() => { @@ -88,14 +90,17 @@ describe("PublicFormComponent", () => { const openSnackbarSpy = spyOn(TestBed.inject(MatSnackBar), "open"); const saveSpy = spyOn(TestBed.inject(EntityFormService), "saveChanges"); saveSpy.and.resolveTo(); - component.form.get("name").setValue("some name"); + component.form.formGroup.get("name").setValue("some name"); component.submit(); - expect(saveSpy).toHaveBeenCalledWith(component.form, component.entity); + expect(saveSpy).toHaveBeenCalledWith( + component.form.formGroup, + component.entity, + ); tick(); expect(openSnackbarSpy).toHaveBeenCalled(); - expect(component.form.get("name")).toHaveValue(null); + expect(component.form.formGroup.get("name")).toHaveValue(null); })); it("should show a snackbar error and not reset when trying to submit invalid form", fakeAsync(() => { @@ -104,27 +109,31 @@ describe("PublicFormComponent", () => { const openSnackbarSpy = spyOn(TestBed.inject(MatSnackBar), "open"); const saveSpy = spyOn(TestBed.inject(EntityFormService), "saveChanges"); saveSpy.and.throwError(new InvalidFormFieldError()); - component.form.get("name").setValue("some name"); + component.form.formGroup.get("name").setValue("some name"); component.submit(); - expect(saveSpy).toHaveBeenCalledWith(component.form, component.entity); + expect(saveSpy).toHaveBeenCalledWith( + component.form.formGroup, + component.entity, + ); tick(); expect(openSnackbarSpy).toHaveBeenCalledWith( jasmine.stringContaining("invalid"), ); - expect(component.form.get("name")).toHaveValue("some name"); + expect(component.form.formGroup.get("name")).toHaveValue("some name"); })); it("should reset the form when clicking reset", fakeAsync(() => { initComponent(); tick(); - component.form.get("name").setValue("some name"); - expect(component.form.get("name")).toHaveValue("some name"); + component.form.formGroup.get("name").setValue("some name"); + expect(component.form.formGroup.get("name")).toHaveValue("some name"); component.reset(); + tick(); - expect(component.form.get("name")).toHaveValue(null); + expect(component.form.formGroup.get("name")).toHaveValue(null); })); function initComponent() { diff --git a/src/app/features/public-form/public-form.component.ts b/src/app/features/public-form/public-form.component.ts index c796658c65..4695170de0 100644 --- a/src/app/features/public-form/public-form.component.ts +++ b/src/app/features/public-form/public-form.component.ts @@ -58,7 +58,10 @@ export class PublicFormComponent implements OnInit { async submit() { try { - await this.entityFormService.saveChanges(this.form, this.entity); + await this.entityFormService.saveChanges( + this.form.formGroup, + this.entity, + ); this.snackbar.open($localize`Successfully submitted form`); } catch (e) { if (e instanceof InvalidFormFieldError) { @@ -70,11 +73,11 @@ export class PublicFormComponent implements OnInit { throw e; } - this.initForm(); + await this.initForm(); } - reset() { - this.initForm(); + async reset() { + await this.initForm(); } private async loadFormConfig() { @@ -90,15 +93,15 @@ export class PublicFormComponent implements OnInit { ); } this.fieldGroups = this.formConfig.columns.map((row) => ({ fields: row })); - this.initForm(); + await this.initForm(); } - private initForm() { + private async initForm() { this.entity = new this.entityType(); Object.entries(this.prefilled).forEach(([prop, value]) => { this.entity[prop] = value; }); - this.form = this.entityFormService.createFormGroup( + this.form = await this.entityFormService.createEntityForm( [].concat(...this.fieldGroups.map((group) => group.fields)), this.entity, ); diff --git a/src/app/features/reporting/data-aggregation.service.spec.ts b/src/app/features/reporting/data-aggregation.service.spec.ts index 85b65d3bf1..ce29f8de2d 100644 --- a/src/app/features/reporting/data-aggregation.service.spec.ts +++ b/src/app/features/reporting/data-aggregation.service.spec.ts @@ -4,16 +4,15 @@ import { Aggregation, DataAggregationService, } from "./data-aggregation.service"; -import { Child } from "../../child-dev-project/children/model/child"; import { QueryService } from "../../core/export/query.service"; import { EventNote } from "../../child-dev-project/attendance/model/event-note"; import moment from "moment"; -import { School } from "../../child-dev-project/schools/model/school"; import { ChildSchoolRelation } from "../../child-dev-project/children/model/childSchoolRelation"; -import { centersUnique } from "../../child-dev-project/children/demo-data-generators/fixtures/centers"; import { genders } from "../../child-dev-project/children/model/genders"; import { mockEntityMapper } from "../../core/entity/entity-mapper/mock-entity-mapper-service"; import { entityRegistry } from "../../core/entity/database-entity.decorator"; +import { createEntityOfType } from "../../core/demo-data/create-entity-of-type"; +import { TestEntity } from "../../utils/test-utils/TestEntity"; describe("DataAggregationService", () => { let service: DataAggregationService; @@ -31,7 +30,7 @@ describe("DataAggregationService", () => { }); it("should run the aggregation queries and return the results", async () => { - const baseQuery = `${Child.ENTITY_TYPE}:toArray`; + const baseQuery = `Child:toArray`; const christiansQuery = "[*religion=christian]"; const muslimsQuery = "[*religion=muslim]"; const childDisaggregation: Aggregation = { @@ -41,11 +40,11 @@ describe("DataAggregationService", () => { { label: "muslims", query: muslimsQuery }, ], }; - const baseData = [new School()]; + const baseData = [createEntityOfType("School")]; mockQueryService.queryData.and.returnValues( baseData, - [new School()], - [new School(), new School()], + [createEntityOfType("School")], + [createEntityOfType("School"), createEntityOfType("School")], ); const report = await service.calculateReport([childDisaggregation]); @@ -81,7 +80,7 @@ describe("DataAggregationService", () => { }); it("should create queries for nested aggregations", async () => { - const baseQuery = `${School.ENTITY_TYPE}:toArray`; + const baseQuery = `School:toArray`; const nestedBaseQuery = `[*private=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId):getActive`; const firstNestedAggregation = `[*schoolClass>3]`; const secondNestedAggregation = `[*schoolClass<=3]`; @@ -107,7 +106,10 @@ describe("DataAggregationService", () => { ], }; - const baseData = [new School(), new School()]; + const baseData = [ + createEntityOfType("School"), + createEntityOfType("School"), + ]; const nestedData = [new ChildSchoolRelation()]; mockQueryService.queryData.and.callFake((query) => { switch (query) { @@ -116,7 +118,7 @@ describe("DataAggregationService", () => { case nestedBaseQuery: return nestedData; default: - return [new School()]; + return [createEntityOfType("School")]; } }); const result = await service.calculateReport([aggregation]); @@ -161,18 +163,18 @@ describe("DataAggregationService", () => { }); it("should correctly parse groupBy results", async () => { - const maleChild = new Child(); - maleChild.gender = genders[1]; - const femaleChild = new Child(); - femaleChild.gender = genders[2]; + const maleChild = new TestEntity(); + maleChild.category = genders[1]; + const femaleChild = new TestEntity(); + femaleChild.category = genders[2]; mockQueryService.queryData.and.returnValue([ femaleChild, maleChild, maleChild, ]); const groupByAggregation: Aggregation = { - query: `${Child.ENTITY_TYPE}`, - groupBy: ["gender"], + query: TestEntity.ENTITY_TYPE, + groupBy: ["category"], label: "Total # of children", }; @@ -185,7 +187,7 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[2] }], + groupedBy: [{ property: "category", value: genders[2] }], result: 1, }, subRows: [], @@ -193,7 +195,7 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[1] }], + groupedBy: [{ property: "category", value: genders[1] }], result: 2, }, subRows: [], @@ -204,21 +206,21 @@ describe("DataAggregationService", () => { }); it("should run aggregations after a groupBy", async () => { - const maleChild = new Child(); - maleChild.gender = genders[1]; - const femaleChild = new Child(); - femaleChild.gender = genders[2]; + const maleChild = new TestEntity(); + maleChild.category = genders[1]; + const femaleChild = new TestEntity(); + femaleChild.category = genders[2]; mockQueryService.queryData.and.returnValue([ maleChild, femaleChild, maleChild, ]); const groupByAggregation: Aggregation = { - query: `${Child.ENTITY_TYPE}`, - groupBy: ["gender"], + query: TestEntity.ENTITY_TYPE, + groupBy: ["category"], label: "Total # of children", aggregations: [ - { query: `[*religion=christian]`, label: "Total # of christians" }, + { query: `[*name=christian]`, label: "Total # of christians" }, ], }; @@ -239,14 +241,14 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[1] }], + groupedBy: [{ property: "category", value: genders[1] }], result: 2, }, subRows: [ { header: { label: "Total # of christians", - groupedBy: [{ property: "gender", value: genders[1] }], + groupedBy: [{ property: "category", value: genders[1] }], result: 3, }, subRows: [], @@ -256,14 +258,14 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[2] }], + groupedBy: [{ property: "category", value: genders[2] }], result: 1, }, subRows: [ { header: { label: "Total # of christians", - groupedBy: [{ property: "gender", value: genders[2] }], + groupedBy: [{ property: "category", value: genders[2] }], result: 3, }, subRows: [], @@ -276,45 +278,50 @@ describe("DataAggregationService", () => { }); it("should support groupBy with an array of properties", async () => { - const alipore = centersUnique.find((c) => c.id === "alipore"); - const barabazar = centersUnique.find((c) => c.id === "barabazar"); - const maleChristianAlipore = new Child(); - maleChristianAlipore.gender = genders[1]; - maleChristianAlipore["religion"] = "christian"; - maleChristianAlipore.center = alipore; - const maleMuslimAlipore = new Child(); - maleMuslimAlipore.gender = genders[1]; - maleMuslimAlipore["religion"] = "muslim"; - maleMuslimAlipore.center = alipore; - const femaleChristianBarabazar = new Child(); - femaleChristianBarabazar.gender = genders[2]; - femaleChristianBarabazar["religion"] = "christian"; - femaleChristianBarabazar.center = barabazar; - const femaleChristianAlipore = new Child(); - femaleChristianAlipore.gender = genders[2]; - femaleChristianAlipore["religion"] = "christian"; - femaleChristianAlipore.center = alipore; + const n1 = "name_1"; + const n2 = "name_2"; + const male1 = new TestEntity(); + male1.category = genders[1]; + male1.name = n1; + const male2 = new TestEntity(); + male2.category = genders[1]; + male2.name = n2; + const female1 = new TestEntity(); + female1.category = genders[2]; + female1.name = n1; + const female1b = new TestEntity(); + female1b.category = genders[2]; + female1b.name = n1; mockQueryService.queryData.and.returnValue([ - femaleChristianAlipore, - maleChristianAlipore, - femaleChristianBarabazar, - maleMuslimAlipore, + female1b, + male1, + female1, + male2, ]); const groupByAggregation: Aggregation = { - query: `${Child.ENTITY_TYPE}`, - groupBy: ["gender", "religion", "center"], + query: TestEntity.ENTITY_TYPE, + groupBy: ["category", "name"], label: "Total # of children", }; const result = await service.calculateReport([groupByAggregation]); expect(result).toEqual([ { - header: { label: "Total # of children", groupedBy: [], result: 4 }, + header: { + label: "Total # of children", + groupedBy: [], + result: 4, + }, subRows: [ { header: { label: "Total # of children", - groupedBy: [{ property: "center", value: alipore }], + groupedBy: [ + { + property: "name", + value: n1, + }, + ], result: 3, }, subRows: [], @@ -322,7 +329,12 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "center", value: barabazar }], + groupedBy: [ + { + property: "name", + value: n2, + }, + ], result: 1, }, subRows: [], @@ -330,58 +342,43 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "religion", value: "christian" }], - result: 3, + groupedBy: [ + { + property: "category", + value: genders[2], + }, + ], + result: 2, }, subRows: [ { header: { label: "Total # of children", groupedBy: [ - { property: "religion", value: "christian" }, - { property: "center", value: alipore }, + { + property: "category", + value: genders[2], + }, + { + property: "name", + value: n1, + }, ], result: 2, }, subRows: [], }, - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "religion", value: "christian" }, - { property: "center", value: barabazar }, - ], - result: 1, - }, - subRows: [], - }, ], }, { header: { label: "Total # of children", - groupedBy: [{ property: "religion", value: "muslim" }], - result: 1, - }, - subRows: [ - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "religion", value: "muslim" }, - { property: "center", value: alipore }, - ], - result: 1, + groupedBy: [ + { + property: "category", + value: genders[1], }, - subRows: [], - }, - ], - }, - { - header: { - label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[2] }], + ], result: 2, }, subRows: [ @@ -389,77 +386,16 @@ describe("DataAggregationService", () => { header: { label: "Total # of children", groupedBy: [ - { property: "gender", value: genders[2] }, - { property: "center", value: alipore }, - ], - result: 1, - }, - subRows: [], - }, - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[2] }, - { property: "center", value: barabazar }, - ], - result: 1, - }, - subRows: [], - }, - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[2] }, - { property: "religion", value: "christian" }, - ], - result: 2, - }, - subRows: [ - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[2] }, - { property: "religion", value: "christian" }, - { property: "center", value: alipore }, - ], - result: 1, + { + property: "category", + value: genders[1], }, - subRows: [], - }, - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[2] }, - { property: "religion", value: "christian" }, - { property: "center", value: barabazar }, - ], - result: 1, + { + property: "name", + value: n1, }, - subRows: [], - }, - ], - }, - ], - }, - { - header: { - label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[1] }], - result: 2, - }, - subRows: [ - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[1] }, - { property: "center", value: alipore }, ], - result: 2, + result: 1, }, subRows: [], }, @@ -467,49 +403,18 @@ describe("DataAggregationService", () => { header: { label: "Total # of children", groupedBy: [ - { property: "gender", value: genders[1] }, - { property: "religion", value: "christian" }, - ], - result: 1, - }, - subRows: [ - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[1] }, - { property: "religion", value: "christian" }, - { property: "center", value: alipore }, - ], - result: 1, + { + property: "category", + value: genders[1], + }, + { + property: "name", + value: n2, }, - subRows: [], - }, - ], - }, - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[1] }, - { property: "religion", value: "muslim" }, ], result: 1, }, - subRows: [ - { - header: { - label: "Total # of children", - groupedBy: [ - { property: "gender", value: genders[1] }, - { property: "religion", value: "muslim" }, - { property: "center", value: alipore }, - ], - result: 1, - }, - subRows: [], - }, - ], + subRows: [], }, ], }, @@ -519,15 +424,15 @@ describe("DataAggregationService", () => { }); it("should allow multiple groupBy's", async () => { - const femaleMuslim = new Child(); - femaleMuslim.gender = genders[2]; - femaleMuslim["religion"] = "muslim"; - const femaleChristian = new Child(); - femaleChristian.gender = genders[2]; - femaleChristian["religion"] = "christian"; - const maleMuslim = new Child(); - maleMuslim.gender = genders[1]; - maleMuslim["religion"] = "muslim"; + const femaleMuslim = new TestEntity(); + femaleMuslim.category = genders[2]; + femaleMuslim.name = "muslim"; + const femaleChristian = new TestEntity(); + femaleChristian.category = genders[2]; + femaleChristian.name = "christian"; + const maleMuslim = new TestEntity(); + maleMuslim.category = genders[1]; + maleMuslim.name = "muslim"; mockQueryService.queryData.and.returnValue([ femaleChristian, femaleMuslim, @@ -537,13 +442,13 @@ describe("DataAggregationService", () => { ]); const nestedGroupBy: Aggregation = { - query: `${Child.ENTITY_TYPE}`, - groupBy: ["gender"], + query: `Child`, + groupBy: ["category"], label: "Total # of children", aggregations: [ { - query: `[*age > 13]`, - groupBy: ["religion"], + query: `[*isActive = true]`, + groupBy: ["name"], label: "Total # of old children", }, ], @@ -564,7 +469,7 @@ describe("DataAggregationService", () => { { header: { label: "Total # of old children", - groupedBy: [{ property: "religion", value: "christian" }], + groupedBy: [{ property: "name", value: "christian" }], result: 2, }, subRows: [], @@ -572,7 +477,7 @@ describe("DataAggregationService", () => { { header: { label: "Total # of old children", - groupedBy: [{ property: "religion", value: "muslim" }], + groupedBy: [{ property: "name", value: "muslim" }], result: 3, }, subRows: [], @@ -582,14 +487,14 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[2] }], + groupedBy: [{ property: "category", value: genders[2] }], result: 4, }, subRows: [ { header: { label: "Total # of old children", - groupedBy: [{ property: "gender", value: genders[2] }], + groupedBy: [{ property: "category", value: genders[2] }], result: 5, }, subRows: [ @@ -597,8 +502,8 @@ describe("DataAggregationService", () => { header: { label: "Total # of old children", groupedBy: [ - { property: "gender", value: genders[2] }, - { property: "religion", value: "christian" }, + { property: "category", value: genders[2] }, + { property: "name", value: "christian" }, ], result: 2, }, @@ -608,8 +513,8 @@ describe("DataAggregationService", () => { header: { label: "Total # of old children", groupedBy: [ - { property: "gender", value: genders[2] }, - { property: "religion", value: "muslim" }, + { property: "category", value: genders[2] }, + { property: "name", value: "muslim" }, ], result: 3, }, @@ -622,14 +527,14 @@ describe("DataAggregationService", () => { { header: { label: "Total # of children", - groupedBy: [{ property: "gender", value: genders[1] }], + groupedBy: [{ property: "category", value: genders[1] }], result: 1, }, subRows: [ { header: { label: "Total # of old children", - groupedBy: [{ property: "gender", value: genders[1] }], + groupedBy: [{ property: "category", value: genders[1] }], result: 5, }, subRows: [ @@ -637,8 +542,8 @@ describe("DataAggregationService", () => { header: { label: "Total # of old children", groupedBy: [ - { property: "gender", value: genders[1] }, - { property: "religion", value: "christian" }, + { property: "category", value: genders[1] }, + { property: "name", value: "christian" }, ], result: 2, }, @@ -648,8 +553,8 @@ describe("DataAggregationService", () => { header: { label: "Total # of old children", groupedBy: [ - { property: "gender", value: genders[1] }, - { property: "religion", value: "muslim" }, + { property: "category", value: genders[1] }, + { property: "name", value: "muslim" }, ], result: 3, }, @@ -665,8 +570,8 @@ describe("DataAggregationService", () => { }); it("should handle subfields of filtered query anywhere in the reporting structure", async () => { - const c1 = new Child(); - c1.status = "1"; + const c1 = new TestEntity(); + c1.name = "1"; const entityMapper = mockEntityMapper([c1]); const queryService = new QueryService( @@ -679,11 +584,11 @@ describe("DataAggregationService", () => { const complexQuery: Aggregation = { label: "!!", - query: "Child:toArray.status", + query: "TestEntity:toArray.name", }; const otherQuery: Aggregation = { label: "other", - query: "School:toArray", + query: "OtherEntity:toArray", }; const result = await service.calculateReport([complexQuery, otherQuery]); diff --git a/src/app/features/reporting/demo-report-config-generator.service.ts b/src/app/features/reporting/demo-report-config-generator.service.ts index c3be07c144..9c61807971 100644 --- a/src/app/features/reporting/demo-report-config-generator.service.ts +++ b/src/app/features/reporting/demo-report-config-generator.service.ts @@ -1,11 +1,8 @@ import { Injectable } from "@angular/core"; import { DemoDataGenerator } from "../../core/demo-data/demo-data-generator"; import { ReportEntity } from "./report-config"; -import { Child } from "../../child-dev-project/children/model/child"; -import { School } from "../../child-dev-project/schools/model/school"; import { ChildSchoolRelation } from "../../child-dev-project/children/model/childSchoolRelation"; import { EventNote } from "../../child-dev-project/attendance/model/event-note"; -import { EducationalMaterial } from "../../child-dev-project/children/educational-material/model/educational-material"; @Injectable() export class DemoReportConfigGeneratorService extends DemoDataGenerator { @@ -30,12 +27,12 @@ const demoReports: Partial[] = [ title: $localize`:Name of a report:Basic Report`, aggregationDefinitions: [ { - query: `${Child.ENTITY_TYPE}:toArray[*isActive=true]`, + query: `Child:toArray[*isActive=true]`, label: $localize`:Label of report query:All children`, groupBy: ["gender"], }, { - query: `${School.ENTITY_TYPE}:toArray`, + query: `School:toArray`, label: $localize`:Label for report query:All schools`, aggregations: [ { @@ -47,7 +44,7 @@ const demoReports: Partial[] = [ query: `[*privateSchool!=true]`, }, { - query: `[*privateSchool!=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId::unique:toEntities(${Child.ENTITY_TYPE})`, + query: `[*privateSchool!=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId::unique:toEntities(Child)`, label: $localize`:Label for report query:Children attending a governmental school`, groupBy: ["gender"], }, @@ -56,7 +53,7 @@ const demoReports: Partial[] = [ query: `[*privateSchool=true]`, }, { - query: `[*privateSchool=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId::unique:toEntities(${Child.ENTITY_TYPE})`, + query: `[*privateSchool=true]:getRelated(${ChildSchoolRelation.ENTITY_TYPE}, schoolId)[*isActive=true].childId::unique:toEntities(Child)`, label: $localize`:Label for report query:Children attending a private school`, groupBy: ["gender"], }, @@ -73,7 +70,7 @@ const demoReports: Partial[] = [ label: $localize`:Label for a report query:Events`, aggregations: [ { - query: `:getParticipantsWithAttendance(PRESENT):unique:toEntities(${Child.ENTITY_TYPE})`, + query: `:getParticipantsWithAttendance(PRESENT):unique:toEntities(Child)`, groupBy: ["gender"], label: $localize`:Label for a report query:Participants`, }, @@ -137,7 +134,7 @@ const demoReports: Partial[] = [ mode: "exporting", aggregationDefinitions: [ { - query: `${EducationalMaterial.ENTITY_TYPE}:toArray[*date >= ? & date <= ?]`, + query: `EducationalMaterial:toArray[*date >= ? & date <= ?]`, groupBy: { label: "Type", property: "materialType" }, subQueries: [ { diff --git a/src/app/features/todos/model/todo-default-configs.ts b/src/app/features/todos/model/todo-default-configs.ts new file mode 100644 index 0000000000..6050e5595b --- /dev/null +++ b/src/app/features/todos/model/todo-default-configs.ts @@ -0,0 +1,98 @@ +import { TimeInterval } from "../recurring-interval/time-interval"; + +export const todoDefaultConfigs = { + "entity:Todo": { + label: $localize`:label for entity:Task`, + labelPlural: $localize`:label (plural) for entity:Tasks`, + toStringAttributes: ["subject"], + hasPII: true, + + attributes: { + subject: { + dataType: "string", + label: $localize`:Label:Subject`, + showInDetailsView: true, + }, + description: { + dataType: "long-text", + showInDetailsView: true, + label: $localize`:Label:Description`, + }, + deadline: { + dataType: "date-only", + showInDetailsView: true, + anonymize: "retain", + label: $localize`:Label:Deadline`, + }, + startDate: { + dataType: "date-only", + showInDetailsView: true, + anonymize: "retain", + label: $localize`:Label:Start date`, + description: $localize`:Description:When you are planning to start work so that you keep enough time before the actual hard deadline.`, + }, + assignedTo: { + label: $localize`:Label:Assigned to`, + dataType: "entity", + isArray: true, + additional: "User", + showInDetailsView: true, + defaultValue: { + mode: "dynamic", + value: "$current_user", + }, + anonymize: "retain", + }, + relatedEntities: { + dataType: "entity", + isArray: true, + label: $localize`:label for the related Entities:Related Records`, + additional: ["Child", "School", "RecurringActivity"], + entityReferenceRole: "composite", + showInDetailsView: true, + anonymize: "retain", + }, + repetitionInterval: { + label: $localize`:label for Todo entity property:repeats`, + additional: [ + { + label: $localize`:repetition interval option:every week`, + interval: { amount: 1, unit: "week" }, + }, + { + label: $localize`:repetition interval option:every month`, + interval: { amount: 1, unit: "month" }, + }, + ] as { label: string; interval: TimeInterval }[], + showInDetailsView: true, + anonymize: "retain", + }, + completed: { + label: $localize`:label for Todo entity property:completed`, + viewComponent: "DisplayTodoCompletion", + anonymize: "retain", + }, + }, + }, + "view:todo": { + component: "TodoList", + config: { + entityType: "Todo", + columns: [ + "deadline", + "subject", + "assignedTo", + "startDate", + "relatedEntities", + ], + filters: [ + { id: "assignedTo" }, + + { + id: "due-status", + type: "prebuilt", + }, + ], + }, + }, +}; diff --git a/src/app/features/todos/model/todo.spec.ts b/src/app/features/todos/model/todo.spec.ts index 4bfdf27647..2ad2d48588 100644 --- a/src/app/features/todos/model/todo.spec.ts +++ b/src/app/features/todos/model/todo.spec.ts @@ -1,18 +1,7 @@ import { Todo } from "./todo"; -import { testEntitySubclass } from "../../../core/entity/model/entity.spec"; import moment from "moment"; describe("Todo", () => { - testEntitySubclass("Todo", Todo, { - _id: "Todo:some-id", - - subject: "new task", - deadline: "2022-12-01", - description: "details of the task", - assignedTo: ["demo"], - relatedEntities: [], - }); - it("should infer isOverdue", () => { expect(Todo.create({}).isOverdue).withContext("empty").toBe(false); diff --git a/src/app/features/todos/model/todo.ts b/src/app/features/todos/model/todo.ts index 29cab000de..e19f6e9f26 100644 --- a/src/app/features/todos/model/todo.ts +++ b/src/app/features/todos/model/todo.ts @@ -1,84 +1,40 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ - import { DatabaseEntity } from "../../../core/entity/database-entity.decorator"; import { Entity } from "../../../core/entity/model/entity"; -import { DatabaseField } from "../../../core/entity/database-field.decorator"; -import { User } from "../../../core/user/user"; -import { Child } from "../../../child-dev-project/children/model/child"; -import { School } from "../../../child-dev-project/schools/model/school"; -import { RecurringActivity } from "../../../child-dev-project/attendance/model/recurring-activity"; import { TimeInterval } from "../recurring-interval/time-interval"; import { TodoCompletion } from "./todo-completion"; import { WarningLevel } from "../../../child-dev-project/warning-level"; -import { PLACEHOLDERS } from "../../../core/entity/schema/entity-schema-field"; +import { DatabaseField } from "../../../core/entity/database-field.decorator"; +/** + * Base Entity Type for the Todo Feature. + * + * All fields are defined in config (in the database) and customized there, + * this class only serves as an interface for required fields upon which core functionalities are built. + */ @DatabaseEntity("Todo") export class Todo extends Entity { - static label = $localize`:label for entity:Task`; - static labelPlural = $localize`:label (plural) for entity:Tasks`; - static toStringAttributes = ["subject"]; - static override hasPII = true; - static create(properties: Partial): Todo { const instance = new Todo(); Object.assign(instance, properties); return instance; } - @DatabaseField({ label: $localize`:Label:Subject`, showInDetailsView: true }) + @DatabaseField() subject: string = ""; - @DatabaseField({ - dataType: "date-only", - label: $localize`:Label:Deadline`, - showInDetailsView: true, - anonymize: "retain", - }) - deadline: Date; + @DatabaseField() + description: string = ""; - @DatabaseField({ - dataType: "date-only", - label: $localize`:Label:Start date`, - description: $localize`:Description:When you are planning to start work so that you keep enough time before the actual hard deadline.`, - showInDetailsView: true, - anonymize: "retain", - }) - startDate: Date; + @DatabaseField({ dataType: "date-only" }) + deadline: Date; - @DatabaseField({ - label: $localize`:Label:Description`, - editComponent: "EditLongText", - showInDetailsView: true, - }) - description: string = ""; + /** + * Optional field to specify a point in time from when the task can be started. + */ + @DatabaseField({ dataType: "date-only" }) + startDate?: Date; - @DatabaseField({ - label: $localize`:Label:Assigned to`, - dataType: "entity", - isArray: true, - additional: User.ENTITY_TYPE, - showInDetailsView: true, - defaultValue: { - mode: "dynamic", - value: PLACEHOLDERS.CURRENT_USER, - }, - anonymize: "retain", - }) + @DatabaseField({ dataType: "entity", isArray: true }) assignedTo: string[] = []; /** @@ -86,48 +42,18 @@ export class Todo extends Entity { * * This property saves ids including their entity type prefix. */ - @DatabaseField({ - dataType: "entity", - isArray: true, - label: $localize`:label for the related Entities:Related Records`, - additional: [ - Child.ENTITY_TYPE, - School.ENTITY_TYPE, - RecurringActivity.ENTITY_TYPE, - ], - entityReferenceRole: "composite", - showInDetailsView: true, - anonymize: "retain", - }) + @DatabaseField({ dataType: "entity", isArray: true }) relatedEntities: string[] = []; - @DatabaseField({ - label: $localize`:label for Todo entity property:repeats`, - additional: [ - { - label: $localize`:repetition interval option:every week`, - interval: { amount: 1, unit: "week" }, - }, - { - label: $localize`:repetition interval option:every month`, - interval: { amount: 1, unit: "month" }, - }, - ] as { label: string; interval: TimeInterval }[], - showInDetailsView: true, - anonymize: "retain", - }) + @DatabaseField() repetitionInterval: TimeInterval; - @DatabaseField({ - label: $localize`:label for Todo entity property:completed`, - viewComponent: "DisplayTodoCompletion", - anonymize: "retain", - }) + @DatabaseField() completed?: TodoCompletion; - get isActive(): boolean { + override get isActive(): boolean { if (this.inactive) { - // manual archiving of records takes precendence + // manual archiving of records takes precedence return false; } @@ -142,7 +68,7 @@ export class Todo extends Entity { ); } - getWarningLevel(): WarningLevel { + override getWarningLevel(): WarningLevel { if (this.isOverdue) { return WarningLevel.URGENT; } diff --git a/src/app/features/todos/recurring-interval/edit-recurring-interval/edit-recurring-interval.component.ts b/src/app/features/todos/recurring-interval/edit-recurring-interval/edit-recurring-interval.component.ts index 5f59ceab74..aed49f29f2 100644 --- a/src/app/features/todos/recurring-interval/edit-recurring-interval/edit-recurring-interval.component.ts +++ b/src/app/features/todos/recurring-interval/edit-recurring-interval/edit-recurring-interval.component.ts @@ -55,7 +55,7 @@ export class EditRecurringIntervalComponent super(); } - ngOnInit(): void { + override ngOnInit(): void { super.ngOnInit(); this.predefinedIntervals = this.additional ?? this.predefinedIntervals; diff --git a/src/app/features/todos/recurring-interval/time-interval.datatype.ts b/src/app/features/todos/recurring-interval/time-interval.datatype.ts index 1ef9a8ca2c..b50b984787 100644 --- a/src/app/features/todos/recurring-interval/time-interval.datatype.ts +++ b/src/app/features/todos/recurring-interval/time-interval.datatype.ts @@ -7,6 +7,6 @@ export class TimeIntervalDatatype extends DefaultDatatype { static override dataType = "time-interval"; static override label: string = $localize`:datatype-label:time interval`; - viewComponent = "DisplayRecurringInterval"; - editComponent = "EditRecurringInterval"; + override viewComponent = "DisplayRecurringInterval"; + override editComponent = "EditRecurringInterval"; } diff --git a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts index bbaf20920a..fd03fb044f 100644 --- a/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts +++ b/src/app/features/todos/todo-completion/display-todo-completion/display-todo-completion.component.spec.ts @@ -11,7 +11,7 @@ import { MockEntityMapperService, } from "../../../../core/entity/entity-mapper/mock-entity-mapper-service"; import { EntityMapperService } from "../../../../core/entity/entity-mapper/entity-mapper.service"; -import { Child } from "../../../../child-dev-project/children/model/child"; +import { TestEntity } from "../../../../utils/test-utils/TestEntity"; describe("DisplayTodoCompletionComponent", () => { let component: DisplayTodoCompletionComponent; @@ -35,8 +35,8 @@ describe("DisplayTodoCompletionComponent", () => { }); it("should load the entity in completedBy when it has full ID", fakeAsync(() => { - const completingChild = new Child("1"); - const otherChild = new Child("2"); + const completingChild = new TestEntity("1"); + const otherChild = new TestEntity("2"); entityMapper.addAll([completingChild, otherChild]); component.value = { diff --git a/src/app/features/todos/todo-details/todo-details.component.html b/src/app/features/todos/todo-details/todo-details.component.html index 19ba07bd2f..43c3324570 100644 --- a/src/app/features/todos/todo-details/todo-details.component.html +++ b/src/app/features/todos/todo-details/todo-details.component.html @@ -24,5 +24,5 @@

{{ entity.subject }}

*ngIf="!entity.isNew" style="margin-right: 8px" > - + diff --git a/src/app/features/todos/todo-details/todo-details.component.spec.ts b/src/app/features/todos/todo-details/todo-details.component.spec.ts index 9d54d9974e..4141c46bf5 100644 --- a/src/app/features/todos/todo-details/todo-details.component.spec.ts +++ b/src/app/features/todos/todo-details/todo-details.component.spec.ts @@ -56,10 +56,10 @@ describe("TodoDetailsComponent", () => { const editedEntityProp = "subject"; component.formColumns = [{ fields: [editedEntityProp] }]; - component.ngOnInit(); + await component.ngOnInit(); - component.form.get(editedEntityProp).setValue("123"); - component.form.get(editedEntityProp).markAsDirty(); + component.form.formGroup.get(editedEntityProp).setValue("123"); + component.form.formGroup.get(editedEntityProp).markAsDirty(); await component.completeTodo(); const savedEntity = await TestBed.inject( diff --git a/src/app/features/todos/todo-details/todo-details.component.ts b/src/app/features/todos/todo-details/todo-details.component.ts index 3886b6bde5..ab6e53e613 100644 --- a/src/app/features/todos/todo-details/todo-details.component.ts +++ b/src/app/features/todos/todo-details/todo-details.component.ts @@ -57,17 +57,20 @@ export class TodoDetailsComponent implements OnInit { this.formColumns = [{ fields: data.columns }]; } - ngOnInit(): void { - this.form = this.entityFormService.createFormGroup( + async ngOnInit(): Promise { + this.form = await this.entityFormService.createEntityForm( [].concat(...this.formColumns.map((group) => group.fields)), this.entity, ); } async completeTodo() { - if (this.form.dirty) { + if (this.form.formGroup.dirty) { // we assume the user always wants to save pending changes rather than discard them - await this.entityFormService.saveChanges(this.form, this.entity); + await this.entityFormService.saveChanges( + this.form.formGroup, + this.entity, + ); } await this.todoService.completeTodo(this.entity); this.dialogRef.close(); diff --git a/src/app/features/todos/todo-list/todo-list.component.ts b/src/app/features/todos/todo-list/todo-list.component.ts index 3c9fdd76af..03528e59bb 100644 --- a/src/app/features/todos/todo-list/todo-list.component.ts +++ b/src/app/features/todos/todo-list/todo-list.component.ts @@ -88,9 +88,9 @@ export class TodoListComponent extends EntityListComponent implements OnInit { - // TODO: make this component obsolete by generalizing Entity and EntityList so that we can define a viewDetailsComponent on the entity that gets opened as popup? + // TODO: make this component obsolete by generalizing Entity and EntityList so that we can define a viewDetailsComponent on the entity that gets opened as popup? #2511 - entityConstructor = Todo; + override entityConstructor = Todo; override clickMode: "navigate" | "popup" | "none" = "none"; @@ -122,6 +122,7 @@ export class TodoListComponent dialog, duplicateRecord, entityActionsService, + null, ); } diff --git a/src/app/features/todos/todos-dashboard/todos-dashboard.component.ts b/src/app/features/todos/todos-dashboard/todos-dashboard.component.ts index 6ae14739b1..6c75eefdd9 100644 --- a/src/app/features/todos/todos-dashboard/todos-dashboard.component.ts +++ b/src/app/features/todos/todos-dashboard/todos-dashboard.component.ts @@ -26,7 +26,7 @@ import { DashboardWidget } from "../../../core/dashboard/dashboard-widget/dashbo ], }) export class TodosDashboardComponent extends DashboardWidget { - static getRequiredEntities() { + static override getRequiredEntities() { return Todo.ENTITY_TYPE; } diff --git a/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.spec.ts b/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.spec.ts index fcb2f2da5f..abafd3cfc4 100644 --- a/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.spec.ts +++ b/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.spec.ts @@ -1,15 +1,13 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { TodosRelatedToEntityComponent } from "./todos-related-to-entity.component"; -import { Entity } from "../../../core/entity/model/entity"; import { DatabaseTestingModule } from "../../../utils/database-testing.module"; -import { Child } from "../../../child-dev-project/children/model/child"; import { Todo } from "../model/todo"; import { EntityMapperService } from "../../../core/entity/entity-mapper/entity-mapper.service"; -import { School } from "../../../child-dev-project/schools/model/school"; -import { User } from "../../../core/user/user"; import { Database } from "../../../core/database/database"; import { DatabaseIndexingService } from "../../../core/entity/database-indexing/database-indexing.service"; +import { TestEntity } from "../../../utils/test-utils/TestEntity"; +import { createEntityOfType } from "../../../core/demo-data/create-entity-of-type"; describe("TodosRelatedToEntityComponent", () => { let component: TodosRelatedToEntityComponent; @@ -25,7 +23,7 @@ describe("TodosRelatedToEntityComponent", () => { fixture = TestBed.createComponent(TodosRelatedToEntityComponent); component = fixture.componentInstance; - component.entity = new Entity(); + component.entity = new TestEntity(); fixture.detectChanges(); })); @@ -37,11 +35,11 @@ describe("TodosRelatedToEntityComponent", () => { }); it("should load data from index when having a single relation", async () => { - const child = new Child(); + const child = createEntityOfType("Child"); const relatedTodo = new Todo(); - relatedTodo.relatedEntities = [child.getId(), new School().getId()]; + relatedTodo.relatedEntities = [child.getId(), new TestEntity().getId()]; const unrelatedTodo = new Todo(); - unrelatedTodo.relatedEntities = [new Child().getId()]; + unrelatedTodo.relatedEntities = [new TestEntity().getId()]; await TestBed.inject(EntityMapperService).saveAll([ relatedTodo, unrelatedTodo, @@ -64,17 +62,21 @@ describe("TodosRelatedToEntityComponent", () => { }); it("should load data with entity mapper when having multiple relations", async () => { - const relatedSchema = Todo.schema.get("relatedEntities"); - const originalAdditional = relatedSchema.additional; - relatedSchema.additional = [User.ENTITY_TYPE]; - const user = new User(); + const relatedEntitiesSchema = Todo.schema.get("relatedEntities"); + const originalRelatedEntitiesAdditional = relatedEntitiesSchema.additional; + relatedEntitiesSchema.additional = [TestEntity.ENTITY_TYPE]; + const assignedToSchema = Todo.schema.get("assignedTo"); + const originalAssignedToAdditional = assignedToSchema.additional; + assignedToSchema.additional = [TestEntity.ENTITY_TYPE]; + + const user = new TestEntity(); const relatedTodo = new Todo(); relatedTodo.relatedEntities = [user.getId()]; const relatedTodo2 = new Todo(); relatedTodo2.assignedTo = [user.getId()]; - relatedTodo2.relatedEntities = [new User().getId()]; + relatedTodo2.relatedEntities = [new TestEntity().getId()]; const unrelatedTodo = new Todo(); - unrelatedTodo.relatedEntities = [new User().getId()]; + unrelatedTodo.relatedEntities = [new TestEntity().getId()]; const entityMapper = TestBed.inject(EntityMapperService); await entityMapper.saveAll([relatedTodo, relatedTodo2, unrelatedTodo]); const loadTypeSpy = spyOn(entityMapper, "loadType").and.callThrough(); @@ -103,6 +105,7 @@ describe("TodosRelatedToEntityComponent", () => { ], }); - relatedSchema.additional = originalAdditional; + relatedEntitiesSchema.additional = originalRelatedEntitiesAdditional; + assignedToSchema.additional = originalAssignedToAdditional; }); }); diff --git a/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts b/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts index ab630b27f6..4a82e25912 100644 --- a/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts +++ b/src/app/features/todos/todos-related-to-entity/todos-related-to-entity.component.ts @@ -54,7 +54,7 @@ export class TodosRelatedToEntityComponent extends RelatedEntitiesComponent Promise.resolve([]), }, }, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting(), ], }) export class MockedTestingModule { static withState( loginState = LoginState.LOGGED_IN, - data: Entity[] = [new User(TEST_USER)], + data: Entity[] = [createEntityOfType("User", TEST_USER)], ): ModuleWithProviders { environment.session_type = SessionType.mock; const mockedEntityMapper = mockEntityMapper([...data]); + let mockLoggingService: jasmine.SpyObj; + mockLoggingService = jasmine.createSpyObj(["warn"]); return { ngModule: MockedTestingModule, @@ -84,6 +93,8 @@ export class MockedTestingModule { useFactory: entityAbilityFactory, deps: [EntitySchemaService], }, + + { provide: LoggingService, useValue: mockLoggingService }, { provide: EntityMapperService, useValue: mockedEntityMapper }, { provide: ConfigService, useValue: createTestingConfigService() }, { @@ -94,12 +105,13 @@ export class MockedTestingModule { provide: SessionSubject, useValue: new BehaviorSubject({ name: TEST_USER, + id: TEST_USER, roles: ["user_app"], }), }, { provide: CurrentUserSubject, - useValue: new BehaviorSubject(new User(TEST_USER)), + useValue: new BehaviorSubject(new TestEntity(TEST_USER)), }, ], }; diff --git a/src/app/utils/storybook-base.module.ts b/src/app/utils/storybook-base.module.ts index 86d4ccda14..4a6c204529 100644 --- a/src/app/utils/storybook-base.module.ts +++ b/src/app/utils/storybook-base.module.ts @@ -14,7 +14,6 @@ import { AppModule } from "../app.module"; import { ConfigurableEnumService } from "../core/basic-datatypes/configurable-enum/configurable-enum.service"; import { createTestingConfigurableEnumService } from "../core/basic-datatypes/configurable-enum/configurable-enum-testing"; import { Entity } from "../core/entity/model/entity"; -import { User } from "../core/user/user"; import { mockEntityMapper, MockEntityMapperService, @@ -71,7 +70,7 @@ export const entityFormStorybookDefaultParameters = { }) export class StorybookBaseModule { private static initData: Entity[] = []; - static withData(data: Entity[] = [new User(TEST_USER)]) { + static withData(data: Entity[] = [new Entity(TEST_USER)]) { StorybookBaseModule.initData = data; return StorybookBaseModule; } diff --git a/src/app/utils/test-utils/TestEntity.ts b/src/app/utils/test-utils/TestEntity.ts new file mode 100644 index 0000000000..d8bbcbc635 --- /dev/null +++ b/src/app/utils/test-utils/TestEntity.ts @@ -0,0 +1,60 @@ +import { DatabaseEntity } from "../../core/entity/database-entity.decorator"; +import { Entity } from "../../core/entity/model/entity"; +import { DatabaseField } from "../../core/entity/database-field.decorator"; +import { EntityDatatype } from "../../core/basic-datatypes/entity/entity.datatype"; +import { ConfigurableEnumValue } from "../../core/basic-datatypes/configurable-enum/configurable-enum.interface"; +import { ConfigurableEnumDatatype } from "../../core/basic-datatypes/configurable-enum/configurable-enum-datatype/configurable-enum.datatype"; +import { DateWithAge } from "../../core/basic-datatypes/date-with-age/dateWithAge"; +import { IconName } from "@fortawesome/fontawesome-svg-core"; + +/** + * Basic Entity type for unit tests, so that we don't have to create custom entity classes for every test. + */ +@DatabaseEntity("TestEntity") +export class TestEntity extends Entity { + static override ENTITY_TYPE = "TestEntity"; + + static override toStringAttributes = ["name"]; + static override label = "Test Entity"; + static override labelPlural = "Test Entities"; + static override icon: IconName = "child"; + static override route = "test-entity"; + static override blockComponent = "ChildBlock"; + static override hasPII = true; + + @DatabaseField({ + label: "Name", + }) + name: string; + + @DatabaseField({ + label: "Other", + }) + other: string; + + @DatabaseField({ + label: "Reference", + dataType: EntityDatatype.dataType, + additional: TestEntity.ENTITY_TYPE, + }) + ref: string; + + @DatabaseField({ + label: "Category", + dataType: ConfigurableEnumDatatype.dataType, + additional: "genders", + }) + category: ConfigurableEnumValue; + + @DatabaseField({ + label: "Date of Birth", + }) + dateOfBirth: DateWithAge; + + static create(data: Partial | string): TestEntity { + if (typeof data === "string") { + data = { name: data }; + } + return Object.assign(new TestEntity(), data); + } +} diff --git a/src/bootstrap-i18n.ts b/src/bootstrap-i18n.ts index f079b43e2d..8eefcc2b5e 100644 --- a/src/bootstrap-i18n.ts +++ b/src/bootstrap-i18n.ts @@ -4,7 +4,7 @@ import { } from "./app/core/language/language-statics"; import { loadTranslations } from "@angular/localize"; import { registerLocaleData } from "@angular/common"; -import * as parseXliffToJson from "./app/utils/parse-xliff-to-js"; +import * as parseXliffToJson from "./app/utils/parse-xliff-to-js.js"; /** * Load translation files and apply them to angular localize system. diff --git a/src/main.ts b/src/main.ts index e1c2702773..ae152fbf58 100644 --- a/src/main.ts +++ b/src/main.ts @@ -35,7 +35,9 @@ Logging.initRemoteLogging({ // Listening to event as soon as possible PwaInstallService.registerPWAInstallListener(); -bootstrap(); // top-level await not possible here yet, therefore wrapped in `bootstrap()` function +bootstrap().catch((reason) => { + Logging.error("Application Bootstrap failed", reason); +}); // top-level await not possible here yet, therefore wrapped in `bootstrap()` function async function bootstrap() { await initLanguage(); diff --git a/src/polyfills.ts b/src/polyfills.ts index f325bf7382..387d0feb60 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -1,19 +1,3 @@ -/* - * This file is part of ndb-core. - * - * ndb-core is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * ndb-core is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with ndb-core. If not, see . - */ /** * This file includes polyfills needed by Angular and is loaded before the app. * You can add your own extra polyfills to this file. @@ -33,7 +17,6 @@ * Zone JS is required by Angular itself. */ import "zone.js"; // Included with Angular CLI. - /*************************************************************************************************** * APPLICATION IMPORTS */ @@ -47,16 +30,13 @@ import "@angular/localize/init"; * Need to import at least one locale-data with intl. */ // import 'intl/locale-data/jsonp/en'; - // Import hammer.js to enable gestures // on mobile devices import "hammerjs"; +import * as buffer from "buffer"; +import * as process from "process"; // WARNING: workaround to allow PouchDB with Angular v6: https://github.com/pouchdb/pouchdb/issues/7263 (window as any).global = window; - -import * as buffer from "buffer"; (window as any).Buffer = buffer.Buffer; - -import * as process from "process"; (window as any).process = process; diff --git a/src/styles/mdc_overwrites/mdc_overwrites.scss b/src/styles/mdc_overwrites/mdc_overwrites.scss index 77ee2fe0a0..a0271ccf1d 100644 --- a/src/styles/mdc_overwrites/mdc_overwrites.scss +++ b/src/styles/mdc_overwrites/mdc_overwrites.scss @@ -9,7 +9,7 @@ /* Text color for fab buttons otherwise defaults to black */ .mat-mdc-fab.mat-accent, .mat-mdc-mini-fab.mat-accent { - --mat-fab-foreground-color: #{mat.get-contrast-color-from-palette( + --mat-fab-foreground-color: #{mat.m2-get-contrast-color-from-palette( theme.$accent, A200 )} !important; @@ -17,7 +17,7 @@ /* Text color for buttons with accent color otherwise defaults to black */ .mat-mdc-raised-button:not(:disabled)[color="accent"] { - --mdc-protected-button-label-text-color: #{mat.get-contrast-color-from-palette( + --mdc-protected-button-label-text-color: #{mat.m2-get-contrast-color-from-palette( theme.$accent, A200 )} !important @@ -34,7 +34,7 @@ .mdc-checkbox__native-control:enabled ~ .mdc-checkbox__background .mdc-checkbox__checkmark { - --mdc-checkbox-selected-checkmark-color: #{mat.get-contrast-color-from-palette( + --mdc-checkbox-selected-checkmark-color: #{mat.m2-get-contrast-color-from-palette( theme.$accent, A200 )} !important @@ -45,7 +45,7 @@ .mat-calendar-body-in-range::before { --mat-datepicker-calendar-date-in-range-state-background-color: #{color-mix( in srgb, - mat.get-color-from-palette(theme.$primary, 500), + mat.m2-get-color-from-palette(theme.$primary, 500), transparent 80% )} !important; } diff --git a/src/styles/variables/_colors.scss b/src/styles/variables/_colors.scss index b09e88f2cb..89275641f6 100644 --- a/src/styles/variables/_colors.scss +++ b/src/styles/variables/_colors.scss @@ -1,28 +1,28 @@ @use "@angular/material" as mat; @use "variables/ndb-light-theme" as theme; -$err-palette: mat.define-palette(mat.$red-palette); -$warn-palette: mat.define-palette(mat.$orange-palette); -$success-palette: mat.define-palette(mat.$green-palette); +$err-palette: mat.m2-define-palette(mat.$m2-red-palette); +$warn-palette: mat.m2-define-palette(mat.$m2-orange-palette); +$success-palette: mat.m2-define-palette(mat.$m2-green-palette); -$success: mat.get-color-from-palette($success-palette); -$warn: mat.get-color-from-palette($warn-palette); -$error: mat.get-color-from-palette($err-palette); +$success: mat.m2-get-color-from-palette($success-palette); +$warn: mat.m2-get-color-from-palette($warn-palette); +$error: mat.m2-get-color-from-palette($err-palette); -$primary: mat.get-color-from-palette(theme.$primary); -$accent: mat.get-color-from-palette(theme.$accent); +$primary: mat.m2-get-color-from-palette(theme.$primary); +$accent: mat.m2-get-color-from-palette(theme.$accent); -$grey-transparent: mat.get-color-from-palette( - mat.$grey-palette, +$grey-transparent: mat.m2-get-color-from-palette( + mat.$m2-grey-palette, $hue: 400, $opacity: 0.1 ); -$grey-light: mat.get-color-from-palette(mat.$grey-palette, 50); -$grey-medium: mat.get-color-from-palette(mat.$grey-palette, 100); -$grey-darker: mat.get-color-from-palette(mat.$grey-palette, 300); +$grey-light: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 50); +$grey-medium: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 100); +$grey-darker: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 300); $muted: rgba(0, 0, 0, 0.54); -$muted-background: mat.get-color-from-palette(mat.$grey-palette); +$muted-background: mat.m2-get-color-from-palette(mat.$m2-grey-palette); $disabled: rgba(0, 0, 0, 0.38); $disabled-transparent: $grey-transparent; $border-color: $grey-darker; @@ -33,18 +33,18 @@ $inactive: grey; /* background for the main view */ $background: white; /* background for the sidebar */ -$background-secondary: mat.get-color-from-palette(theme.$primary, 50); +$background-secondary: mat.m2-get-color-from-palette(theme.$primary, 50); /* light grey color background to highlight special ui elements */ -$background-neutral: mat.get-color-from-palette( - mat.$light-theme-background-palette, +$background-neutral: mat.m2-get-color-from-palette( + mat.$m2-light-theme-background-palette, card ); /* especially for labels in disabled forms (which is our default view) */ -$text: mat.get-color-from-palette(mat.$light-theme-foreground-palette, text); -$text-secondary: mat.get-color-from-palette(mat.$grey-palette, 800); -$hint-text: mat.get-color-from-palette( - mat.$light-theme-foreground-palette, +$text: mat.m2-get-color-from-palette(mat.$m2-light-theme-foreground-palette, text); +$text-secondary: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 800); +$hint-text: mat.m2-get-color-from-palette( + mat.$m2-light-theme-foreground-palette, hint-text ); diff --git a/src/styles/variables/_ndb-light-theme.scss b/src/styles/variables/_ndb-light-theme.scss index 4e1064211d..52e5a98b91 100644 --- a/src/styles/variables/_ndb-light-theme.scss +++ b/src/styles/variables/_ndb-light-theme.scss @@ -4,69 +4,69 @@ @use "@angular/material" as mat; $primary-palette: ( - 50: var(--primary-50, map-get(mat.$orange-palette, 50)), - 100: var(--primary-100, map-get(mat.$orange-palette, 100)), - 200: var(--primary-200, map-get(mat.$orange-palette, 200)), - 300: var(--primary-300, map-get(mat.$orange-palette, 300)), - 400: var(--primary-400, map-get(mat.$orange-palette, 400)), - 500: var(--primary-500, map-get(mat.$orange-palette, 500)), - 600: var(--primary-600, map-get(mat.$orange-palette, 600)), - 700: var(--primary-700, map-get(mat.$orange-palette, 700)), - 800: var(--primary-800, map-get(mat.$orange-palette, 800)), - 900: var(--primary-900, map-get(mat.$orange-palette, 900)), - A100: var(--primary-A100, map-get(mat.$orange-palette, A100)), - A200: var(--primary-A200, map-get(mat.$orange-palette, A200)), - A400: var(--primary-A400, map-get(mat.$orange-palette, A400)), - A700: var(--primary-A700, map-get(mat.$orange-palette, A700)), - contrast: map-get(mat.$orange-palette, contrast), + 50: var(--primary-50, map-get(mat.$m2-orange-palette, 50)), + 100: var(--primary-100, map-get(mat.$m2-orange-palette, 100)), + 200: var(--primary-200, map-get(mat.$m2-orange-palette, 200)), + 300: var(--primary-300, map-get(mat.$m2-orange-palette, 300)), + 400: var(--primary-400, map-get(mat.$m2-orange-palette, 400)), + 500: var(--primary-500, map-get(mat.$m2-orange-palette, 500)), + 600: var(--primary-600, map-get(mat.$m2-orange-palette, 600)), + 700: var(--primary-700, map-get(mat.$m2-orange-palette, 700)), + 800: var(--primary-800, map-get(mat.$m2-orange-palette, 800)), + 900: var(--primary-900, map-get(mat.$m2-orange-palette, 900)), + A100: var(--primary-A100, map-get(mat.$m2-orange-palette, A100)), + A200: var(--primary-A200, map-get(mat.$m2-orange-palette, A200)), + A400: var(--primary-A400, map-get(mat.$m2-orange-palette, A400)), + A700: var(--primary-A700, map-get(mat.$m2-orange-palette, A700)), + contrast: map-get(mat.$m2-orange-palette, contrast), ); $secondary-palette: ( - 50: var(--secondary-50, map-get(mat.$blue-palette, 50)), - 100: var(--secondary-100, map-get(mat.$blue-palette, 100)), - 200: var(--secondary-200, map-get(mat.$blue-palette, 200)), - 300: var(--secondary-300, map-get(mat.$blue-palette, 300)), - 400: var(--secondary-400, map-get(mat.$blue-palette, 400)), - 500: var(--secondary-500, map-get(mat.$blue-palette, 500)), - 600: var(--secondary-600, map-get(mat.$blue-palette, 600)), - 700: var(--secondary-700, map-get(mat.$blue-palette, 700)), - 800: var(--secondary-800, map-get(mat.$blue-palette, 800)), - 900: var(--secondary-900, map-get(mat.$blue-palette, 900)), - A100: var(--secondary-A100, map-get(mat.$blue-palette, A100)), - A200: var(--secondary-A200, map-get(mat.$blue-palette, A200)), - A400: var(--secondary-A400, map-get(mat.$blue-palette, A400)), - A700: var(--secondary-A700, map-get(mat.$blue-palette, A700)), - contrast: map-get(mat.$blue-palette, contrast), + 50: var(--secondary-50, map-get(mat.$m2-blue-palette, 50)), + 100: var(--secondary-100, map-get(mat.$m2-blue-palette, 100)), + 200: var(--secondary-200, map-get(mat.$m2-blue-palette, 200)), + 300: var(--secondary-300, map-get(mat.$m2-blue-palette, 300)), + 400: var(--secondary-400, map-get(mat.$m2-blue-palette, 400)), + 500: var(--secondary-500, map-get(mat.$m2-blue-palette, 500)), + 600: var(--secondary-600, map-get(mat.$m2-blue-palette, 600)), + 700: var(--secondary-700, map-get(mat.$m2-blue-palette, 700)), + 800: var(--secondary-800, map-get(mat.$m2-blue-palette, 800)), + 900: var(--secondary-900, map-get(mat.$m2-blue-palette, 900)), + A100: var(--secondary-A100, map-get(mat.$m2-blue-palette, A100)), + A200: var(--secondary-A200, map-get(mat.$m2-blue-palette, A200)), + A400: var(--secondary-A400, map-get(mat.$m2-blue-palette, A400)), + A700: var(--secondary-A700, map-get(mat.$m2-blue-palette, A700)), + contrast: map-get(mat.$m2-blue-palette, contrast), ); $error-palette: ( - 50: var(--error-50, map-get(mat.$red-palette, 50)), - 100: var(--error-100, map-get(mat.$red-palette, 100)), - 200: var(--error-200, map-get(mat.$red-palette, 200)), - 300: var(--error-300, map-get(mat.$red-palette, 300)), - 400: var(--error-400, map-get(mat.$red-palette, 400)), - 500: var(--error-500, map-get(mat.$red-palette, 500)), - 600: var(--error-600, map-get(mat.$red-palette, 600)), - 700: var(--error-700, map-get(mat.$red-palette, 700)), - 800: var(--error-800, map-get(mat.$red-palette, 800)), - 900: var(--error-900, map-get(mat.$red-palette, 900)), - A200: var(--error-A200, map-get(mat.$red-palette, A200)), - A400: var(--error-A400, map-get(mat.$red-palette, A400)), - A700: var(--error-A700, map-get(mat.$red-palette, A700)), - contrast: map-get(mat.$red-palette, contrast), + 50: var(--error-50, map-get(mat.$m2-red-palette, 50)), + 100: var(--error-100, map-get(mat.$m2-red-palette, 100)), + 200: var(--error-200, map-get(mat.$m2-red-palette, 200)), + 300: var(--error-300, map-get(mat.$m2-red-palette, 300)), + 400: var(--error-400, map-get(mat.$m2-red-palette, 400)), + 500: var(--error-500, map-get(mat.$m2-red-palette, 500)), + 600: var(--error-600, map-get(mat.$m2-red-palette, 600)), + 700: var(--error-700, map-get(mat.$m2-red-palette, 700)), + 800: var(--error-800, map-get(mat.$m2-red-palette, 800)), + 900: var(--error-900, map-get(mat.$m2-red-palette, 900)), + A200: var(--error-A200, map-get(mat.$m2-red-palette, A200)), + A400: var(--error-A400, map-get(mat.$m2-red-palette, A400)), + A700: var(--error-A700, map-get(mat.$m2-red-palette, A700)), + contrast: map-get(mat.$m2-red-palette, contrast), ); /* Define the applications theme. */ -$primary: mat.define-palette($primary-palette); -$accent: mat.define-palette($secondary-palette, A200, A100, A400); -$warn: mat.define-palette($error-palette); +$primary: mat.m2-define-palette($primary-palette); +$accent: mat.m2-define-palette($secondary-palette, A200, A100, A400); +$warn: mat.m2-define-palette($error-palette); -$typography: mat.define-typography-config( +$typography: mat.m2-define-typography-config( $font-family: var(--font-family, sans-serif), ); /* Create the theme object (a Sass map containing all of the palettes). */ -$theme: mat.define-light-theme( +$theme: mat.m2-define-light-theme( ( color: ( primary: $primary, diff --git a/src/tsconfig.app.json b/src/tsconfig.app.json deleted file mode 100644 index 0f49fe11d2..0000000000 --- a/src/tsconfig.app.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/app" - }, - "files": [ - "main.ts", - "polyfills.ts" - ], - "include": [ - "src/**/*.d.ts" - ] -} diff --git a/src/tsconfig.spec.json b/src/tsconfig.spec.json deleted file mode 100644 index 811b406fe2..0000000000 --- a/src/tsconfig.spec.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/spec" - } -} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000000..809178179c --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts", + "src/polyfills.ts", + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 9b26b9b6af..10744c2068 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,44 +1,62 @@ { "compileOnSave": false, "compilerOptions": { - "module": "es2020", "outDir": "./dist/out-tsc", "baseUrl": "src", + "strict": false, + // we are working towards enabling this soon + "noImplicitOverride": true, + // TODO: enable more strict rules (these are included in latest angular default projects) + // "noPropertyAccessFromIndexSignature": true, + // "noImplicitReturns": true, + // "noFallthroughCasesInSwitch": true, + // "isolatedModules": true, + "skipLibCheck": true, + "esModuleInterop": true, "sourceMap": true, "declaration": false, - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "ES2022", "downlevelIteration": true, - "esModuleInterop": true, - "skipLibCheck": true, - "useDefineForClassFields": false, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "moduleResolution": "bundler", + "importHelpers": true, "allowSyntheticDefaultImports": true, + "useDefineForClassFields": false, + // TODO fix decorator issues caused by the changed initialization order + "target": "ES2022", + "module": "ES2022", "resolveJsonModule": true, - "types": [ - "jasmine" - ], "lib": [ - "es2018", + "ES2022", "dom" ], + "types": [ + "jasmine" + ], "paths": { "stream": [ - "../node_modules/stream-browserify" + "./node_modules/stream-browserify" + ], + "process": [ + "./node_modules/process" ], "util": [ - "../node_modules/util" + "./node_modules/util" ], "assert": [ - "../node_modules/assert" + "./node_modules/assert" ] } }, + "exclude": [ + "e2e", + "./cypress.config.ts", + "**/*.cy.tsx" + ], "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, "strictTemplates": true - }, - "include": [ - "src" - ] + } } diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000000..64cc445a64 --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + }, + "files": [ + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +}